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