41b49508bf0581a940975e348e54fd6d599dd397
[m6w6/ext-pq] / src / php_pqtxn.c
1 /*
2 +--------------------------------------------------------------------+
3 | PECL :: pq |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the conditions mentioned |
7 | in the accompanying LICENSE file are met. |
8 +--------------------------------------------------------------------+
9 | Copyright (c) 2013, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
16
17 #include <php.h>
18 #include <ext/standard/php_smart_str.h>
19
20 #include <libpq-events.h>
21 #include <libpq/libpq-fs.h>
22
23 #include "php_pq.h"
24 #include "php_pq_misc.h"
25 #include "php_pq_object.h"
26 #include "php_pqexc.h"
27 #include "php_pqres.h"
28 #include "php_pqlob.h"
29 #include "php_pqtxn.h"
30
31 zend_class_entry *php_pqtxn_class_entry;
32 static zend_object_handlers php_pqtxn_object_handlers;
33 static HashTable php_pqtxn_object_prophandlers;
34
35 const char *isolation_level(long *isolation)
36 {
37 switch (*isolation) {
38 case PHP_PQTXN_SERIALIZABLE:
39 return "SERIALIZABLE";
40 case PHP_PQTXN_REPEATABLE_READ:
41 return "REPEATABLE READ";
42 default:
43 *isolation = PHP_PQTXN_READ_COMMITTED;
44 /* no break */
45 case PHP_PQTXN_READ_COMMITTED:
46 return "READ COMMITTED";
47 }
48 }
49
50 static void php_pqtxn_object_free(void *o TSRMLS_DC)
51 {
52 php_pqtxn_object_t *obj = o;
53 #if DBG_GC
54 fprintf(stderr, "FREE txn(#%d) %p (conn(#%d): %p)\n", obj->zv.handle, obj, obj->intern->conn->zv.handle, obj->intern->conn);
55 #endif
56 if (obj->intern) {
57 if (obj->intern->open) {
58 PGresult *res = PQexec(obj->intern->conn->intern->conn, "ROLLBACK");
59
60 if (res) {
61 PHP_PQclear(res);
62 }
63 }
64 php_pq_object_delref(obj->intern->conn TSRMLS_CC);
65 efree(obj->intern);
66 obj->intern = NULL;
67 }
68 zend_object_std_dtor((zend_object *) o TSRMLS_CC);
69 efree(obj);
70 }
71
72 zend_object_value php_pqtxn_create_object_ex(zend_class_entry *ce, php_pqtxn_t *intern, php_pqtxn_object_t **ptr TSRMLS_DC)
73 {
74 php_pqtxn_object_t *o;
75
76 o = ecalloc(1, sizeof(*o));
77 zend_object_std_init((zend_object *) o, ce TSRMLS_CC);
78 object_properties_init((zend_object *) o, ce);
79 o->prophandler = &php_pqtxn_object_prophandlers;
80
81 if (ptr) {
82 *ptr = o;
83 }
84
85 if (intern) {
86 o->intern = intern;
87 }
88
89 o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_pqtxn_object_free, NULL TSRMLS_CC);
90 o->zv.handlers = &php_pqtxn_object_handlers;
91
92 return o->zv;
93 }
94
95 static zend_object_value php_pqtxn_create_object(zend_class_entry *class_type TSRMLS_DC)
96 {
97 return php_pqtxn_create_object_ex(class_type, NULL, NULL TSRMLS_CC);
98 }
99
100 static void php_pqtxn_object_read_connection(zval *object, void *o, zval *return_value TSRMLS_DC)
101 {
102 php_pqtxn_object_t *obj = o;
103
104 php_pq_object_to_zval(obj->intern->conn, &return_value TSRMLS_CC);
105 }
106
107 static void php_pqtxn_object_read_isolation(zval *object, void *o, zval *return_value TSRMLS_DC)
108 {
109 php_pqtxn_object_t *obj = o;
110
111 RETVAL_LONG(obj->intern->isolation);
112 }
113
114 static void php_pqtxn_object_read_readonly(zval *object, void *o, zval *return_value TSRMLS_DC)
115 {
116 php_pqtxn_object_t *obj = o;
117
118 RETVAL_BOOL(obj->intern->readonly);
119 }
120
121 static void php_pqtxn_object_read_deferrable(zval *object, void *o, zval *return_value TSRMLS_DC)
122 {
123 php_pqtxn_object_t *obj = o;
124
125 RETVAL_BOOL(obj->intern->deferrable);
126 }
127
128 static void php_pqtxn_object_write_isolation(zval *object, void *o, zval *value TSRMLS_DC)
129 {
130 php_pqtxn_object_t *obj = o;
131 php_pqtxn_isolation_t orig = obj->intern->isolation;
132 zval *zisolation = value;
133 PGresult *res;
134
135 if (Z_TYPE_P(zisolation) != IS_LONG) {
136 if (Z_REFCOUNT_P(value) > 1) {
137 zval *tmp;
138 MAKE_STD_ZVAL(tmp);
139 ZVAL_ZVAL(tmp, zisolation, 1, 0);
140 convert_to_long(tmp);
141 zisolation = tmp;
142 } else {
143 convert_to_long_ex(&zisolation);
144 }
145 }
146
147 switch ((obj->intern->isolation = Z_LVAL_P(zisolation))) {
148 case PHP_PQTXN_READ_COMMITTED:
149 res = PQexec(obj->intern->conn->intern->conn, "SET TRANSACTION ISOLATION LEVEL READ COMMITED");
150 break;
151 case PHP_PQTXN_REPEATABLE_READ:
152 res = PQexec(obj->intern->conn->intern->conn, "SET TRANSACTION ISOLATION LEVEL REPEATABLE READ");
153 break;
154 case PHP_PQTXN_SERIALIZABLE:
155 res = PQexec(obj->intern->conn->intern->conn, "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE");
156 break;
157 default:
158 obj->intern->isolation = orig;
159 res = NULL;
160 break;
161 }
162
163 if (zisolation != value) {
164 zval_ptr_dtor(&zisolation);
165 }
166
167 if (res) {
168 php_pqres_success(res TSRMLS_CC);
169 PHP_PQclear(res);
170 }
171 }
172
173 static void php_pqtxn_object_write_readonly(zval *object, void *o, zval *value TSRMLS_DC)
174 {
175 php_pqtxn_object_t *obj = o;
176 PGresult *res;
177
178 if ((obj->intern->readonly = z_is_true(value))) {
179 res = PQexec(obj->intern->conn->intern->conn, "SET TRANSACTION READ ONLY");
180 } else {
181 res = PQexec(obj->intern->conn->intern->conn, "SET TRANSACTION READ WRITE");
182 }
183
184 if (res) {
185 php_pqres_success(res TSRMLS_CC);
186 PHP_PQclear(res);
187 }
188 }
189
190 static void php_pqtxn_object_write_deferrable(zval *object, void *o, zval *value TSRMLS_DC)
191 {
192 php_pqtxn_object_t *obj = o;
193 PGresult *res;
194
195 if ((obj->intern->deferrable = z_is_true(value))) {
196 res = PQexec(obj->intern->conn->intern->conn, "SET TRANSACTION DEFERRABLE");
197 } else {
198 res = PQexec(obj->intern->conn->intern->conn, "SET TRANSACTION NOT DEFERRABLE");
199 }
200
201 if (res) {
202 php_pqres_success(res TSRMLS_CC);
203 PHP_PQclear(res);
204 }
205 }
206
207
208 ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_construct, 0, 0, 1)
209 ZEND_ARG_OBJ_INFO(0, connection, pq\\Connection, 0)
210 ZEND_ARG_INFO(0, async)
211 ZEND_ARG_INFO(0, isolation)
212 ZEND_ARG_INFO(0, readonly)
213 ZEND_ARG_INFO(0, deferrable)
214 ZEND_END_ARG_INFO();
215 static PHP_METHOD(pqtxn, __construct) {
216 zend_error_handling zeh;
217 zval *zconn;
218 long isolation = PHP_PQTXN_READ_COMMITTED;
219 zend_bool async = 0, readonly = 0, deferrable = 0;
220 STATUS rv;
221
222 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
223 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|blbb", &zconn, php_pqconn_class_entry, &async, &isolation, &readonly, &deferrable);
224 zend_restore_error_handling(&zeh TSRMLS_CC);
225
226 if (SUCCESS == rv) {
227 php_pqconn_object_t *conn_obj = zend_object_store_get_object(zconn TSRMLS_CC);
228
229 if (!conn_obj->intern) {
230 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Connection not initialized");
231 } else {
232 if (async) {
233 rv = php_pqconn_start_transaction_async(zconn, conn_obj, isolation, readonly, deferrable TSRMLS_CC);
234 } else {
235 rv = php_pqconn_start_transaction(zconn, conn_obj, isolation, readonly, deferrable TSRMLS_CC);
236 }
237
238 if (SUCCESS == rv) {
239 php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
240
241 obj->intern = ecalloc(1, sizeof(*obj->intern));
242
243 php_pq_object_addref(conn_obj TSRMLS_CC);
244 obj->intern->conn = conn_obj;
245 obj->intern->open = 1;
246 obj->intern->isolation = isolation;
247 obj->intern->readonly = readonly;
248 obj->intern->deferrable = deferrable;
249 }
250 }
251 }
252 }
253
254 ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_savepoint, 0, 0, 0)
255 ZEND_END_ARG_INFO();
256 static PHP_METHOD(pqtxn, savepoint) {
257 zend_error_handling zeh;
258 STATUS rv;
259
260 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
261 rv = zend_parse_parameters_none();
262 zend_restore_error_handling(&zeh TSRMLS_CC);
263
264 if (SUCCESS == rv) {
265 php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
266
267 if (!obj->intern) {
268 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized");
269 } else if (!obj->intern->open) {
270 throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transaction already closed");
271 } else {
272 PGresult *res;
273 smart_str cmd = {0};
274
275 smart_str_appends(&cmd, "SAVEPOINT \"");
276 smart_str_append_unsigned(&cmd, ++obj->intern->savepoint);
277 smart_str_appends(&cmd, "\"");
278 smart_str_0(&cmd);
279
280 res = PQexec(obj->intern->conn->intern->conn, cmd.c);
281
282 if (!res) {
283 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to create %s (%s)", cmd.c, PHP_PQerrorMessage(obj->intern->conn->intern->conn));
284 } else {
285 php_pqres_success(res TSRMLS_CC);
286 PHP_PQclear(res);
287 }
288
289 smart_str_free(&cmd);
290 }
291 }
292 }
293
294 ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_savepoint_async, 0, 0, 0)
295 ZEND_END_ARG_INFO();
296 static PHP_METHOD(pqtxn, savepointAsync) {
297 zend_error_handling zeh;
298 STATUS rv;
299
300 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
301 rv = zend_parse_parameters_none();
302 zend_restore_error_handling(&zeh TSRMLS_CC);
303
304 if (SUCCESS == rv) {
305 php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
306
307 if (!obj->intern) {
308 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized");
309 } else if (!obj->intern->open) {
310 throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transaction already closed");
311 } else {
312 smart_str cmd = {0};
313
314 smart_str_appends(&cmd, "SAVEPOINT \"");
315 smart_str_append_unsigned(&cmd, ++obj->intern->savepoint);
316 smart_str_appends(&cmd, "\"");
317 smart_str_0(&cmd);
318
319 if (!PQsendQuery(obj->intern->conn->intern->conn, cmd.c)) {
320 throw_exce(EX_IO TSRMLS_CC, "Failed to create %s (%s)", cmd.c, PHP_PQerrorMessage(obj->intern->conn->intern->conn));
321 }
322
323 smart_str_free(&cmd);
324 }
325 }
326 }
327
328 ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_commit, 0, 0, 0)
329 ZEND_END_ARG_INFO();
330 static PHP_METHOD(pqtxn, commit) {
331 zend_error_handling zeh;
332 STATUS rv;
333
334 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
335 rv = zend_parse_parameters_none();
336 zend_restore_error_handling(&zeh TSRMLS_CC);
337
338 if (SUCCESS == rv) {
339 php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
340
341 if (!obj->intern) {
342 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transacation not initialized");
343 } else if (!obj->intern->open) {
344 throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transacation already closed");
345 } else {
346 PGresult *res;
347 smart_str cmd = {0};
348
349 if (!obj->intern->savepoint) {
350 res = PQexec(obj->intern->conn->intern->conn, "COMMIT");
351 } else {
352 smart_str_appends(&cmd, "RELEASE SAVEPOINT \"");
353 smart_str_append_unsigned(&cmd, obj->intern->savepoint--);
354 smart_str_appends(&cmd, "\"");
355 smart_str_0(&cmd);
356
357 res = PQexec(obj->intern->conn->intern->conn, cmd.c);
358 }
359
360 if (!res) {
361 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to %s (%s)", cmd.c ? cmd.c : "commit transaction", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
362 } else {
363 if (SUCCESS == php_pqres_success(res TSRMLS_CC)) {
364 if (!cmd.c) {
365 obj->intern->open = 0;
366 }
367 }
368 PHP_PQclear(res);
369 }
370
371 smart_str_free(&cmd);
372 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
373 }
374 }
375 }
376
377 ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_commit_async, 0, 0, 0)
378 ZEND_END_ARG_INFO();
379 static PHP_METHOD(pqtxn, commitAsync) {
380 zend_error_handling zeh;
381 STATUS rv;
382
383 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
384 rv = zend_parse_parameters_none();
385 zend_restore_error_handling(&zeh TSRMLS_CC);
386
387 if (SUCCESS == rv) {
388 php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
389
390 if (!obj->intern) {
391 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized");
392 } else if (!obj->intern->open) {
393 throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transaction already closed");
394 } else {
395 int rc;
396 smart_str cmd = {0};
397
398 if (!obj->intern->savepoint) {
399 rc = PQsendQuery(obj->intern->conn->intern->conn, "COMMIT");
400 } else {
401 smart_str_appends(&cmd, "RELEASE SAVEPOINT \"");
402 smart_str_append_unsigned(&cmd, obj->intern->savepoint--);
403 smart_str_appends(&cmd, "\"");
404 smart_str_0(&cmd);
405
406 rc = PQsendQuery(obj->intern->conn->intern->conn, cmd.c);
407 }
408
409 if (!rc) {
410 throw_exce(EX_IO TSRMLS_CC, "Failed to %s (%s)", cmd.c ? cmd.c : "commmit transaction", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
411 } else {
412 if (!cmd.c) {
413 obj->intern->open = 0;
414 }
415 obj->intern->conn->intern->poller = PQconsumeInput;
416 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
417 }
418
419 smart_str_free(&cmd);
420 }
421 }
422 }
423
424 ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_rollback, 0, 0, 0)
425 ZEND_END_ARG_INFO();
426 static PHP_METHOD(pqtxn, rollback) {
427 zend_error_handling zeh;
428 STATUS rv;
429
430 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
431 rv = zend_parse_parameters_none();
432 zend_restore_error_handling(&zeh TSRMLS_CC);
433
434 if (SUCCESS == rv) {
435 php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
436
437 if (!obj->intern) {
438 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized");
439 } else if (!obj->intern->open) {
440 throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transaction already closed");
441 } else {
442 PGresult *res;
443 smart_str cmd = {0};
444
445 if (!obj->intern->savepoint) {
446 res = PQexec(obj->intern->conn->intern->conn, "ROLLBACK");
447 } else {
448 smart_str_appends(&cmd, "ROLLBACK TO SAVEPOINT \"");
449 smart_str_append_unsigned(&cmd, obj->intern->savepoint--);
450 smart_str_appends(&cmd, "\"");
451 smart_str_0(&cmd);
452
453 res = PQexec(obj->intern->conn->intern->conn, cmd.c);
454 }
455
456 if (!res) {
457 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to %s (%s)", cmd.c ? cmd.c : "rollback transaction", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
458 } else {
459 if (SUCCESS == php_pqres_success(res TSRMLS_CC)) {
460 if (!cmd.c) {
461 obj->intern->open = 0;
462 }
463 }
464 PHP_PQclear(res);
465 }
466
467 smart_str_free(&cmd);
468 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
469 }
470 }
471 }
472
473 ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_rollback_async, 0, 0, 0)
474 ZEND_END_ARG_INFO();
475 static PHP_METHOD(pqtxn, rollbackAsync) {
476 zend_error_handling zeh;
477 STATUS rv;
478
479 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
480 rv = zend_parse_parameters_none();
481 zend_restore_error_handling(&zeh TSRMLS_CC);
482
483 if (SUCCESS == rv) {
484 php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
485
486 if (!obj->intern) {
487 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized");
488 } else if (!obj->intern->open) {
489 throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transaction already closed");
490 } else {
491 int rc;
492 smart_str cmd = {0};
493
494 if (!obj->intern->savepoint) {
495 rc = PQsendQuery(obj->intern->conn->intern->conn, "ROLLBACK");
496 } else {
497 smart_str_appends(&cmd, "ROLLBACK TO SAVEPOINT \"");
498 smart_str_append_unsigned(&cmd, obj->intern->savepoint--);
499 smart_str_appends(&cmd, "\"");
500 smart_str_0(&cmd);
501
502 rc = PQsendQuery(obj->intern->conn->intern->conn, cmd.c);
503 }
504
505 if (!rc) {
506 throw_exce(EX_IO TSRMLS_CC, "Failed to %s (%s)", cmd.c ? cmd.c : "rollback transaction", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
507 } else {
508 if (!cmd.c) {
509 obj->intern->open = 0;
510 }
511 obj->intern->conn->intern->poller = PQconsumeInput;
512 }
513
514 smart_str_free(&cmd);
515 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
516 }
517 }
518 }
519
520 ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_export_snapshot, 0, 0, 0)
521 ZEND_END_ARG_INFO();
522 static PHP_METHOD(pqtxn, exportSnapshot) {
523 zend_error_handling zeh;
524 STATUS rv;
525
526 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
527 rv = zend_parse_parameters_none();
528 zend_restore_error_handling(&zeh TSRMLS_CC);
529
530 if (SUCCESS == rv) {
531 php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
532
533 if (!obj->intern) {
534 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized");
535 } else {
536 PGresult *res = PQexec(obj->intern->conn->intern->conn, "SELECT pg_export_snapshot()");
537
538 if (!res) {
539 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to export transaction snapshot (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
540 } else {
541 if (SUCCESS == php_pqres_success(res TSRMLS_CC)) {
542 RETVAL_STRING(PQgetvalue(res, 0, 0), 1);
543 }
544
545 PHP_PQclear(res);
546 }
547
548 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
549 }
550 }
551 }
552
553 ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_export_snapshot_async, 0, 0, 0)
554 ZEND_END_ARG_INFO();
555 static PHP_METHOD(pqtxn, exportSnapshotAsync) {
556 zend_error_handling zeh;
557 STATUS rv;
558
559 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
560 rv = zend_parse_parameters_none();
561 zend_restore_error_handling(&zeh TSRMLS_CC);
562
563 if (SUCCESS == rv) {
564 php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
565
566 if (!obj->intern) {
567 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized");
568 } else if (!PQsendQuery(obj->intern->conn->intern->conn, "SELECT pg_export_snapshot()")) {
569 throw_exce(EX_IO TSRMLS_CC, "Failed to export transaction snapshot (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
570 } else {
571 obj->intern->conn->intern->poller = PQconsumeInput;
572 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
573 }
574 }
575 }
576
577 ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_import_snapshot, 0, 0, 1)
578 ZEND_ARG_INFO(0, snapshot_id)
579 ZEND_END_ARG_INFO();
580 static PHP_METHOD(pqtxn, importSnapshot) {
581 zend_error_handling zeh;
582 char *snapshot_str;
583 int snapshot_len;
584 STATUS rv;
585
586 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
587 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &snapshot_str, &snapshot_len);
588 zend_restore_error_handling(&zeh TSRMLS_CC);
589
590 if (SUCCESS == rv) {
591 php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
592
593 if (!obj->intern) {
594 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized");
595 } else if (obj->intern->isolation < PHP_PQTXN_REPEATABLE_READ) {
596 throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transaction must have at least isolation level REPEATABLE READ to be able to import a snapshot");
597 } else {
598 char *sid = PQescapeLiteral(obj->intern->conn->intern->conn, snapshot_str, snapshot_len);
599
600 if (!sid) {
601 throw_exce(EX_ESCAPE TSRMLS_CC, "Failed to quote snapshot identifier (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
602 } else {
603 PGresult *res;
604 smart_str cmd = {0};
605
606 smart_str_appends(&cmd, "SET TRANSACTION SNAPSHOT ");
607 smart_str_appends(&cmd, sid);
608 smart_str_0(&cmd);
609
610 res = PQexec(obj->intern->conn->intern->conn, cmd.c);
611
612 if (!res) {
613 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to import transaction snapshot (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
614 } else {
615 php_pqres_success(res TSRMLS_CC);
616 PHP_PQclear(res);
617 }
618
619 smart_str_free(&cmd);
620 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
621 }
622 }
623 }
624 }
625
626 ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_import_snapshot_async, 0, 0, 1)
627 ZEND_ARG_INFO(0, snapshot_id)
628 ZEND_END_ARG_INFO();
629 static PHP_METHOD(pqtxn, importSnapshotAsync) {
630 zend_error_handling zeh;
631 char *snapshot_str;
632 int snapshot_len;
633 STATUS rv;
634
635 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
636 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &snapshot_str, &snapshot_len);
637 zend_restore_error_handling(&zeh TSRMLS_CC);
638
639 if (SUCCESS == rv) {
640 php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
641
642 if (!obj->intern) {
643 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized");
644 } else if (obj->intern->isolation < PHP_PQTXN_REPEATABLE_READ) {
645 throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transaction must have at least isolation level REPEATABLE READ to be able to import a snapshot");
646 } else {
647 char *sid = PQescapeLiteral(obj->intern->conn->intern->conn, snapshot_str, snapshot_len);
648
649 if (!sid) {
650 throw_exce(EX_ESCAPE TSRMLS_CC, "Failed to quote snapshot identifier (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
651 } else {
652 smart_str cmd = {0};
653
654 smart_str_appends(&cmd, "SET TRANSACTION SNAPSHOT ");
655 smart_str_appends(&cmd, sid);
656 smart_str_0(&cmd);
657
658 if (!PQsendQuery(obj->intern->conn->intern->conn, cmd.c)) {
659 throw_exce(EX_IO TSRMLS_CC, "Failed to %s (%s)", cmd.c, PHP_PQerrorMessage(obj->intern->conn->intern->conn));
660 } else {
661 obj->intern->conn->intern->poller = PQconsumeInput;
662 }
663
664 smart_str_free(&cmd);
665 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
666 }
667 }
668 }
669 }
670
671 ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_open_lob, 0, 0, 1)
672 ZEND_ARG_INFO(0, oid)
673 ZEND_ARG_INFO(0, mode)
674 ZEND_END_ARG_INFO();
675 static PHP_METHOD(pqtxn, openLOB) {
676 zend_error_handling zeh;
677 long mode = INV_WRITE|INV_READ, loid;
678 STATUS rv;
679
680 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
681 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|l", &loid, &mode);
682 zend_restore_error_handling(&zeh TSRMLS_CC);
683
684 if (SUCCESS == rv) {
685 php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
686
687 if (!obj->intern) {
688 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized");
689 } else {
690 int lofd = lo_open(obj->intern->conn->intern->conn, loid, mode);
691
692 if (lofd < 0) {
693 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to open large object with oid=%u with mode '%s' (%s)", loid, strmode(mode), PHP_PQerrorMessage(obj->intern->conn->intern->conn));
694 } else {
695 php_pqlob_t *lob = ecalloc(1, sizeof(*lob));
696
697 lob->lofd = lofd;
698 lob->loid = loid;
699 php_pq_object_addref(obj TSRMLS_CC);
700 lob->txn = obj;
701
702 return_value->type = IS_OBJECT;
703 return_value->value.obj = php_pqlob_create_object_ex(php_pqlob_class_entry, lob, NULL TSRMLS_CC);
704 }
705
706 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
707 }
708 }
709 }
710
711 ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_create_lob, 0, 0, 0)
712 ZEND_ARG_INFO(0, mode)
713 ZEND_END_ARG_INFO();
714 static PHP_METHOD(pqtxn, createLOB) {
715 zend_error_handling zeh;
716 long mode = INV_WRITE|INV_READ;
717 STATUS rv;
718
719 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
720 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &mode);
721 zend_restore_error_handling(&zeh TSRMLS_CC);
722
723 if (SUCCESS == rv) {
724 php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
725
726 if (!obj->intern) {
727 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized");
728 } else {
729 Oid loid = lo_creat(obj->intern->conn->intern->conn, mode);
730
731 if (loid == InvalidOid) {
732 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to create large object with mode '%s' (%s)", strmode(mode), PHP_PQerrorMessage(obj->intern->conn->intern->conn));
733 } else {
734 int lofd = lo_open(obj->intern->conn->intern->conn, loid, mode);
735
736 if (lofd < 0) {
737 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to open large object with oid=%u with mode '%s': %s", loid, strmode(mode), PHP_PQerrorMessage(obj->intern->conn->intern->conn));
738 } else {
739 php_pqlob_t *lob = ecalloc(1, sizeof(*lob));
740
741 lob->lofd = lofd;
742 lob->loid = loid;
743 php_pq_object_addref(obj TSRMLS_CC);
744 lob->txn = obj;
745
746 return_value->type = IS_OBJECT;
747 return_value->value.obj = php_pqlob_create_object_ex(php_pqlob_class_entry, lob, NULL TSRMLS_CC);
748 }
749 }
750
751 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
752 }
753 }
754 }
755
756 ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_unlink_lob, 0, 0, 1)
757 ZEND_ARG_INFO(0, oid)
758 ZEND_END_ARG_INFO();
759 static PHP_METHOD(pqtxn, unlinkLOB) {
760 zend_error_handling zeh;
761 long loid;
762 STATUS rv;
763
764 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
765 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &loid);
766 zend_restore_error_handling(&zeh TSRMLS_CC);
767
768 if (SUCCESS == rv) {
769 php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
770
771 if (!obj->intern) {
772 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized");
773 } else {
774 int rc = lo_unlink(obj->intern->conn->intern->conn, loid);
775
776 if (rc != 1) {
777 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to unlink LOB (oid=%u): %s", loid, PHP_PQerrorMessage(obj->intern->conn->intern->conn));
778 }
779
780 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
781 }
782 }
783 }
784
785 ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_import_lob, 0, 0, 1)
786 ZEND_ARG_INFO(0, local_path)
787 ZEND_ARG_INFO(0, oid)
788 ZEND_END_ARG_INFO();
789 static PHP_METHOD(pqtxn, importLOB) {
790 zend_error_handling zeh;
791 char *path_str;
792 int path_len;
793 long oid = InvalidOid;
794 STATUS rv;
795
796 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
797 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p|l", &path_str, &path_len, &oid);
798 zend_restore_error_handling(&zeh TSRMLS_CC);
799
800 if (rv == SUCCESS) {
801 php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
802
803 if (!obj->intern) {
804 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized");
805 } else {
806 if (oid == InvalidOid) {
807 oid = lo_import(obj->intern->conn->intern->conn, path_str);
808 } else {
809 oid = lo_import_with_oid(obj->intern->conn->intern->conn, path_str, oid);
810 }
811
812 if (oid == InvalidOid) {
813 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to import LOB from '%s' (%s)", path_str, PHP_PQerrorMessage(obj->intern->conn->intern->conn));
814 } else {
815 RETVAL_LONG(oid);
816 }
817
818 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
819 }
820 }
821 }
822
823 ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_export_lob, 0, 0, 2)
824 ZEND_ARG_INFO(0, oid)
825 ZEND_ARG_INFO(0, local_path)
826 ZEND_END_ARG_INFO();
827 static PHP_METHOD(pqtxn, exportLOB) {
828 zend_error_handling zeh;
829 char *path_str;
830 int path_len;
831 long oid;
832 STATUS rv;
833
834 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
835 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lp", &oid, &path_str, &path_len);
836 zend_restore_error_handling(&zeh TSRMLS_CC);
837
838 if (rv == SUCCESS) {
839 php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
840
841 if (!obj->intern) {
842 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized");
843 } else {
844 int rc = lo_export(obj->intern->conn->intern->conn, oid, path_str);
845
846 if (rc == -1) {
847 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to export LOB (oid=%u) to '%s' (%s)", oid, path_str, PHP_PQerrorMessage(obj->intern->conn->intern->conn));
848 }
849
850 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
851 }
852 }
853 }
854
855 static zend_function_entry php_pqtxn_methods[] = {
856 PHP_ME(pqtxn, __construct, ai_pqtxn_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
857 PHP_ME(pqtxn, commit, ai_pqtxn_commit, ZEND_ACC_PUBLIC)
858 PHP_ME(pqtxn, rollback, ai_pqtxn_rollback, ZEND_ACC_PUBLIC)
859 PHP_ME(pqtxn, commitAsync, ai_pqtxn_commit_async, ZEND_ACC_PUBLIC)
860 PHP_ME(pqtxn, rollbackAsync, ai_pqtxn_rollback_async, ZEND_ACC_PUBLIC)
861 PHP_ME(pqtxn, savepoint, ai_pqtxn_savepoint, ZEND_ACC_PUBLIC)
862 PHP_ME(pqtxn, savepointAsync, ai_pqtxn_savepoint_async, ZEND_ACC_PUBLIC)
863 PHP_ME(pqtxn, exportSnapshot, ai_pqtxn_export_snapshot, ZEND_ACC_PUBLIC)
864 PHP_ME(pqtxn, exportSnapshotAsync, ai_pqtxn_export_snapshot_async, ZEND_ACC_PUBLIC)
865 PHP_ME(pqtxn, importSnapshot, ai_pqtxn_import_snapshot, ZEND_ACC_PUBLIC)
866 PHP_ME(pqtxn, importSnapshotAsync, ai_pqtxn_import_snapshot_async, ZEND_ACC_PUBLIC)
867 PHP_ME(pqtxn, openLOB, ai_pqtxn_open_lob, ZEND_ACC_PUBLIC)
868 PHP_ME(pqtxn, createLOB, ai_pqtxn_create_lob, ZEND_ACC_PUBLIC)
869 PHP_ME(pqtxn, unlinkLOB, ai_pqtxn_unlink_lob, ZEND_ACC_PUBLIC)
870 PHP_ME(pqtxn, importLOB, ai_pqtxn_import_lob, ZEND_ACC_PUBLIC)
871 PHP_ME(pqtxn, exportLOB, ai_pqtxn_export_lob, ZEND_ACC_PUBLIC)
872 {0}
873 };
874
875 PHP_MSHUTDOWN_FUNCTION(pqtxn)
876 {
877 zend_hash_destroy(&php_pqtxn_object_prophandlers);
878 return SUCCESS;
879 }
880
881 PHP_MINIT_FUNCTION(pqtxn)
882 {
883 zend_class_entry ce = {0};
884 php_pq_object_prophandler_t ph = {0};
885
886 INIT_NS_CLASS_ENTRY(ce, "pq", "Transaction", php_pqtxn_methods);
887 php_pqtxn_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC);
888 php_pqtxn_class_entry->create_object = php_pqtxn_create_object;
889
890 memcpy(&php_pqtxn_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
891 php_pqtxn_object_handlers.read_property = php_pq_object_read_prop;
892 php_pqtxn_object_handlers.write_property = php_pq_object_write_prop;
893 php_pqtxn_object_handlers.clone_obj = NULL;
894 php_pqtxn_object_handlers.get_property_ptr_ptr = NULL;
895 php_pqtxn_object_handlers.get_gc = NULL;
896 php_pqtxn_object_handlers.get_properties = php_pq_object_properties;
897 php_pqtxn_object_handlers.get_debug_info = php_pq_object_debug_info;
898
899 zend_hash_init(&php_pqtxn_object_prophandlers, 4, NULL, NULL, 1);
900
901 zend_declare_property_null(php_pqtxn_class_entry, ZEND_STRL("connection"), ZEND_ACC_PUBLIC TSRMLS_CC);
902 ph.read = php_pqtxn_object_read_connection;
903 zend_hash_add(&php_pqtxn_object_prophandlers, "connection", sizeof("connection"), (void *) &ph, sizeof(ph), NULL);
904
905 zend_declare_property_null(php_pqtxn_class_entry, ZEND_STRL("isolation"), ZEND_ACC_PUBLIC TSRMLS_CC);
906 ph.read = php_pqtxn_object_read_isolation;
907 ph.write = php_pqtxn_object_write_isolation;
908 zend_hash_add(&php_pqtxn_object_prophandlers, "isolation", sizeof("isolation"), (void *) &ph, sizeof(ph), NULL);
909
910 zend_declare_property_bool(php_pqtxn_class_entry, ZEND_STRL("readonly"), 0, ZEND_ACC_PUBLIC TSRMLS_CC);
911 ph.read = php_pqtxn_object_read_readonly;
912 ph.write = php_pqtxn_object_write_readonly;
913 zend_hash_add(&php_pqtxn_object_prophandlers, "readonly", sizeof("readonly"), (void *) &ph, sizeof(ph), NULL);
914
915 zend_declare_property_bool(php_pqtxn_class_entry, ZEND_STRL("deferrable"), 0, ZEND_ACC_PUBLIC TSRMLS_CC);
916 ph.read = php_pqtxn_object_read_deferrable;
917 ph.write = php_pqtxn_object_write_deferrable;
918 zend_hash_add(&php_pqtxn_object_prophandlers, "deferrable", sizeof("deferrable"), (void *) &ph, sizeof(ph), NULL);
919 ph.write = NULL;
920
921 zend_declare_class_constant_long(php_pqtxn_class_entry, ZEND_STRL("READ_COMMITTED"), PHP_PQTXN_READ_COMMITTED TSRMLS_CC);
922 zend_declare_class_constant_long(php_pqtxn_class_entry, ZEND_STRL("REPEATABLE_READ"), PHP_PQTXN_REPEATABLE_READ TSRMLS_CC);
923 zend_declare_class_constant_long(php_pqtxn_class_entry, ZEND_STRL("SERIALIZABLE"), PHP_PQTXN_SERIALIZABLE TSRMLS_CC);
924
925 return SUCCESS;
926 }
927
928 /*
929 * Local variables:
930 * tab-width: 4
931 * c-basic-offset: 4
932 * End:
933 * vim600: noet sw=4 ts=4 fdm=marker
934 * vim<600: noet sw=4 ts=4
935 */