transactions.pt 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616
  1. # Test implicit and explicit transaction semantics.
  2. # See "Multiple Statements in a Simple Query"
  3. # From https://www.postgresql.org/docs/current/protocol-flow.html
  4. # Note: all of the "SELECT 1/(SELECT 0)" things are here to produce a
  5. # query error. We do not use SELECT 1/0 because in Postgres that doesn't
  6. # send a RowDescription message while the former does. This is probably
  7. # due to Postgres constant folding rules that run before its execution
  8. # phase. This isn't important to the protocol, and both (sending or not
  9. # sending RowDescription) are valid. Because we send a RowDescription
  10. # on SELECT 1/0, we use the more complicated form to force Postgres to
  11. # also send one, unifying the responses here.
  12. # "When a simple Query message contains more than one SQL statement
  13. # (separated by semicolons), those statements are executed as a single
  14. # transaction, unless explicit transaction control commands are included
  15. # to force a different behavior."
  16. send
  17. Query {"query": "SELECT 1; SELECT 1/(SELECT 0); SELECT 2;"}
  18. ----
  19. # Our error codes differ, so only extract the message.
  20. until err_field_typs=M
  21. ReadyForQuery
  22. ----
  23. RowDescription {"fields":[{"name":"?column?"}]}
  24. DataRow {"fields":["1"]}
  25. CommandComplete {"tag":"SELECT 1"}
  26. RowDescription {"fields":[{"name":"?column?"}]}
  27. ErrorResponse {"fields":[{"typ":"M","value":"division by zero"}]}
  28. ReadyForQuery {"status":"I"}
  29. # There are two transactions here, the first (explicit) succeeds and
  30. # the second (implicitly started after the COMMIT) fails.
  31. send
  32. Query {"query": "BEGIN; SELECT 1; COMMIT; SELECT 1/(SELECT 0); SELECT 2"}
  33. ----
  34. until err_field_typs=M
  35. ReadyForQuery
  36. ----
  37. CommandComplete {"tag":"BEGIN"}
  38. RowDescription {"fields":[{"name":"?column?"}]}
  39. DataRow {"fields":["1"]}
  40. CommandComplete {"tag":"SELECT 1"}
  41. CommandComplete {"tag":"COMMIT"}
  42. RowDescription {"fields":[{"name":"?column?"}]}
  43. ErrorResponse {"fields":[{"typ":"M","value":"division by zero"}]}
  44. ReadyForQuery {"status":"I"}
  45. # The transaction fails, so statements after it should not be executed,
  46. # thus the ROLLBACK should not be executed in the first Query.
  47. send
  48. Query {"query": "BEGIN; SELECT 1/(SELECT 0); ROLLBACK;"}
  49. Query {"query": "ROLLBACK"}
  50. ----
  51. until err_field_typs=M
  52. ReadyForQuery
  53. ReadyForQuery
  54. ----
  55. CommandComplete {"tag":"BEGIN"}
  56. RowDescription {"fields":[{"name":"?column?"}]}
  57. ErrorResponse {"fields":[{"typ":"M","value":"division by zero"}]}
  58. ReadyForQuery {"status":"E"}
  59. CommandComplete {"tag":"ROLLBACK"}
  60. ReadyForQuery {"status":"I"}
  61. # In implicit transactions, an error with a later ROLLBACK still doesn't execute
  62. # anything after the error, but also doesn't need to have the next query start
  63. # with ROLLBACK.
  64. send
  65. Query {"query": "SELECT 1; SELECT 1/(SELECT 0); SELECT 2; ROLLBACK; SELECT 3"}
  66. Query {"query": "SELECT 4"}
  67. ----
  68. until err_field_typs=M
  69. ReadyForQuery
  70. ReadyForQuery
  71. ----
  72. RowDescription {"fields":[{"name":"?column?"}]}
  73. DataRow {"fields":["1"]}
  74. CommandComplete {"tag":"SELECT 1"}
  75. RowDescription {"fields":[{"name":"?column?"}]}
  76. ErrorResponse {"fields":[{"typ":"M","value":"division by zero"}]}
  77. ReadyForQuery {"status":"I"}
  78. RowDescription {"fields":[{"name":"?column?"}]}
  79. DataRow {"fields":["4"]}
  80. CommandComplete {"tag":"SELECT 1"}
  81. ReadyForQuery {"status":"I"}
  82. # The entire query is parsed at once, preventing any of it from running if that fails.
  83. send
  84. Query {"query": "BEGIN; SELECT 1; COMMIT; SELCT 1/(SELECT 0);"}
  85. ----
  86. until err_field_typs=C
  87. ErrorResponse
  88. ReadyForQuery
  89. ----
  90. ErrorResponse {"fields":[{"typ":"C","value":"42601"}]}
  91. ReadyForQuery {"status":"I"}
  92. # "If the BEGIN follows some statements that were executed as an implicit
  93. # transaction block, those statements are not immediately committed;
  94. # in effect, they are retroactively included into the new regular
  95. # transaction block."
  96. send
  97. Query {"query": "SELECT 1; BEGIN; SELECT 2;"}
  98. Query {"query": "COMMIT"}
  99. ----
  100. until
  101. ReadyForQuery
  102. ReadyForQuery
  103. ----
  104. RowDescription {"fields":[{"name":"?column?"}]}
  105. DataRow {"fields":["1"]}
  106. CommandComplete {"tag":"SELECT 1"}
  107. CommandComplete {"tag":"BEGIN"}
  108. RowDescription {"fields":[{"name":"?column?"}]}
  109. DataRow {"fields":["2"]}
  110. CommandComplete {"tag":"SELECT 1"}
  111. ReadyForQuery {"status":"T"}
  112. CommandComplete {"tag":"COMMIT"}
  113. ReadyForQuery {"status":"I"}
  114. # "If the session is already in a transaction block, as a result of a
  115. # BEGIN in some previous message, then the Query message simply continues
  116. # that transaction block, whether the message contains one statement or
  117. # several. However, if the Query message contains a COMMIT or ROLLBACK
  118. # closing the existing transaction block, then any following statements
  119. # are executed in an implicit transaction block."
  120. send
  121. Query {"query": "BEGIN; SELECT 1/(SELECT 0); SELECT 1;"}
  122. Query {"query": "SELECT 2; ROLLBACK; SELECT 3;"}
  123. Query {"query": "ROLLBACK; SELECT 4;"}
  124. ----
  125. until err_field_typs=M
  126. ReadyForQuery
  127. ReadyForQuery
  128. ReadyForQuery
  129. ----
  130. CommandComplete {"tag":"BEGIN"}
  131. RowDescription {"fields":[{"name":"?column?"}]}
  132. ErrorResponse {"fields":[{"typ":"M","value":"division by zero"}]}
  133. ReadyForQuery {"status":"E"}
  134. ErrorResponse {"fields":[{"typ":"M","value":"current transaction is aborted, commands ignored until end of transaction block"}]}
  135. ReadyForQuery {"status":"E"}
  136. CommandComplete {"tag":"ROLLBACK"}
  137. RowDescription {"fields":[{"name":"?column?"}]}
  138. DataRow {"fields":["4"]}
  139. CommandComplete {"tag":"SELECT 1"}
  140. ReadyForQuery {"status":"I"}
  141. # "A COMMIT or ROLLBACK appearing in an implicit transaction block is
  142. # executed as normal, closing the implicit block; however, a warning
  143. # will be issued since a COMMIT or ROLLBACK without a previous BEGIN
  144. # might represent a mistake. If more statements follow, a new implicit
  145. # transaction block will be started for them."
  146. send
  147. Query {"query": "SELECT 1; COMMIT; SELECT 2"}
  148. Query {"query": "SELECT 3; ROLLBACK; SELECT 4"}
  149. Query {"query": "COMMIT;"}
  150. Query {"query": "ROLLBACK;"}
  151. ----
  152. until
  153. ReadyForQuery
  154. ReadyForQuery
  155. ReadyForQuery
  156. ReadyForQuery
  157. ----
  158. RowDescription {"fields":[{"name":"?column?"}]}
  159. DataRow {"fields":["1"]}
  160. CommandComplete {"tag":"SELECT 1"}
  161. NoticeResponse {"fields":[{"typ":"S","value":"WARNING"},{"typ":"C","value":"25P01"},{"typ":"M","value":"there is no transaction in progress"}]}
  162. CommandComplete {"tag":"COMMIT"}
  163. RowDescription {"fields":[{"name":"?column?"}]}
  164. DataRow {"fields":["2"]}
  165. CommandComplete {"tag":"SELECT 1"}
  166. ReadyForQuery {"status":"I"}
  167. RowDescription {"fields":[{"name":"?column?"}]}
  168. DataRow {"fields":["3"]}
  169. CommandComplete {"tag":"SELECT 1"}
  170. NoticeResponse {"fields":[{"typ":"S","value":"WARNING"},{"typ":"C","value":"25P01"},{"typ":"M","value":"there is no transaction in progress"}]}
  171. CommandComplete {"tag":"ROLLBACK"}
  172. RowDescription {"fields":[{"name":"?column?"}]}
  173. DataRow {"fields":["4"]}
  174. CommandComplete {"tag":"SELECT 1"}
  175. ReadyForQuery {"status":"I"}
  176. NoticeResponse {"fields":[{"typ":"S","value":"WARNING"},{"typ":"C","value":"25P01"},{"typ":"M","value":"there is no transaction in progress"}]}
  177. CommandComplete {"tag":"COMMIT"}
  178. ReadyForQuery {"status":"I"}
  179. NoticeResponse {"fields":[{"typ":"S","value":"WARNING"},{"typ":"C","value":"25P01"},{"typ":"M","value":"there is no transaction in progress"}]}
  180. CommandComplete {"tag":"ROLLBACK"}
  181. ReadyForQuery {"status":"I"}
  182. send
  183. Query {"query": "BEGIN;"}
  184. Query {"query": "BEGIN;"}
  185. ----
  186. until
  187. ReadyForQuery
  188. ReadyForQuery
  189. ----
  190. CommandComplete {"tag":"BEGIN"}
  191. ReadyForQuery {"status":"T"}
  192. NoticeResponse {"fields":[{"typ":"S","value":"WARNING"},{"typ":"C","value":"25001"},{"typ":"M","value":"there is already a transaction in progress"}]}
  193. CommandComplete {"tag":"BEGIN"}
  194. ReadyForQuery {"status":"T"}
  195. send
  196. Query {"query": "COMMIT;"}
  197. ----
  198. until
  199. ReadyForQuery
  200. ----
  201. CommandComplete {"tag":"COMMIT"}
  202. ReadyForQuery {"status":"I"}
  203. # Test a failure, rollback, failure, rollback chain to verify that we never process after the first failure.
  204. send
  205. Query {"query": "SELECT 1/(SELECT 0); ROLLBACK; SELECT 2; SELECT 1/(SELECT 0); ROLLBACK;"}
  206. ----
  207. until err_field_typs=M
  208. ReadyForQuery
  209. ----
  210. RowDescription {"fields":[{"name":"?column?"}]}
  211. ErrorResponse {"fields":[{"typ":"M","value":"division by zero"}]}
  212. ReadyForQuery {"status":"I"}
  213. # Test DISCARD ALL, which "cannot be executed inside a transaction
  214. # block". This is important to test here because in materialize it
  215. # calls end_transaction, so we need to ensure it has correct transaction
  216. # semantics.
  217. # Should fail within an implicit transaction.
  218. send
  219. Query {"query": "SELECT 1; DISCARD ALL;"}
  220. ----
  221. until err_field_typs=C
  222. ReadyForQuery
  223. ----
  224. RowDescription {"fields":[{"name":"?column?"}]}
  225. DataRow {"fields":["1"]}
  226. CommandComplete {"tag":"SELECT 1"}
  227. ErrorResponse {"fields":[{"typ":"C","value":"25001"}]}
  228. ReadyForQuery {"status":"I"}
  229. # Should fail within an explicit transaction.
  230. send
  231. Query {"query": "BEGIN"}
  232. Query {"query": "DISCARD ALL"}
  233. Query {"query": "ROLLBACK"}
  234. ----
  235. until err_field_typs=C
  236. ReadyForQuery
  237. ReadyForQuery
  238. ReadyForQuery
  239. ----
  240. CommandComplete {"tag":"BEGIN"}
  241. ReadyForQuery {"status":"T"}
  242. ErrorResponse {"fields":[{"typ":"C","value":"25001"}]}
  243. ReadyForQuery {"status":"E"}
  244. CommandComplete {"tag":"ROLLBACK"}
  245. ReadyForQuery {"status":"I"}
  246. # Should succeed as a single statement.
  247. send
  248. Query {"query": "DISCARD ALL"}
  249. ----
  250. until
  251. ReadyForQuery
  252. ----
  253. CommandComplete {"tag":"DISCARD ALL"}
  254. ReadyForQuery {"status":"I"}
  255. # Should (apparently?) succeed as a second statement in an extended
  256. # session. I expected this to fail but I guess postgres allows it.
  257. send
  258. Parse {"query": "SELECT 1"}
  259. Bind
  260. Execute
  261. Parse {"query": "DISCARD ALL"}
  262. Bind
  263. Execute
  264. Sync
  265. ----
  266. until
  267. ReadyForQuery
  268. ----
  269. ParseComplete
  270. BindComplete
  271. DataRow {"fields":["1"]}
  272. CommandComplete {"tag":"SELECT 1"}
  273. ParseComplete
  274. BindComplete
  275. CommandComplete {"tag":"DISCARD ALL"}
  276. ReadyForQuery {"status":"I"}
  277. # Verify DISCARD ALL fails in explicit transaction during extended mode.
  278. send
  279. Parse {"query": "BEGIN"}
  280. Bind
  281. Execute
  282. Parse {"query": "DISCARD ALL"}
  283. Bind
  284. Execute
  285. Sync
  286. Query {"query": "ROLLBACK"}
  287. ----
  288. until err_field_typs=C
  289. ReadyForQuery
  290. ReadyForQuery
  291. ----
  292. ParseComplete
  293. BindComplete
  294. CommandComplete {"tag":"BEGIN"}
  295. ParseComplete
  296. BindComplete
  297. ErrorResponse {"fields":[{"typ":"C","value":"25001"}]}
  298. ReadyForQuery {"status":"E"}
  299. CommandComplete {"tag":"ROLLBACK"}
  300. ReadyForQuery {"status":"I"}
  301. # Test Sync end transaciton behavior:
  302. # "At completion of each series of extended-query messages, the frontend
  303. # should issue a Sync message. This parameterless message causes
  304. # the backend to close the current transaction if it's not inside a
  305. # BEGIN/COMMIT transaction block (“close” meaning to commit if no
  306. # error, or roll back if error)."
  307. # An error in extended protocol ignores other statements until Sync,
  308. # even if it's a ROLLBACK.
  309. send
  310. Parse {"query": "SELECT 1"}
  311. Bind
  312. Execute
  313. Parse {"query": "SELECT 1/(SELECT 0)"}
  314. Bind
  315. Execute
  316. Parse {"query": "ROLLBACK"}
  317. Bind
  318. Execute
  319. Parse {"query": "SELECT 2"}
  320. Bind
  321. Execute
  322. Sync
  323. ----
  324. until err_field_typs=M
  325. ReadyForQuery
  326. ----
  327. ParseComplete
  328. BindComplete
  329. DataRow {"fields":["1"]}
  330. CommandComplete {"tag":"SELECT 1"}
  331. ParseComplete
  332. BindComplete
  333. ErrorResponse {"fields":[{"typ":"M","value":"division by zero"}]}
  334. ReadyForQuery {"status":"I"}
  335. # A ROLLBACK must be the first message in a new extended session.
  336. send
  337. Query {"query": "BEGIN"}
  338. Parse {"query": "SELECT 1"}
  339. Bind
  340. Execute
  341. Parse {"query": "SELECT 1/(SELECT 0)"}
  342. Bind
  343. Execute
  344. Parse {"query": "ROLLBACK"}
  345. Bind
  346. Execute
  347. Parse {"query": "SELECT 2"}
  348. Bind
  349. Execute
  350. Sync
  351. Parse {"query": "SELECT 3"}
  352. Bind
  353. Execute
  354. Parse {"query": "ROLLBACK"}
  355. Bind
  356. Execute
  357. Sync
  358. Parse {"query": "ROLLBACK"}
  359. Bind
  360. Execute
  361. Parse {"query": "SELECT 4"}
  362. Bind
  363. Execute
  364. Sync
  365. ----
  366. until err_field_typs=M
  367. ReadyForQuery
  368. ReadyForQuery
  369. ReadyForQuery
  370. ReadyForQuery
  371. ----
  372. CommandComplete {"tag":"BEGIN"}
  373. ReadyForQuery {"status":"T"}
  374. ParseComplete
  375. BindComplete
  376. DataRow {"fields":["1"]}
  377. CommandComplete {"tag":"SELECT 1"}
  378. ParseComplete
  379. BindComplete
  380. ErrorResponse {"fields":[{"typ":"M","value":"division by zero"}]}
  381. ReadyForQuery {"status":"E"}
  382. ErrorResponse {"fields":[{"typ":"M","value":"current transaction is aborted, commands ignored until end of transaction block"}]}
  383. ReadyForQuery {"status":"E"}
  384. ParseComplete
  385. BindComplete
  386. CommandComplete {"tag":"ROLLBACK"}
  387. ParseComplete
  388. BindComplete
  389. DataRow {"fields":["4"]}
  390. CommandComplete {"tag":"SELECT 1"}
  391. ReadyForQuery {"status":"I"}
  392. # Verify there are no missed messages.
  393. send
  394. Query {"query": "SELECT 45"}
  395. ----
  396. until
  397. ReadyForQuery
  398. ----
  399. RowDescription {"fields":[{"name":"?column?"}]}
  400. DataRow {"fields":["45"]}
  401. CommandComplete {"tag":"SELECT 1"}
  402. ReadyForQuery {"status":"I"}
  403. # Verify a failed transaction should return rollback if commit is issued.
  404. send
  405. Query {"query": "BEGIN; SELECT 0; COMMIT"}
  406. Query {"query": "BEGIN; SELECT 0/0;"}
  407. Query {"query": "COMMIT"}
  408. Query {"query": "SELECT 1"}
  409. ----
  410. until err_field_typs=M ignore=RowDescription
  411. ReadyForQuery
  412. ReadyForQuery
  413. ReadyForQuery
  414. ReadyForQuery
  415. ----
  416. CommandComplete {"tag":"BEGIN"}
  417. DataRow {"fields":["0"]}
  418. CommandComplete {"tag":"SELECT 1"}
  419. CommandComplete {"tag":"COMMIT"}
  420. ReadyForQuery {"status":"I"}
  421. CommandComplete {"tag":"BEGIN"}
  422. ErrorResponse {"fields":[{"typ":"M","value":"division by zero"}]}
  423. ReadyForQuery {"status":"E"}
  424. CommandComplete {"tag":"ROLLBACK"}
  425. ReadyForQuery {"status":"I"}
  426. DataRow {"fields":["1"]}
  427. CommandComplete {"tag":"SELECT 1"}
  428. ReadyForQuery {"status":"I"}
  429. # Verify implicit transactions are properly upgraded
  430. send
  431. Query {"query": "CREATE TABLE t (a INT)"}
  432. Query {"query": "INSERT INTO t VALUES (1); BEGIN; COMMIT;"}
  433. Parse {"query": "SELECT a FROM t"}
  434. Bind
  435. Execute
  436. Sync
  437. ----
  438. until err_field_typs=M ignore=RowDescription
  439. ReadyForQuery
  440. ReadyForQuery
  441. ReadyForQuery
  442. ----
  443. CommandComplete {"tag":"CREATE TABLE"}
  444. ReadyForQuery {"status":"I"}
  445. CommandComplete {"tag":"INSERT 0 1"}
  446. CommandComplete {"tag":"BEGIN"}
  447. CommandComplete {"tag":"COMMIT"}
  448. ReadyForQuery {"status":"I"}
  449. ParseComplete
  450. BindComplete
  451. DataRow {"fields":["1"]}
  452. CommandComplete {"tag":"SELECT 1"}
  453. ReadyForQuery {"status":"I"}
  454. # Verify implicit transactions are properly upgraded, and share fate
  455. # with upgraded txn; the insert of 2 should get discarded
  456. send
  457. Query {"query": "INSERT INTO t VALUES (2); BEGIN; SELECT 0/0;"}
  458. Parse {"query": "COMMIT"}
  459. Bind
  460. Execute
  461. Sync
  462. Parse {"query": "SELECT a FROM t"}
  463. Bind
  464. Execute
  465. Sync
  466. ----
  467. until err_field_typs=M ignore=RowDescription
  468. ReadyForQuery
  469. ReadyForQuery
  470. ReadyForQuery
  471. ----
  472. CommandComplete {"tag":"INSERT 0 1"}
  473. CommandComplete {"tag":"BEGIN"}
  474. ErrorResponse {"fields":[{"typ":"M","value":"division by zero"}]}
  475. ReadyForQuery {"status":"E"}
  476. ParseComplete
  477. BindComplete
  478. CommandComplete {"tag":"ROLLBACK"}
  479. ReadyForQuery {"status":"I"}
  480. ParseComplete
  481. BindComplete
  482. DataRow {"fields":["1"]}
  483. CommandComplete {"tag":"SELECT 1"}
  484. ReadyForQuery {"status":"I"}
  485. # PG permits starting a read only txn with writes blended into its ops;
  486. # evidence of this behavior by an errored txn does not commit write.
  487. send
  488. Query {"query": "INSERT INTO t VALUES (2); BEGIN READ ONLY; SELECT 0/0;"}
  489. Parse {"query": "COMMIT"}
  490. Bind
  491. Execute
  492. Sync
  493. Parse {"query": "SELECT a FROM t"}
  494. Bind
  495. Execute
  496. Sync
  497. ----
  498. until err_field_typs=M ignore=RowDescription
  499. ReadyForQuery
  500. ReadyForQuery
  501. ReadyForQuery
  502. ----
  503. CommandComplete {"tag":"INSERT 0 1"}
  504. CommandComplete {"tag":"BEGIN"}
  505. ErrorResponse {"fields":[{"typ":"M","value":"division by zero"}]}
  506. ReadyForQuery {"status":"E"}
  507. ParseComplete
  508. BindComplete
  509. CommandComplete {"tag":"ROLLBACK"}
  510. ReadyForQuery {"status":"I"}
  511. ParseComplete
  512. BindComplete
  513. DataRow {"fields":["1"]}
  514. CommandComplete {"tag":"SELECT 1"}
  515. ReadyForQuery {"status":"I"}
  516. # PG permits commits writes that are part of read-only txns
  517. # n.b. this is possible only because we support selecting constants
  518. # in write-only txns
  519. send
  520. Parse {"query": "INSERT INTO t VALUES (2)"}
  521. Bind
  522. Execute
  523. Parse {"query": "BEGIN READ ONLY"}
  524. Bind
  525. Execute
  526. Parse {"query": "SELECT 1"}
  527. Bind
  528. Execute
  529. Parse {"query": "COMMIT"}
  530. Bind
  531. Execute
  532. Sync
  533. Parse {"query": "SELECT a FROM t"}
  534. Bind
  535. Execute
  536. Sync
  537. ----
  538. until err_field_typs=M ignore=RowDescription
  539. ReadyForQuery
  540. ReadyForQuery
  541. ----
  542. ParseComplete
  543. BindComplete
  544. CommandComplete {"tag":"INSERT 0 1"}
  545. ParseComplete
  546. BindComplete
  547. CommandComplete {"tag":"BEGIN"}
  548. ParseComplete
  549. BindComplete
  550. DataRow {"fields":["1"]}
  551. CommandComplete {"tag":"SELECT 1"}
  552. ParseComplete
  553. BindComplete
  554. CommandComplete {"tag":"COMMIT"}
  555. ReadyForQuery {"status":"I"}
  556. ParseComplete
  557. BindComplete
  558. DataRow {"fields":["1"]}
  559. DataRow {"fields":["2"]}
  560. CommandComplete {"tag":"SELECT 2"}
  561. ReadyForQuery {"status":"I"}