cursorLifecycleFlow covers DECLARE → FETCH n → FETCH n (position advances) → FETCH ALL (drains the rest) → FETCH on exhausted cursor (zero rows) → CLOSE → FETCH after CLOSE (clean missing-cursor error).
(t *testing.T, db *sql.DB)
| 106 | // FETCH ALL (drains the rest) → FETCH on exhausted cursor (zero rows) → |
| 107 | // CLOSE → FETCH after CLOSE (clean missing-cursor error). |
| 108 | func cursorLifecycleFlow(t *testing.T, db *sql.DB) { |
| 109 | tx := beginCursorTx(t, db) |
| 110 | |
| 111 | mustExecTx(t, tx, "DECLARE cur_lifecycle CURSOR FOR SELECT id, name FROM users ORDER BY id") |
| 112 | |
| 113 | assertCursorRows(t, "FETCH 3", fetchCursorRows(t, tx, "FETCH 3 FROM cur_lifecycle"), cursorUsers(1, 3)) |
| 114 | assertCursorRows(t, "FETCH 3 (resumes)", fetchCursorRows(t, tx, "FETCH 3 FROM cur_lifecycle"), cursorUsers(4, 6)) |
| 115 | assertCursorRows(t, "FETCH ALL", fetchCursorRows(t, tx, "FETCH ALL FROM cur_lifecycle"), cursorUsers(7, 10)) |
| 116 | // An exhausted cursor returns zero rows, not an error. |
| 117 | assertCursorRows(t, "FETCH 3 (exhausted)", fetchCursorRows(t, tx, "FETCH 3 FROM cur_lifecycle"), nil) |
| 118 | |
| 119 | mustExecTx(t, tx, "CLOSE cur_lifecycle") |
| 120 | |
| 121 | rows, err := tx.Query("FETCH 3 FROM cur_lifecycle") |
| 122 | if err == nil { |
| 123 | _ = rows.Close() |
| 124 | t.Fatal("FETCH after CLOSE succeeded, want missing-cursor error") |
| 125 | } |
| 126 | if !strings.Contains(err.Error(), `cursor "cur_lifecycle" does not exist`) { |
| 127 | t.Errorf("FETCH after CLOSE: error = %q, want it to mention the missing cursor", err) |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | // cursorMoveFlow covers MOVE n advancing the position without returning rows. |
| 132 | func cursorMoveFlow(t *testing.T, db *sql.DB) { |
no test coverage detected