|
|
@@ -0,0 +1,36 @@
|
|
|
+package pgserver
|
|
|
+
|
|
|
+import (
|
|
|
+ "testing"
|
|
|
+)
|
|
|
+
|
|
|
+// A constraint failure at COMMIT is a normal conflict: storage has already
|
|
|
+// rolled the transaction back, so the wire session must return to idle instead
|
|
|
+// of rejecting every later statement, including ROLLBACK.
|
|
|
+func TestFailedCommitClearsTransactionState(t *testing.T) {
|
|
|
+ c, client := newDataConnection(t)
|
|
|
+ runQuery(t, c, client, &Message{Type: MsgQuery, Data: append([]byte("CREATE TABLE t (id INTEGER PRIMARY KEY, email TEXT UNIQUE)"), 0)})
|
|
|
+ runQuery(t, c, client, &Message{Type: MsgQuery, Data: append([]byte("INSERT INTO t VALUES (1, 'a@x')"), 0)})
|
|
|
+
|
|
|
+ runQuery(t, c, client, &Message{Type: MsgQuery, Data: append([]byte("BEGIN"), 0)})
|
|
|
+ runQuery(t, c, client, &Message{Type: MsgQuery, Data: append([]byte("INSERT INTO t VALUES (2, 'a@x')"), 0)})
|
|
|
+
|
|
|
+ msgs := runQuery(t, c, client, &Message{Type: MsgQuery, Data: append([]byte("COMMIT"), 0)})
|
|
|
+ if len(msgs) == 0 || msgs[0].Type != MsgErrorResponse {
|
|
|
+ t.Fatalf("COMMIT should fail with the buffered unique violation, got %v", msgs)
|
|
|
+ }
|
|
|
+ if c.txStatus != TxStatusIdle {
|
|
|
+ t.Fatalf("tx status = %v, want idle after a failed COMMIT", c.txStatus)
|
|
|
+ }
|
|
|
+
|
|
|
+ msgs = runQuery(t, c, client, &Message{Type: MsgQuery, Data: append([]byte("SELECT 1"), 0)})
|
|
|
+ if len(msgs) < 2 || msgs[0].Type != MsgRowDescription {
|
|
|
+ t.Fatalf("SELECT after failed COMMIT = %v, want a result", msgs)
|
|
|
+ }
|
|
|
+
|
|
|
+ runQuery(t, c, client, &Message{Type: MsgQuery, Data: append([]byte("BEGIN"), 0)})
|
|
|
+ msgs = runQuery(t, c, client, &Message{Type: MsgQuery, Data: append([]byte("ROLLBACK"), 0)})
|
|
|
+ if len(msgs) == 0 || msgs[0].Type == MsgErrorResponse {
|
|
|
+ t.Fatalf("BEGIN/ROLLBACK after failed COMMIT = %v", msgs)
|
|
|
+ }
|
|
|
+}
|