Merge branch 'meta/travis'
[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 *php_pq_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 && obj->intern->conn->intern) {
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
233 switch (ZEND_NUM_ARGS()) {
234 case 1:
235 case 2:
236 isolation = conn_obj->intern->default_txn_isolation;
237 /* no break */
238 case 3:
239 readonly = conn_obj->intern->default_txn_readonly;
240 /* no break */
241 case 4:
242 deferrable = conn_obj->intern->default_txn_deferrable;
243 break;
244 }
245
246 if (async) {
247 rv = php_pqconn_start_transaction_async(zconn, conn_obj, isolation, readonly, deferrable TSRMLS_CC);
248 } else {
249 rv = php_pqconn_start_transaction(zconn, conn_obj, isolation, readonly, deferrable TSRMLS_CC);
250 }
251
252 if (SUCCESS == rv) {
253 php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
254
255 obj->intern = ecalloc(1, sizeof(*obj->intern));
256
257 php_pq_object_addref(conn_obj TSRMLS_CC);
258 obj->intern->conn = conn_obj;
259 obj->intern->open = 1;
260 obj->intern->isolation = isolation;
261 obj->intern->readonly = readonly;
262 obj->intern->deferrable = deferrable;
263 }
264 }
265 }
266 }
267
268 ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_savepoint, 0, 0, 0)
269 ZEND_END_ARG_INFO();
270 static PHP_METHOD(pqtxn, savepoint) {
271 zend_error_handling zeh;
272 STATUS rv;
273
274 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
275 rv = zend_parse_parameters_none();
276 zend_restore_error_handling(&zeh TSRMLS_CC);
277
278 if (SUCCESS == rv) {
279 php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
280
281 if (!obj->intern) {
282 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized");
283 } else if (!obj->intern->open) {
284 throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transaction already closed");
285 } else {
286 PGresult *res;
287 smart_str cmd = {0};
288
289 smart_str_appends(&cmd, "SAVEPOINT \"");
290 smart_str_append_unsigned(&cmd, ++obj->intern->savepoint);
291 smart_str_appends(&cmd, "\"");
292 smart_str_0(&cmd);
293
294 res = PQexec(obj->intern->conn->intern->conn, cmd.c);
295
296 if (!res) {
297 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to create %s (%s)", cmd.c, PHP_PQerrorMessage(obj->intern->conn->intern->conn));
298 } else {
299 php_pqres_success(res TSRMLS_CC);
300 PHP_PQclear(res);
301 }
302
303 smart_str_free(&cmd);
304 }
305 }
306 }
307
308 ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_savepoint_async, 0, 0, 0)
309 ZEND_END_ARG_INFO();
310 static PHP_METHOD(pqtxn, savepointAsync) {
311 zend_error_handling zeh;
312 STATUS rv;
313
314 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
315 rv = zend_parse_parameters_none();
316 zend_restore_error_handling(&zeh TSRMLS_CC);
317
318 if (SUCCESS == rv) {
319 php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
320
321 if (!obj->intern) {
322 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized");
323 } else if (!obj->intern->open) {
324 throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transaction already closed");
325 } else {
326 smart_str cmd = {0};
327
328 smart_str_appends(&cmd, "SAVEPOINT \"");
329 smart_str_append_unsigned(&cmd, ++obj->intern->savepoint);
330 smart_str_appends(&cmd, "\"");
331 smart_str_0(&cmd);
332
333 if (!PQsendQuery(obj->intern->conn->intern->conn, cmd.c)) {
334 throw_exce(EX_IO TSRMLS_CC, "Failed to create %s (%s)", cmd.c, PHP_PQerrorMessage(obj->intern->conn->intern->conn));
335 }
336
337 smart_str_free(&cmd);
338 }
339 }
340 }
341
342 ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_commit, 0, 0, 0)
343 ZEND_END_ARG_INFO();
344 static PHP_METHOD(pqtxn, commit) {
345 zend_error_handling zeh;
346 STATUS rv;
347
348 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
349 rv = zend_parse_parameters_none();
350 zend_restore_error_handling(&zeh TSRMLS_CC);
351
352 if (SUCCESS == rv) {
353 php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
354
355 if (!obj->intern) {
356 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transacation not initialized");
357 } else if (!obj->intern->open) {
358 throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transaction already closed");
359 } else {
360 PGresult *res;
361 smart_str cmd = {0};
362
363 if (!obj->intern->savepoint) {
364 res = PQexec(obj->intern->conn->intern->conn, "COMMIT");
365 } else {
366 smart_str_appends(&cmd, "RELEASE SAVEPOINT \"");
367 smart_str_append_unsigned(&cmd, obj->intern->savepoint--);
368 smart_str_appends(&cmd, "\"");
369 smart_str_0(&cmd);
370
371 res = PQexec(obj->intern->conn->intern->conn, cmd.c);
372 }
373
374 if (!res) {
375 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to %s (%s)", cmd.c ? cmd.c : "commit transaction", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
376 } else {
377 if (SUCCESS == php_pqres_success(res TSRMLS_CC)) {
378 if (!cmd.c) {
379 obj->intern->open = 0;
380 }
381 }
382 PHP_PQclear(res);
383 }
384
385 smart_str_free(&cmd);
386 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
387 }
388 }
389 }
390
391 ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_commit_async, 0, 0, 0)
392 ZEND_END_ARG_INFO();
393 static PHP_METHOD(pqtxn, commitAsync) {
394 zend_error_handling zeh;
395 STATUS rv;
396
397 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
398 rv = zend_parse_parameters_none();
399 zend_restore_error_handling(&zeh TSRMLS_CC);
400
401 if (SUCCESS == rv) {
402 php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
403
404 if (!obj->intern) {
405 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized");
406 } else if (!obj->intern->open) {
407 throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transaction already closed");
408 } else {
409 int rc;
410 smart_str cmd = {0};
411
412 if (!obj->intern->savepoint) {
413 rc = PQsendQuery(obj->intern->conn->intern->conn, "COMMIT");
414 } else {
415 smart_str_appends(&cmd, "RELEASE SAVEPOINT \"");
416 smart_str_append_unsigned(&cmd, obj->intern->savepoint--);
417 smart_str_appends(&cmd, "\"");
418 smart_str_0(&cmd);
419
420 rc = PQsendQuery(obj->intern->conn->intern->conn, cmd.c);
421 }
422
423 if (!rc) {
424 throw_exce(EX_IO TSRMLS_CC, "Failed to %s (%s)", cmd.c ? cmd.c : "commmit transaction", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
425 } else {
426 if (!cmd.c) {
427 obj->intern->open = 0;
428 }
429 obj->intern->conn->intern->poller = PQconsumeInput;
430 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
431 }
432
433 smart_str_free(&cmd);
434 }
435 }
436 }
437
438 ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_rollback, 0, 0, 0)
439 ZEND_END_ARG_INFO();
440 static PHP_METHOD(pqtxn, rollback) {
441 zend_error_handling zeh;
442 STATUS rv;
443
444 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
445 rv = zend_parse_parameters_none();
446 zend_restore_error_handling(&zeh TSRMLS_CC);
447
448 if (SUCCESS == rv) {
449 php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
450
451 if (!obj->intern) {
452 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized");
453 } else if (!obj->intern->open) {
454 throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transaction already closed");
455 } else {
456 PGresult *res;
457 smart_str cmd = {0};
458
459 if (!obj->intern->savepoint) {
460 res = PQexec(obj->intern->conn->intern->conn, "ROLLBACK");
461 } else {
462 smart_str_appends(&cmd, "ROLLBACK TO SAVEPOINT \"");
463 smart_str_append_unsigned(&cmd, obj->intern->savepoint--);
464 smart_str_appends(&cmd, "\"");
465 smart_str_0(&cmd);
466
467 res = PQexec(obj->intern->conn->intern->conn, cmd.c);
468 }
469
470 if (!res) {
471 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to %s (%s)", cmd.c ? cmd.c : "rollback transaction", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
472 } else {
473 if (SUCCESS == php_pqres_success(res TSRMLS_CC)) {
474 if (!cmd.c) {
475 obj->intern->open = 0;
476 }
477 }
478 PHP_PQclear(res);
479 }
480
481 smart_str_free(&cmd);
482 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
483 }
484 }
485 }
486
487 ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_rollback_async, 0, 0, 0)
488 ZEND_END_ARG_INFO();
489 static PHP_METHOD(pqtxn, rollbackAsync) {
490 zend_error_handling zeh;
491 STATUS rv;
492
493 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
494 rv = zend_parse_parameters_none();
495 zend_restore_error_handling(&zeh TSRMLS_CC);
496
497 if (SUCCESS == rv) {
498 php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
499
500 if (!obj->intern) {
501 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized");
502 } else if (!obj->intern->open) {
503 throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transaction already closed");
504 } else {
505 int rc;
506 smart_str cmd = {0};
507
508 if (!obj->intern->savepoint) {
509 rc = PQsendQuery(obj->intern->conn->intern->conn, "ROLLBACK");
510 } else {
511 smart_str_appends(&cmd, "ROLLBACK TO SAVEPOINT \"");
512 smart_str_append_unsigned(&cmd, obj->intern->savepoint--);
513 smart_str_appends(&cmd, "\"");
514 smart_str_0(&cmd);
515
516 rc = PQsendQuery(obj->intern->conn->intern->conn, cmd.c);
517 }
518
519 if (!rc) {
520 throw_exce(EX_IO TSRMLS_CC, "Failed to %s (%s)", cmd.c ? cmd.c : "rollback transaction", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
521 } else {
522 if (!cmd.c) {
523 obj->intern->open = 0;
524 }
525 obj->intern->conn->intern->poller = PQconsumeInput;
526 }
527
528 smart_str_free(&cmd);
529 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
530 }
531 }
532 }
533
534 ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_export_snapshot, 0, 0, 0)
535 ZEND_END_ARG_INFO();
536 static PHP_METHOD(pqtxn, exportSnapshot) {
537 zend_error_handling zeh;
538 STATUS rv;
539
540 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
541 rv = zend_parse_parameters_none();
542 zend_restore_error_handling(&zeh TSRMLS_CC);
543
544 if (SUCCESS == rv) {
545 php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
546
547 if (!obj->intern) {
548 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized");
549 } else {
550 PGresult *res = PQexec(obj->intern->conn->intern->conn, "SELECT pg_export_snapshot()");
551
552 if (!res) {
553 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to export transaction snapshot (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
554 } else {
555 if (SUCCESS == php_pqres_success(res TSRMLS_CC)) {
556 RETVAL_STRING(PQgetvalue(res, 0, 0), 1);
557 }
558
559 PHP_PQclear(res);
560 }
561
562 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
563 }
564 }
565 }
566
567 ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_export_snapshot_async, 0, 0, 0)
568 ZEND_END_ARG_INFO();
569 static PHP_METHOD(pqtxn, exportSnapshotAsync) {
570 zend_error_handling zeh;
571 STATUS rv;
572
573 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
574 rv = zend_parse_parameters_none();
575 zend_restore_error_handling(&zeh TSRMLS_CC);
576
577 if (SUCCESS == rv) {
578 php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
579
580 if (!obj->intern) {
581 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized");
582 } else if (!PQsendQuery(obj->intern->conn->intern->conn, "SELECT pg_export_snapshot()")) {
583 throw_exce(EX_IO TSRMLS_CC, "Failed to export transaction snapshot (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
584 } else {
585 obj->intern->conn->intern->poller = PQconsumeInput;
586 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
587 }
588 }
589 }
590
591 ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_import_snapshot, 0, 0, 1)
592 ZEND_ARG_INFO(0, snapshot_id)
593 ZEND_END_ARG_INFO();
594 static PHP_METHOD(pqtxn, importSnapshot) {
595 zend_error_handling zeh;
596 char *snapshot_str;
597 int snapshot_len;
598 STATUS rv;
599
600 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
601 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &snapshot_str, &snapshot_len);
602 zend_restore_error_handling(&zeh TSRMLS_CC);
603
604 if (SUCCESS == rv) {
605 php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
606
607 if (!obj->intern) {
608 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized");
609 } else if (obj->intern->isolation < PHP_PQTXN_REPEATABLE_READ) {
610 throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transaction must have at least isolation level REPEATABLE READ to be able to import a snapshot");
611 } else {
612 char *sid = PQescapeLiteral(obj->intern->conn->intern->conn, snapshot_str, snapshot_len);
613
614 if (!sid) {
615 throw_exce(EX_ESCAPE TSRMLS_CC, "Failed to quote snapshot identifier (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
616 } else {
617 PGresult *res;
618 smart_str cmd = {0};
619
620 smart_str_appends(&cmd, "SET TRANSACTION SNAPSHOT ");
621 smart_str_appends(&cmd, sid);
622 smart_str_0(&cmd);
623
624 res = PQexec(obj->intern->conn->intern->conn, cmd.c);
625
626 if (!res) {
627 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to import transaction snapshot (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
628 } else {
629 php_pqres_success(res TSRMLS_CC);
630 PHP_PQclear(res);
631 }
632
633 smart_str_free(&cmd);
634 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
635 }
636 }
637 }
638 }
639
640 ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_import_snapshot_async, 0, 0, 1)
641 ZEND_ARG_INFO(0, snapshot_id)
642 ZEND_END_ARG_INFO();
643 static PHP_METHOD(pqtxn, importSnapshotAsync) {
644 zend_error_handling zeh;
645 char *snapshot_str;
646 int snapshot_len;
647 STATUS rv;
648
649 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
650 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &snapshot_str, &snapshot_len);
651 zend_restore_error_handling(&zeh TSRMLS_CC);
652
653 if (SUCCESS == rv) {
654 php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
655
656 if (!obj->intern) {
657 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized");
658 } else if (obj->intern->isolation < PHP_PQTXN_REPEATABLE_READ) {
659 throw_exce(EX_RUNTIME TSRMLS_CC, "pq\\Transaction must have at least isolation level REPEATABLE READ to be able to import a snapshot");
660 } else {
661 char *sid = PQescapeLiteral(obj->intern->conn->intern->conn, snapshot_str, snapshot_len);
662
663 if (!sid) {
664 throw_exce(EX_ESCAPE TSRMLS_CC, "Failed to quote snapshot identifier (%s)", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
665 } else {
666 smart_str cmd = {0};
667
668 smart_str_appends(&cmd, "SET TRANSACTION SNAPSHOT ");
669 smart_str_appends(&cmd, sid);
670 smart_str_0(&cmd);
671
672 if (!PQsendQuery(obj->intern->conn->intern->conn, cmd.c)) {
673 throw_exce(EX_IO TSRMLS_CC, "Failed to %s (%s)", cmd.c, PHP_PQerrorMessage(obj->intern->conn->intern->conn));
674 } else {
675 obj->intern->conn->intern->poller = PQconsumeInput;
676 }
677
678 smart_str_free(&cmd);
679 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
680 }
681 }
682 }
683 }
684
685 ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_open_lob, 0, 0, 1)
686 ZEND_ARG_INFO(0, oid)
687 ZEND_ARG_INFO(0, mode)
688 ZEND_END_ARG_INFO();
689 static PHP_METHOD(pqtxn, openLOB) {
690 zend_error_handling zeh;
691 long mode = INV_WRITE|INV_READ, loid;
692 STATUS rv;
693
694 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
695 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|l", &loid, &mode);
696 zend_restore_error_handling(&zeh TSRMLS_CC);
697
698 if (SUCCESS == rv) {
699 php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
700
701 if (!obj->intern) {
702 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized");
703 } else {
704 int lofd = lo_open(obj->intern->conn->intern->conn, loid, mode);
705
706 if (lofd < 0) {
707 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to open large object with oid=%u with mode '%s' (%s)", loid, php_pq_strmode(mode), PHP_PQerrorMessage(obj->intern->conn->intern->conn));
708 } else {
709 php_pqlob_t *lob = ecalloc(1, sizeof(*lob));
710
711 lob->lofd = lofd;
712 lob->loid = loid;
713 php_pq_object_addref(obj TSRMLS_CC);
714 lob->txn = obj;
715
716 return_value->type = IS_OBJECT;
717 return_value->value.obj = php_pqlob_create_object_ex(php_pqlob_class_entry, lob, NULL TSRMLS_CC);
718 }
719
720 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
721 }
722 }
723 }
724
725 ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_create_lob, 0, 0, 0)
726 ZEND_ARG_INFO(0, mode)
727 ZEND_END_ARG_INFO();
728 static PHP_METHOD(pqtxn, createLOB) {
729 zend_error_handling zeh;
730 long mode = INV_WRITE|INV_READ;
731 STATUS rv;
732
733 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
734 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &mode);
735 zend_restore_error_handling(&zeh TSRMLS_CC);
736
737 if (SUCCESS == rv) {
738 php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
739
740 if (!obj->intern) {
741 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized");
742 } else {
743 Oid loid = lo_creat(obj->intern->conn->intern->conn, mode);
744
745 if (loid == InvalidOid) {
746 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to create large object with mode '%s' (%s)", php_pq_strmode(mode), PHP_PQerrorMessage(obj->intern->conn->intern->conn));
747 } else {
748 int lofd = lo_open(obj->intern->conn->intern->conn, loid, mode);
749
750 if (lofd < 0) {
751 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to open large object with oid=%u with mode '%s': %s", loid, php_pq_strmode(mode), PHP_PQerrorMessage(obj->intern->conn->intern->conn));
752 } else {
753 php_pqlob_t *lob = ecalloc(1, sizeof(*lob));
754
755 lob->lofd = lofd;
756 lob->loid = loid;
757 php_pq_object_addref(obj TSRMLS_CC);
758 lob->txn = obj;
759
760 return_value->type = IS_OBJECT;
761 return_value->value.obj = php_pqlob_create_object_ex(php_pqlob_class_entry, lob, NULL TSRMLS_CC);
762 }
763 }
764
765 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
766 }
767 }
768 }
769
770 ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_unlink_lob, 0, 0, 1)
771 ZEND_ARG_INFO(0, oid)
772 ZEND_END_ARG_INFO();
773 static PHP_METHOD(pqtxn, unlinkLOB) {
774 zend_error_handling zeh;
775 long loid;
776 STATUS rv;
777
778 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
779 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &loid);
780 zend_restore_error_handling(&zeh TSRMLS_CC);
781
782 if (SUCCESS == rv) {
783 php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
784
785 if (!obj->intern) {
786 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized");
787 } else {
788 int rc = lo_unlink(obj->intern->conn->intern->conn, loid);
789
790 if (rc != 1) {
791 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to unlink LOB (oid=%u): %s", loid, PHP_PQerrorMessage(obj->intern->conn->intern->conn));
792 }
793
794 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
795 }
796 }
797 }
798
799 ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_import_lob, 0, 0, 1)
800 ZEND_ARG_INFO(0, local_path)
801 ZEND_ARG_INFO(0, oid)
802 ZEND_END_ARG_INFO();
803 static PHP_METHOD(pqtxn, importLOB) {
804 zend_error_handling zeh;
805 char *path_str;
806 int path_len;
807 long oid = InvalidOid;
808 STATUS rv;
809
810 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
811 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p|l", &path_str, &path_len, &oid);
812 zend_restore_error_handling(&zeh TSRMLS_CC);
813
814 if (rv == SUCCESS) {
815 php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
816
817 if (!obj->intern) {
818 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized");
819 } else {
820 if (oid == InvalidOid) {
821 oid = lo_import(obj->intern->conn->intern->conn, path_str);
822 } else {
823 oid = lo_import_with_oid(obj->intern->conn->intern->conn, path_str, oid);
824 }
825
826 if (oid == InvalidOid) {
827 throw_exce(EX_RUNTIME TSRMLS_CC, "Failed to import LOB from '%s' (%s)", path_str, PHP_PQerrorMessage(obj->intern->conn->intern->conn));
828 } else {
829 RETVAL_LONG(oid);
830 }
831
832 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
833 }
834 }
835 }
836
837 ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_export_lob, 0, 0, 2)
838 ZEND_ARG_INFO(0, oid)
839 ZEND_ARG_INFO(0, local_path)
840 ZEND_END_ARG_INFO();
841 static PHP_METHOD(pqtxn, exportLOB) {
842 zend_error_handling zeh;
843 char *path_str;
844 int path_len;
845 long oid;
846 STATUS rv;
847
848 zend_replace_error_handling(EH_THROW, exce(EX_INVALID_ARGUMENT), &zeh TSRMLS_CC);
849 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lp", &oid, &path_str, &path_len);
850 zend_restore_error_handling(&zeh TSRMLS_CC);
851
852 if (rv == SUCCESS) {
853 php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
854
855 if (!obj->intern) {
856 throw_exce(EX_UNINITIALIZED TSRMLS_CC, "pq\\Transaction not initialized");
857 } else {
858 int rc = lo_export(obj->intern->conn->intern->conn, oid, path_str);
859
860 if (rc == -1) {
861 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));
862 }
863
864 php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
865 }
866 }
867 }
868
869 static zend_function_entry php_pqtxn_methods[] = {
870 PHP_ME(pqtxn, __construct, ai_pqtxn_construct, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR)
871 PHP_ME(pqtxn, commit, ai_pqtxn_commit, ZEND_ACC_PUBLIC)
872 PHP_ME(pqtxn, rollback, ai_pqtxn_rollback, ZEND_ACC_PUBLIC)
873 PHP_ME(pqtxn, commitAsync, ai_pqtxn_commit_async, ZEND_ACC_PUBLIC)
874 PHP_ME(pqtxn, rollbackAsync, ai_pqtxn_rollback_async, ZEND_ACC_PUBLIC)
875 PHP_ME(pqtxn, savepoint, ai_pqtxn_savepoint, ZEND_ACC_PUBLIC)
876 PHP_ME(pqtxn, savepointAsync, ai_pqtxn_savepoint_async, ZEND_ACC_PUBLIC)
877 PHP_ME(pqtxn, exportSnapshot, ai_pqtxn_export_snapshot, ZEND_ACC_PUBLIC)
878 PHP_ME(pqtxn, exportSnapshotAsync, ai_pqtxn_export_snapshot_async, ZEND_ACC_PUBLIC)
879 PHP_ME(pqtxn, importSnapshot, ai_pqtxn_import_snapshot, ZEND_ACC_PUBLIC)
880 PHP_ME(pqtxn, importSnapshotAsync, ai_pqtxn_import_snapshot_async, ZEND_ACC_PUBLIC)
881 PHP_ME(pqtxn, openLOB, ai_pqtxn_open_lob, ZEND_ACC_PUBLIC)
882 PHP_ME(pqtxn, createLOB, ai_pqtxn_create_lob, ZEND_ACC_PUBLIC)
883 PHP_ME(pqtxn, unlinkLOB, ai_pqtxn_unlink_lob, ZEND_ACC_PUBLIC)
884 PHP_ME(pqtxn, importLOB, ai_pqtxn_import_lob, ZEND_ACC_PUBLIC)
885 PHP_ME(pqtxn, exportLOB, ai_pqtxn_export_lob, ZEND_ACC_PUBLIC)
886 {0}
887 };
888
889 PHP_MSHUTDOWN_FUNCTION(pqtxn)
890 {
891 zend_hash_destroy(&php_pqtxn_object_prophandlers);
892 return SUCCESS;
893 }
894
895 PHP_MINIT_FUNCTION(pqtxn)
896 {
897 zend_class_entry ce = {0};
898 php_pq_object_prophandler_t ph = {0};
899
900 INIT_NS_CLASS_ENTRY(ce, "pq", "Transaction", php_pqtxn_methods);
901 php_pqtxn_class_entry = zend_register_internal_class_ex(&ce, NULL, NULL TSRMLS_CC);
902 php_pqtxn_class_entry->create_object = php_pqtxn_create_object;
903
904 memcpy(&php_pqtxn_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
905 php_pqtxn_object_handlers.read_property = php_pq_object_read_prop;
906 php_pqtxn_object_handlers.write_property = php_pq_object_write_prop;
907 php_pqtxn_object_handlers.clone_obj = NULL;
908 php_pqtxn_object_handlers.get_property_ptr_ptr = NULL;
909 php_pqtxn_object_handlers.get_gc = NULL;
910 php_pqtxn_object_handlers.get_properties = php_pq_object_properties;
911 php_pqtxn_object_handlers.get_debug_info = php_pq_object_debug_info;
912
913 zend_hash_init(&php_pqtxn_object_prophandlers, 4, NULL, NULL, 1);
914
915 zend_declare_property_null(php_pqtxn_class_entry, ZEND_STRL("connection"), ZEND_ACC_PUBLIC TSRMLS_CC);
916 ph.read = php_pqtxn_object_read_connection;
917 zend_hash_add(&php_pqtxn_object_prophandlers, "connection", sizeof("connection"), (void *) &ph, sizeof(ph), NULL);
918
919 zend_declare_property_null(php_pqtxn_class_entry, ZEND_STRL("isolation"), ZEND_ACC_PUBLIC TSRMLS_CC);
920 ph.read = php_pqtxn_object_read_isolation;
921 ph.write = php_pqtxn_object_write_isolation;
922 zend_hash_add(&php_pqtxn_object_prophandlers, "isolation", sizeof("isolation"), (void *) &ph, sizeof(ph), NULL);
923
924 zend_declare_property_bool(php_pqtxn_class_entry, ZEND_STRL("readonly"), 0, ZEND_ACC_PUBLIC TSRMLS_CC);
925 ph.read = php_pqtxn_object_read_readonly;
926 ph.write = php_pqtxn_object_write_readonly;
927 zend_hash_add(&php_pqtxn_object_prophandlers, "readonly", sizeof("readonly"), (void *) &ph, sizeof(ph), NULL);
928
929 zend_declare_property_bool(php_pqtxn_class_entry, ZEND_STRL("deferrable"), 0, ZEND_ACC_PUBLIC TSRMLS_CC);
930 ph.read = php_pqtxn_object_read_deferrable;
931 ph.write = php_pqtxn_object_write_deferrable;
932 zend_hash_add(&php_pqtxn_object_prophandlers, "deferrable", sizeof("deferrable"), (void *) &ph, sizeof(ph), NULL);
933 ph.write = NULL;
934
935 zend_declare_class_constant_long(php_pqtxn_class_entry, ZEND_STRL("READ_COMMITTED"), PHP_PQTXN_READ_COMMITTED TSRMLS_CC);
936 zend_declare_class_constant_long(php_pqtxn_class_entry, ZEND_STRL("REPEATABLE_READ"), PHP_PQTXN_REPEATABLE_READ TSRMLS_CC);
937 zend_declare_class_constant_long(php_pqtxn_class_entry, ZEND_STRL("SERIALIZABLE"), PHP_PQTXN_SERIALIZABLE TSRMLS_CC);
938
939 return SUCCESS;
940 }
941
942 /*
943 * Local variables:
944 * tab-width: 4
945 * c-basic-offset: 4
946 * End:
947 * vim600: noet sw=4 ts=4 fdm=marker
948 * vim<600: noet sw=4 ts=4
949 */