+ STATUS rv;
+
+ zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC);
+ rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O|blbb", &zconn, php_pqconn_class_entry, &async, &isolation, &readonly, &deferrable);
+ zend_restore_error_handling(&zeh TSRMLS_CC);
+
+ if (SUCCESS == rv) {
+ php_pqconn_object_t *conn_obj = zend_object_store_get_object(zconn TSRMLS_CC);
+
+ if (!conn_obj->intern) {
+ zend_throw_exception_ex(exce(EX_RUNTIME), EX_RUNTIME TSRMLS_CC, "pq\\Connection not initialized");
+ } else {
+ if (async) {
+ rv = php_pqconn_start_transaction_async(zconn, conn_obj, isolation, readonly, deferrable TSRMLS_CC);
+ } else {
+ rv = php_pqconn_start_transaction(zconn, conn_obj, isolation, readonly, deferrable TSRMLS_CC);
+ }
+
+ if (SUCCESS == rv) {
+ php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+ obj->intern = ecalloc(1, sizeof(*obj->intern));
+
+ php_pq_object_addref(conn_obj TSRMLS_CC);
+ obj->intern->conn = conn_obj;
+ obj->intern->open = 1;
+ obj->intern->isolation = isolation;
+ obj->intern->readonly = readonly;
+ obj->intern->deferrable = deferrable;
+ }
+ }
+ }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_savepoint, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(pqtxn, savepoint) {
+ zend_error_handling zeh;
+ STATUS rv;
+
+ zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC);
+ rv = zend_parse_parameters_none();
+ zend_restore_error_handling(&zeh TSRMLS_CC);
+
+ if (SUCCESS == rv) {
+ php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+ if (!obj->intern) {
+ zend_throw_exception_ex(exce(EX_RUNTIME), EX_RUNTIME TSRMLS_CC, "pq\\Transaction not initialized");
+ } else if (!obj->intern->open) {
+ zend_throw_exception_ex(exce(EX_RUNTIME), EX_RUNTIME TSRMLS_CC, "pq\\Transaction already closed");
+ } else {
+ PGresult *res;
+ smart_str cmd = {0};
+
+ smart_str_appends(&cmd, "SAVEPOINT \"");
+ smart_str_append_unsigned(&cmd, ++obj->intern->savepoint);
+ smart_str_appends(&cmd, "\"");
+ smart_str_0(&cmd);
+
+ res = PQexec(obj->intern->conn->intern->conn, cmd.c);
+
+ if (!res) {
+ zend_throw_exception_ex(exce(EX_RUNTIME), EX_RUNTIME TSRMLS_CC, "Failed to create %s (%s)", cmd.c, PHP_PQerrorMessage(obj->intern->conn->intern->conn));
+ } else {
+ php_pqres_success(res TSRMLS_CC);
+ PHP_PQclear(res);
+ }
+
+ smart_str_free(&cmd);
+ }
+ }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_savepoint_async, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(pqtxn, savepointAsync) {
+ zend_error_handling zeh;
+ STATUS rv;
+
+ zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC);
+ rv = zend_parse_parameters_none();
+ zend_restore_error_handling(&zeh TSRMLS_CC);
+
+ if (SUCCESS == rv) {
+ php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+ if (!obj->intern) {
+ zend_throw_exception_ex(exce(EX_RUNTIME), EX_RUNTIME TSRMLS_CC, "pq\\Transaction not initialized");
+ } else if (!obj->intern->open) {
+ zend_throw_exception_ex(exce(EX_RUNTIME), EX_RUNTIME TSRMLS_CC, "pq\\Transaction already closed");
+ } else {
+ smart_str cmd = {0};
+
+ smart_str_appends(&cmd, "SAVEPOINT \"");
+ smart_str_append_unsigned(&cmd, ++obj->intern->savepoint);
+ smart_str_appends(&cmd, "\"");
+ smart_str_0(&cmd);
+
+ if (!PQsendQuery(obj->intern->conn->intern->conn, cmd.c)) {
+ zend_throw_exception_ex(exce(EX_RUNTIME), EX_RUNTIME TSRMLS_CC, "Failed to create %s (%s)", cmd.c, PHP_PQerrorMessage(obj->intern->conn->intern->conn));
+ }
+
+ smart_str_free(&cmd);
+ }
+ }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_commit, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(pqtxn, commit) {
+ zend_error_handling zeh;
+ STATUS rv;
+
+ zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC);
+ rv = zend_parse_parameters_none();
+ zend_restore_error_handling(&zeh TSRMLS_CC);
+
+ if (SUCCESS == rv) {
+ php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+ if (!obj->intern) {
+ zend_throw_exception_ex(exce(EX_RUNTIME), EX_RUNTIME TSRMLS_CC, "pq\\Transacation not initialized");
+ } else if (!obj->intern->open) {
+ zend_throw_exception_ex(exce(EX_RUNTIME), EX_RUNTIME TSRMLS_CC, "pq\\Transacation already closed");
+ } else {
+ PGresult *res;
+ smart_str cmd = {0};
+
+ if (!obj->intern->savepoint) {
+ res = PQexec(obj->intern->conn->intern->conn, "COMMIT");
+ } else {
+ smart_str_appends(&cmd, "RELEASE SAVEPOINT \"");
+ smart_str_append_unsigned(&cmd, obj->intern->savepoint--);
+ smart_str_appends(&cmd, "\"");
+ smart_str_0(&cmd);
+
+ res = PQexec(obj->intern->conn->intern->conn, cmd.c);
+ }
+
+ if (!res) {
+ zend_throw_exception_ex(exce(EX_RUNTIME), EX_RUNTIME TSRMLS_CC, "Failed to %s (%s)", cmd.c ? cmd.c : "commit transaction", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
+ } else {
+ if (SUCCESS == php_pqres_success(res TSRMLS_CC)) {
+ if (!cmd.c) {
+ obj->intern->open = 0;
+ }
+ }
+ PHP_PQclear(res);
+ }
+
+ smart_str_free(&cmd);
+ php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
+ }
+ }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_commit_async, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(pqtxn, commitAsync) {
+ zend_error_handling zeh;
+ STATUS rv;
+
+ zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC);
+ rv = zend_parse_parameters_none();
+ zend_restore_error_handling(&zeh TSRMLS_CC);
+
+ if (SUCCESS == rv) {
+ php_pqtxn_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC);
+
+ if (!obj->intern) {
+ zend_throw_exception_ex(exce(EX_RUNTIME), EX_RUNTIME TSRMLS_CC, "pq\\Transaction not initialized");
+ } else if (!obj->intern->open) {
+ zend_throw_exception_ex(exce(EX_RUNTIME), EX_RUNTIME TSRMLS_CC, "pq\\Transaction already closed");
+ } else {
+ int rc;
+ smart_str cmd = {0};
+
+ if (!obj->intern->savepoint) {
+ rc = PQsendQuery(obj->intern->conn->intern->conn, "COMMIT");
+ } else {
+ smart_str_appends(&cmd, "RELEASE SAVEPOINT \"");
+ smart_str_append_unsigned(&cmd, obj->intern->savepoint--);
+ smart_str_appends(&cmd, "\"");
+ smart_str_0(&cmd);
+
+ rc = PQsendQuery(obj->intern->conn->intern->conn, cmd.c);
+ }
+
+ if (!rc) {
+ zend_throw_exception_ex(exce(EX_RUNTIME), EX_RUNTIME TSRMLS_CC, "Failed to %s (%s)", cmd.c ? cmd.c : "commmit transaction", PHP_PQerrorMessage(obj->intern->conn->intern->conn));
+ } else {
+ if (!cmd.c) {
+ obj->intern->open = 0;
+ }
+ obj->intern->conn->intern->poller = PQconsumeInput;
+ php_pqconn_notify_listeners(obj->intern->conn TSRMLS_CC);
+ }
+
+ smart_str_free(&cmd);
+ }
+ }
+}
+
+ZEND_BEGIN_ARG_INFO_EX(ai_pqtxn_rollback, 0, 0, 0)
+ZEND_END_ARG_INFO();
+static PHP_METHOD(pqtxn, rollback) {
+ zend_error_handling zeh;
+ STATUS rv;
+
+ zend_replace_error_handling(EH_THROW, NULL, &zeh TSRMLS_CC);
+ rv = zend_parse_parameters_none();
+ zend_restore_error_handling(&zeh TSRMLS_CC);