Copilot commented on code in PR #4424:
URL: https://github.com/apache/arrow-adbc/pull/4424#discussion_r3450235030


##########
c/driver/postgresql/connection.cc:
##########
@@ -489,6 +489,26 @@ AdbcStatusCode PostgresConnection::Commit(struct 
AdbcError* error) {
   return ADBC_STATUS_OK;
 }
 
+AdbcStatusCode PostgresConnection::EnsureTransaction(struct AdbcError* error) {
+  if (autocommit_) {
+    return ADBC_STATUS_OK;
+  }
+  auto txstatus = PQtransactionStatus(conn_);
+  if (txstatus == PQTRANS_ACTIVE || txstatus == PQTRANS_INTRANS) {
+    return ADBC_STATUS_OK;
+  }
+
+  PGresult* result = PQexec(conn_, "BEGIN TRANSACTION");
+  if (PQresultStatus(result) != PGRES_COMMAND_OK) {
+    InternalAdbcSetError(error, "%s%s",
+                         "[libpq] Failed to begin transaction: ", 
PQerrorMessage(conn_));
+    PQclear(result);
+    return ADBC_STATUS_IO;
+  }
+  PQclear(result);
+  return ADBC_STATUS_OK;
+}

Review Comment:
   EnsureTransaction() attempts to start a new transaction for any 
non-(ACTIVE|INTRANS) state. If the connection is in PQTRANS_INERROR (aborted 
transaction), sending "BEGIN TRANSACTION" will always fail with "current 
transaction is aborted" and masks the real issue; callers should instead be 
told to rollback before executing more statements (and avoid changing the error 
classification to IO). Handling PQTRANS_UNKNOWN explicitly would also avoid 
starting a transaction when libpq can't determine state.



##########
python/adbc_driver_postgresql/tests/test_dbapi.py:
##########
@@ -723,3 +723,50 @@ def test_bind_null_unknown_inference(postgres: 
dbapi.Connection) -> None:
         result = cur.fetchone()
         assert result is not None
         assert result[0] is None
+
+
+def test_transaction(postgres_uri: str) -> None:
+    with dbapi.connect(postgres_uri) as conn1, dbapi.connect(postgres_uri) as 
conn2:
+        with conn1.cursor() as cur1:
+            cur1.execute("DROP TABLE IF EXISTS test_transaction")
+        conn1.commit()
+
+        with conn1.cursor() as cur1:
+            cur1.execute("CREATE TABLE test_transaction (a INTEGER)")
+
+        with conn2.cursor() as cur2:
+            with pytest.raises(dbapi.ProgrammingError) as excinfo:
+                cur2.execute("INSERT INTO test_transaction VALUES (1)")
+        assert excinfo.value.sqlstate == "42P01"
+        conn2.rollback()
+
+        with conn1.cursor() as cur1:
+            cur1.execute("INSERT INTO test_transaction VALUES (1)")
+
+        conn1.rollback()
+
+        with conn1.cursor() as cur1:
+            with pytest.raises(dbapi.ProgrammingError) as excinfo:
+                cur1.execute("INSERT INTO test_transaction VALUES (1)")
+

Review Comment:
   The second `pytest.raises(... ) as excinfo` in `test_transaction` never 
asserts anything about the captured exception, which makes the test less 
deterministic (it would pass for any ProgrammingError). Since the first similar 
block asserts sqlstate, this one should too.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to