cursors.pt 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. # Verify we can pgwire Bind, Execute, then SQL FETCH and CLOSE, then
  2. # pgwire Execute fails (due to the close). This tests that pgwire portals
  3. # and SQL cursors are the same thing.
  4. send
  5. Query {"query": "BEGIN"}
  6. Parse {"name": "s", "query": "VALUES (2), (4), (6)"}
  7. Bind {"portal": "p", "statement": "s"}
  8. Execute {"portal": "p", "max_rows": 1}
  9. Sync
  10. Query {"query": "FETCH 1 p"}
  11. Query {"query": "CLOSE \"p\""}
  12. Execute {"portal": "p", "max_rows": 1}
  13. Sync
  14. Query {"query": "ROLLBACK"}
  15. ----
  16. until
  17. ReadyForQuery
  18. ReadyForQuery
  19. ReadyForQuery
  20. ReadyForQuery
  21. ReadyForQuery
  22. ReadyForQuery
  23. ----
  24. CommandComplete {"tag":"BEGIN"}
  25. ReadyForQuery {"status":"T"}
  26. ParseComplete
  27. BindComplete
  28. DataRow {"fields":["2"]}
  29. PortalSuspended
  30. ReadyForQuery {"status":"T"}
  31. RowDescription {"fields":[{"name":"column1"}]}
  32. DataRow {"fields":["4"]}
  33. CommandComplete {"tag":"FETCH 1"}
  34. ReadyForQuery {"status":"T"}
  35. CommandComplete {"tag":"CLOSE CURSOR"}
  36. ReadyForQuery {"status":"T"}
  37. ErrorResponse {"fields":[{"typ":"S","value":"ERROR"},{"typ":"C","value":"34000"},{"typ":"M","value":"portal \"p\" does not exist"}]}
  38. ReadyForQuery {"status":"E"}
  39. CommandComplete {"tag":"ROLLBACK"}
  40. ReadyForQuery {"status":"I"}
  41. # Ensure FETCH with no count returns 1 row.
  42. send
  43. Query {"query": "BEGIN"}
  44. Query {"query": "DECLARE c CURSOR FOR VALUES (2), (4), (6)"}
  45. Query {"query": "FETCH c"}
  46. Query {"query": "CLOSE c"}
  47. Query {"query": "COMMIT"}
  48. ----
  49. until
  50. ReadyForQuery
  51. ReadyForQuery
  52. ReadyForQuery
  53. ReadyForQuery
  54. ReadyForQuery
  55. ----
  56. CommandComplete {"tag":"BEGIN"}
  57. ReadyForQuery {"status":"T"}
  58. CommandComplete {"tag":"DECLARE CURSOR"}
  59. ReadyForQuery {"status":"T"}
  60. RowDescription {"fields":[{"name":"column1"}]}
  61. DataRow {"fields":["2"]}
  62. CommandComplete {"tag":"FETCH 1"}
  63. ReadyForQuery {"status":"T"}
  64. CommandComplete {"tag":"CLOSE CURSOR"}
  65. ReadyForQuery {"status":"T"}
  66. CommandComplete {"tag":"COMMIT"}
  67. ReadyForQuery {"status":"I"}
  68. # FETCH with a high count.
  69. send
  70. Query {"query": "BEGIN"}
  71. Query {"query": "DECLARE c CURSOR FOR VALUES (2), (4), (6)"}
  72. Query {"query": "FETCH 2000 c"}
  73. Query {"query": "CLOSE c"}
  74. Query {"query": "COMMIT"}
  75. ----
  76. until
  77. ReadyForQuery
  78. ReadyForQuery
  79. ReadyForQuery
  80. ReadyForQuery
  81. ReadyForQuery
  82. ----
  83. CommandComplete {"tag":"BEGIN"}
  84. ReadyForQuery {"status":"T"}
  85. CommandComplete {"tag":"DECLARE CURSOR"}
  86. ReadyForQuery {"status":"T"}
  87. RowDescription {"fields":[{"name":"column1"}]}
  88. DataRow {"fields":["2"]}
  89. DataRow {"fields":["4"]}
  90. DataRow {"fields":["6"]}
  91. CommandComplete {"tag":"FETCH 3"}
  92. ReadyForQuery {"status":"T"}
  93. CommandComplete {"tag":"CLOSE CURSOR"}
  94. ReadyForQuery {"status":"T"}
  95. CommandComplete {"tag":"COMMIT"}
  96. ReadyForQuery {"status":"I"}
  97. # DECLARE outside a transaction should fail. A test like:
  98. # Query {"query": "DECLARE c CURSOR FOR VALUES (2), (4), (6)"}
  99. # would trigger this but we have slightly different transaction semantics
  100. # than Postgres that would require special casing this situation, and
  101. # it's safe to ignore, so we don't implement or test it.
  102. # Surprisingly, the same statement but with a SELECT added on passes. This
  103. # is because Postgres has some different transaction states. For a Query
  104. # message containing a single statement, it uses the DEFAULT state. If
  105. # there are multiple statements it uses INPROGRESS_IMPLICIT, which acts
  106. # similar to a BEGIN. The SELECT here triggers that, which then causes
  107. # DECLARE to error. Since we have fewer transaction states our DECLARE
  108. # doesn't detect this for the single statement case.
  109. send
  110. Query {"query": "DECLARE c CURSOR FOR VALUES (2), (4), (6); SELECT 1"}
  111. ----
  112. until
  113. ReadyForQuery
  114. ----
  115. CommandComplete {"tag":"DECLARE CURSOR"}
  116. RowDescription {"fields":[{"name":"?column?"}]}
  117. DataRow {"fields":["1"]}
  118. CommandComplete {"tag":"SELECT 1"}
  119. ReadyForQuery {"status":"I"}
  120. send
  121. Query {"query": "CLOSE c"}
  122. ----
  123. until err_field_typs=M
  124. ReadyForQuery
  125. ----
  126. ErrorResponse {"fields":[{"typ":"M","value":"cursor \"c\" does not exist"}]}
  127. ReadyForQuery {"status":"I"}
  128. send
  129. Query {"query": "FETCH c"}
  130. ----
  131. until err_field_typs=M
  132. ReadyForQuery
  133. ----
  134. ErrorResponse {"fields":[{"typ":"M","value":"cursor \"c\" does not exist"}]}
  135. ReadyForQuery {"status":"I"}
  136. # Verify that cursor and portal close messages differ.
  137. send
  138. Execute {"portal": "c"}
  139. Sync
  140. ----
  141. until err_field_typs=M
  142. ReadyForQuery
  143. ----
  144. ErrorResponse {"fields":[{"typ":"M","value":"portal \"c\" does not exist"}]}
  145. ReadyForQuery {"status":"I"}
  146. # Verify that Describe will fail before and after a transaction, but work within one.
  147. send
  148. Describe {"variant": "P", "name": "c"}
  149. Sync
  150. Query {"query": "BEGIN"}
  151. Query {"query": "DECLARE c CURSOR FOR VALUES (2), (4), (6)"}
  152. Describe {"variant": "P", "name": "c"}
  153. Query {"query": "COMMIT"}
  154. Describe {"variant": "P", "name": "c"}
  155. Sync
  156. ----
  157. until
  158. ErrorResponse
  159. ReadyForQuery
  160. ReadyForQuery
  161. ReadyForQuery
  162. ReadyForQuery
  163. ReadyForQuery
  164. ----
  165. ErrorResponse {"fields":[{"typ":"S","value":"ERROR"},{"typ":"C","value":"34000"},{"typ":"M","value":"portal \"c\" does not exist"}]}
  166. ReadyForQuery {"status":"I"}
  167. CommandComplete {"tag":"BEGIN"}
  168. ReadyForQuery {"status":"T"}
  169. CommandComplete {"tag":"DECLARE CURSOR"}
  170. ReadyForQuery {"status":"T"}
  171. RowDescription {"fields":[{"name":"column1"}]}
  172. CommandComplete {"tag":"COMMIT"}
  173. ReadyForQuery {"status":"I"}
  174. ErrorResponse {"fields":[{"typ":"S","value":"ERROR"},{"typ":"C","value":"34000"},{"typ":"M","value":"portal \"c\" does not exist"}]}
  175. ReadyForQuery {"status":"I"}
  176. # Verify that a single Query message can declare and fetch from a
  177. # portal. This tests that, even though DECLARE cannot be used outside
  178. # of a transaction, a single Query message with multiple statements is
  179. # a transaction. Also verify that it doesn't exist afterward.
  180. send
  181. Query {"query": "DECLARE c CURSOR FOR VALUES (2), (4), (6); FETCH 2 c; FETCH 10 c;"}
  182. Describe {"variant": "P", "name": "c"}
  183. Sync
  184. ----
  185. until
  186. ReadyForQuery
  187. ReadyForQuery
  188. ----
  189. CommandComplete {"tag":"DECLARE CURSOR"}
  190. RowDescription {"fields":[{"name":"column1"}]}
  191. DataRow {"fields":["2"]}
  192. DataRow {"fields":["4"]}
  193. CommandComplete {"tag":"FETCH 2"}
  194. RowDescription {"fields":[{"name":"column1"}]}
  195. DataRow {"fields":["6"]}
  196. CommandComplete {"tag":"FETCH 1"}
  197. ReadyForQuery {"status":"I"}
  198. ErrorResponse {"fields":[{"typ":"S","value":"ERROR"},{"typ":"C","value":"34000"},{"typ":"M","value":"portal \"c\" does not exist"}]}
  199. ReadyForQuery {"status":"I"}
  200. # Test cursors in extended protocol.
  201. send
  202. Query {"query": "BEGIN"}
  203. Parse {"query": "DECLARE c CURSOR FOR VALUES (1), (2), (3)"}
  204. Describe
  205. Bind
  206. Execute
  207. Parse {"query": "FETCH c"}
  208. Describe
  209. Bind
  210. Execute
  211. Execute {"portal": "c"}
  212. Parse {"query": "CLOSE c"}
  213. Describe
  214. Bind
  215. Execute
  216. Sync
  217. Query {"query": "COMMIT"}
  218. ----
  219. until
  220. ReadyForQuery
  221. ReadyForQuery
  222. ReadyForQuery
  223. ----
  224. CommandComplete {"tag":"BEGIN"}
  225. ReadyForQuery {"status":"T"}
  226. ParseComplete
  227. ParameterDescription {"parameters":[]}
  228. NoData
  229. BindComplete
  230. CommandComplete {"tag":"DECLARE CURSOR"}
  231. ParseComplete
  232. ParameterDescription {"parameters":[]}
  233. RowDescription {"fields":[{"name":"column1"}]}
  234. BindComplete
  235. DataRow {"fields":["1"]}
  236. CommandComplete {"tag":"FETCH 1"}
  237. DataRow {"fields":["2"]}
  238. DataRow {"fields":["3"]}
  239. CommandComplete {"tag":"SELECT 2"}
  240. ParseComplete
  241. ParameterDescription {"parameters":[]}
  242. NoData
  243. BindComplete
  244. CommandComplete {"tag":"CLOSE CURSOR"}
  245. ReadyForQuery {"status":"T"}
  246. CommandComplete {"tag":"COMMIT"}
  247. ReadyForQuery {"status":"I"}
  248. # Here's a tricky one. Create a portal ("c") with 6 rows in it. Create
  249. # and bind another portal ("a") that fetches 2 rows from "c", but don't
  250. # execute "a" at all. Execute the empty portal pulling 2 rows from c
  251. # (1, 2). Execute portal "a" but only request 1 row (3). At this point
  252. # "a" has also cached row 4 because it's a 2 row FETCH. Row 4 is in some
  253. # cache somewhere, and no longer in portal "c". Verify this by pulling
  254. # 2 more rows from "c" (5, 6). Finally pull the remaining rows from "a"
  255. # (4). This test verifies that the first execution of a FETCH will pull
  256. # N rows from its target portal and cache them.
  257. # NOTE: We differ from postgres here so don't actually test this. The
  258. # test and comment are left here so future readers can understand what
  259. # should be happening if we were fully compliant.
  260. #send
  261. #Query {"query": "BEGIN"}
  262. #Query {"query": "DECLARE c CURSOR FOR VALUES (1), (2), (3), (4), (5), (6)"}
  263. #Parse {"query": "FETCH 2 c"}
  264. #Bind {"portal": "a"}
  265. #Sync
  266. #Query {"query": "FETCH 2 c"}
  267. #Execute {"portal": "a", "max_rows": 1}
  268. #Sync
  269. #Query {"query": "FETCH 2 c"}
  270. #Execute {"portal": "a"}
  271. #Sync
  272. #Query {"query": "FETCH 2 c"}
  273. #Query {"query": "COMMIT"}
  274. #----
  275. #
  276. #until
  277. #ReadyForQuery
  278. #ReadyForQuery
  279. #ReadyForQuery
  280. #ReadyForQuery
  281. #ReadyForQuery
  282. #ReadyForQuery
  283. #ReadyForQuery
  284. #ReadyForQuery
  285. #ReadyForQuery
  286. #----
  287. #CommandComplete {"tag":"BEGIN"}
  288. #ReadyForQuery {"status":"T"}
  289. #CommandComplete {"tag":"DECLARE CURSOR"}
  290. #ReadyForQuery {"status":"T"}
  291. #ParseComplete
  292. #BindComplete
  293. #ReadyForQuery {"status":"T"}
  294. #RowDescription {"fields":[{"name":"column1"}]}
  295. #DataRow {"fields":["1"]}
  296. #DataRow {"fields":["2"]}
  297. #CommandComplete {"tag":"FETCH 2"}
  298. #ReadyForQuery {"status":"T"}
  299. #DataRow {"fields":["3"]}
  300. #PortalSuspended
  301. #ReadyForQuery {"status":"T"}
  302. #RowDescription {"fields":[{"name":"column1"}]}
  303. #DataRow {"fields":["5"]}
  304. #DataRow {"fields":["6"]}
  305. #CommandComplete {"tag":"FETCH 2"}
  306. #ReadyForQuery {"status":"T"}
  307. #DataRow {"fields":["4"]}
  308. #CommandComplete {"tag":"FETCH 2"}
  309. #ReadyForQuery {"status":"T"}
  310. #RowDescription {"fields":[{"name":"column1"}]}
  311. #CommandComplete {"tag":"FETCH 0"}
  312. #ReadyForQuery {"status":"T"}
  313. #CommandComplete {"tag":"COMMIT"}
  314. #ReadyForQuery {"status":"I"}
  315. # Test FETCH 0. This should return 0 rows (not all remaining, like Execute).
  316. send
  317. Query {"query": "BEGIN"}
  318. Query {"query": "DECLARE c CURSOR FOR VALUES (1), (2), (3), (4), (5), (6)"}
  319. Query {"query": "FETCH 0 c"}
  320. Query {"query": "COMMIT"}
  321. ----
  322. until
  323. ReadyForQuery
  324. ReadyForQuery
  325. ReadyForQuery
  326. ReadyForQuery
  327. ----
  328. CommandComplete {"tag":"BEGIN"}
  329. ReadyForQuery {"status":"T"}
  330. CommandComplete {"tag":"DECLARE CURSOR"}
  331. ReadyForQuery {"status":"T"}
  332. RowDescription {"fields":[{"name":"column1"}]}
  333. CommandComplete {"tag":"FETCH 0"}
  334. ReadyForQuery {"status":"T"}
  335. CommandComplete {"tag":"COMMIT"}
  336. ReadyForQuery {"status":"I"}
  337. # Executing a DECLARE twice fails.
  338. send
  339. Query {"query": "BEGIN"}
  340. Parse {"query": "DECLARE c CURSOR FOR VALUES (1), (2), (3)"}
  341. Bind
  342. Execute
  343. Execute
  344. Sync
  345. Query {"query": "ROLLBACK"}
  346. ----
  347. until
  348. ReadyForQuery
  349. ReadyForQuery
  350. ReadyForQuery
  351. ----
  352. CommandComplete {"tag":"BEGIN"}
  353. ReadyForQuery {"status":"T"}
  354. ParseComplete
  355. BindComplete
  356. CommandComplete {"tag":"DECLARE CURSOR"}
  357. ErrorResponse {"fields":[{"typ":"S","value":"ERROR"},{"typ":"C","value":"55000"},{"typ":"M","value":"portal \"\" cannot be run"}]}
  358. ReadyForQuery {"status":"E"}
  359. CommandComplete {"tag":"ROLLBACK"}
  360. ReadyForQuery {"status":"I"}
  361. # Executing a FETCH twice does nothing the second time. It must be
  362. # rebound to get more rows.
  363. # Executing a CLOSE twice fails like DECLARE.
  364. send
  365. Query {"query": "BEGIN"}
  366. Parse {"query": "DECLARE c CURSOR FOR VALUES (1), (2), (3)"}
  367. Bind
  368. Execute
  369. Parse {"query": "FETCH c"}
  370. Bind
  371. Execute
  372. Execute
  373. Bind
  374. Execute
  375. Execute
  376. Parse {"query": "CLOSE c"}
  377. Bind
  378. Execute
  379. Execute
  380. Sync
  381. Query {"query": "ROLLBACK"}
  382. ----
  383. until
  384. ReadyForQuery
  385. ReadyForQuery
  386. ReadyForQuery
  387. ----
  388. CommandComplete {"tag":"BEGIN"}
  389. ReadyForQuery {"status":"T"}
  390. ParseComplete
  391. BindComplete
  392. CommandComplete {"tag":"DECLARE CURSOR"}
  393. ParseComplete
  394. BindComplete
  395. DataRow {"fields":["1"]}
  396. CommandComplete {"tag":"FETCH 1"}
  397. CommandComplete {"tag":"FETCH 1"}
  398. BindComplete
  399. DataRow {"fields":["2"]}
  400. CommandComplete {"tag":"FETCH 1"}
  401. CommandComplete {"tag":"FETCH 1"}
  402. ParseComplete
  403. BindComplete
  404. CommandComplete {"tag":"CLOSE CURSOR"}
  405. ErrorResponse {"fields":[{"typ":"S","value":"ERROR"},{"typ":"C","value":"55000"},{"typ":"M","value":"portal \"\" cannot be run"}]}
  406. ReadyForQuery {"status":"E"}
  407. CommandComplete {"tag":"ROLLBACK"}
  408. ReadyForQuery {"status":"I"}
  409. # Test FETCH with various combinations of row and execute counts. And
  410. # recall that since we don't support Execute with max_rows < FETCH's
  411. # count, we aren't testing that here, but should be if we add it.
  412. send
  413. Query {"query": "BEGIN"}
  414. Parse {"query": "DECLARE c CURSOR FOR VALUES (1), (2), (3), (4), (5), (6), (7), (8)"}
  415. Bind
  416. Execute
  417. Parse {"query": "FETCH 2 c"}
  418. Bind
  419. Execute
  420. Execute
  421. Bind
  422. Execute {"max_rows": 4}
  423. Execute {"max_rows": 4}
  424. Execute
  425. Parse {"query": "FETCH c"}
  426. Bind
  427. Execute {"max_rows": 2}
  428. Execute {"max_rows": 2}
  429. Sync
  430. Query {"query": "COMMIT"}
  431. ----
  432. until
  433. ReadyForQuery
  434. ReadyForQuery
  435. ReadyForQuery
  436. ----
  437. CommandComplete {"tag":"BEGIN"}
  438. ReadyForQuery {"status":"T"}
  439. ParseComplete
  440. BindComplete
  441. CommandComplete {"tag":"DECLARE CURSOR"}
  442. ParseComplete
  443. BindComplete
  444. DataRow {"fields":["1"]}
  445. DataRow {"fields":["2"]}
  446. CommandComplete {"tag":"FETCH 2"}
  447. CommandComplete {"tag":"FETCH 2"}
  448. BindComplete
  449. DataRow {"fields":["3"]}
  450. DataRow {"fields":["4"]}
  451. CommandComplete {"tag":"FETCH 2"}
  452. CommandComplete {"tag":"FETCH 2"}
  453. CommandComplete {"tag":"FETCH 2"}
  454. ParseComplete
  455. BindComplete
  456. DataRow {"fields":["5"]}
  457. CommandComplete {"tag":"FETCH 1"}
  458. CommandComplete {"tag":"FETCH 1"}
  459. ReadyForQuery {"status":"T"}
  460. CommandComplete {"tag":"COMMIT"}
  461. ReadyForQuery {"status":"I"}
  462. # Verify that the empty portal is removed after Query.
  463. send
  464. Query {"query": "BEGIN; DECLARE c CURSOR FOR VALUES (1), (2); FETCH c;"}
  465. Execute
  466. Sync
  467. Query {"query": "ROLLBACK"}
  468. ----
  469. until
  470. ReadyForQuery
  471. ReadyForQuery
  472. ReadyForQuery
  473. ----
  474. CommandComplete {"tag":"BEGIN"}
  475. CommandComplete {"tag":"DECLARE CURSOR"}
  476. RowDescription {"fields":[{"name":"column1"}]}
  477. DataRow {"fields":["1"]}
  478. CommandComplete {"tag":"FETCH 1"}
  479. ReadyForQuery {"status":"T"}
  480. ErrorResponse {"fields":[{"typ":"S","value":"ERROR"},{"typ":"C","value":"34000"},{"typ":"M","value":"portal \"\" does not exist"}]}
  481. ReadyForQuery {"status":"E"}
  482. CommandComplete {"tag":"ROLLBACK"}
  483. ReadyForQuery {"status":"I"}
  484. # Test binary cursors. This tests that a cursor declared over simple query
  485. # (i.e., text result format) can be requested in binary.
  486. send
  487. Query {"query": "BEGIN; DECLARE c CURSOR FOR VALUES (1), (2);"}
  488. Parse {"query": "FETCH c"}
  489. Bind {"result_formats": [1]}
  490. Execute
  491. Sync
  492. Query {"query": "COMMIT"}
  493. ----
  494. until
  495. ReadyForQuery
  496. ReadyForQuery
  497. ReadyForQuery
  498. ----
  499. CommandComplete {"tag":"BEGIN"}
  500. CommandComplete {"tag":"DECLARE CURSOR"}
  501. ReadyForQuery {"status":"T"}
  502. ParseComplete
  503. BindComplete
  504. DataRow {"fields":["\u0000\u0000\u0000\u0001"]}
  505. CommandComplete {"tag":"FETCH 1"}
  506. ReadyForQuery {"status":"T"}
  507. CommandComplete {"tag":"COMMIT"}
  508. ReadyForQuery {"status":"I"}
  509. # Verify that a complaint is issued if DECLARE is not in an implicit or
  510. # explicit transaction.
  511. send
  512. Query {"query": "DECLARE c CURSOR FOR SELECT 1"}
  513. ----
  514. until
  515. ReadyForQuery
  516. ----
  517. ErrorResponse {"fields":[{"typ":"S","value":"ERROR"},{"typ":"C","value":"25P01"},{"typ":"M","value":"DECLARE CURSOR can only be used in transaction blocks"}]}
  518. ReadyForQuery {"status":"I"}
  519. send
  520. Parse {"query": "DECLARE c CURSOR FOR SELECT 1"}
  521. Bind
  522. Execute
  523. Sync
  524. ----
  525. until
  526. ReadyForQuery
  527. ----
  528. ParseComplete
  529. BindComplete
  530. ErrorResponse {"fields":[{"typ":"S","value":"ERROR"},{"typ":"C","value":"25P01"},{"typ":"M","value":"DECLARE CURSOR can only be used in transaction blocks"}]}
  531. ReadyForQuery {"status":"I"}