17 | --------------------------------------------------------------------------------
18 | -- Decode Rows
19 | --------------------------------------------------------------------------------
21 | ||| Decode a list of raw Oracle rows into a list of typed values.
22 | |||
23 | ||| Each raw row returned from Oracle is passed through the `FromRow` instance for the requested type.
24 | |||
25 | ||| Decoding proceeds from the first row to the last row.
26 | |||
27 | ||| If any row fails to decode, decoding stops immediately and the first `OracleError` is returned.
28 | |||
29 | ||| This function is used internally by `query_`, but is exported so callers can decode results obtained from `queryRaw` manually.
30 | |||
31 | ||| Example:
32 | |||
33 | ||| ```idris
34 | ||| decodeRows
35 | ||| [ [OracleInt 1, OracleString "Alice"]
36 | ||| , [OracleInt 2, OracleString "Bob"]
37 | ||| ]
38 | ||| ```
39 | |||
40 | export
44 | where
55 | --------------------------------------------------------------------------------
56 | -- Untyped Query
57 | --------------------------------------------------------------------------------
59 | ||| Execute a SQL query while automatically managing the prepared statement lifetime.
60 | |||
61 | ||| This is the raw query API.
62 | |||
63 | ||| The statement is:
64 | ||| 1. Prepared.
65 | ||| 2. Bound with parameters.
66 | ||| 3. Executed.
67 | ||| 4. Fetched.
68 | ||| 5. Released.
69 | |||
70 | ||| Returned rows contain raw Oracle values.
71 | |||
73 | queryRaw : Connection -> String -> List BindParameter -> IO (Either OracleError (List (List OracleValue)))
80 | --------------------------------------------------------------------------------
81 | -- Typed Query
82 | --------------------------------------------------------------------------------
84 | ||| Execute a query and decode every returned row.
85 | |||
86 | ||| The target type must provide a `FromRow` implementation describing how to convert `List OracleValue` into the target value.
87 | |||
88 | ||| Example:
89 | |||
90 | ||| ```idris
91 | ||| record Employee where
92 | ||| constructor MkEmployee
93 | ||| id : Int64
94 | ||| name : String
95 | |||
96 | ||| implementation FromRow Employee where
97 | ||| fromRow [OracleInt id, OracleString name] =
98 | ||| Right (MkEmployee id name)
99 | ||| fromRow _ =
100 | ||| Left invalidRow
101 | |||
102 | ||| employees <- query_ conn
103 | ||| "select id,name from employees"
104 | ||| []
105 | ||| ```
106 | |||
108 | query_ : FromRow a => Connection -> String -> List BindParameter -> IO (Either OracleError (List a))
117 | --------------------------------------------------------------------------------
118 | -- Single Row Query
119 | --------------------------------------------------------------------------------
121 | ||| Execute a query and decode at most a single row.
122 | |||
123 | ||| Returns:
124 | ||| - Left OracleError if execution fails.
125 | ||| - Right Nothing if no rows were returned.
126 | ||| - Right (Just value) for the first row.
127 | |||
128 | ||| If multiple rows are returned, only the first row is used and the remainder are ignored.
129 | |||
130 | ||| Example:
131 | |||
132 | ||| ```idris
133 | ||| employee <-
134 | ||| queryOne
135 | ||| conn
136 | ||| "select id,name
137 | ||| from employees
138 | ||| where id = :id"
139 | ||| [ MkBindParameter "id"
140 | ||| (OracleInt 1)
141 | ||| ]
142 | ||| ```
143 | |||
145 | queryOne : FromRow a => Connection -> String -> List BindParameter -> IO (Either OracleError (Maybe a))
156 | ||| Execute a query and require exactly one row.
157 | |||
158 | ||| Returns:
159 | ||| - Left OracleError if query execution fails.
160 | ||| - Left OracleError if no rows are returned.
161 | ||| - Left OracleError if more than one row is returned.
162 | ||| - Right value if exactly one row is returned.
163 | |||
164 | ||| This function is useful when querying by a primary key or other unique identifier.
165 | |||
166 | ||| Example:
167 | |||
168 | ||| ```idris
169 | ||| employee <-
170 | ||| queryExactlyOne
171 | ||| conn
172 | ||| "select id,name
173 | ||| from employees
174 | ||| where id = :id"
175 | ||| [ MkBindParameter "id"
176 | ||| (OracleInt 1)
177 | ||| ]
178 | ||| ```
179 | |||
181 | queryExactlyOne : FromRow a => Connection -> String -> List BindParameter -> IO (Either OracleError a)
188 | pure $
189 | Left $
190 | MkOracleError
192 | "Expected exactly one row but query returned no rows"
193 | "Oracle.Query.queryExactlyOne"
194 | False
198 | pure $
199 | Left $
200 | MkOracleError
202 | "Expected exactly one row but query returned multiple rows"
203 | "Oracle.Query.queryExactlyOne"
204 | False
206 | --------------------------------------------------------------------------------
207 | -- Structured Query
208 | --------------------------------------------------------------------------------
210 | ||| Execute a structured Query and decode every returned row.
211 | |||
212 | ||| The SQL statement is constructed using `buildQuerySQL`, which renders each `QueryColumn` in the SELECT projection.
213 | |||
214 | ||| Ordinary columns are rendered unchanged, while `JSONColumn` expressions are wrapped with `JSON_SERIALIZE(... RETURNING CLOB)`.
215 | |||
216 | ||| Each returned row is decoded using the `FromRow` implementation for the requested result type.
217 | |||
218 | ||| If the query returns no rows, this function succeeds with an empty list.
219 | |||
220 | ||| Any Oracle error encountered while preparing, binding, executing, or fetching the query is returned as `Left OracleError`.
221 | |||
227 | ||| Execute a structured Query and decode exactly one returned row.
228 | |||
229 | ||| The SQL statement is constructed using `buildQuerySQL`, which renders each `QueryColumn` in the SELECT projection.
230 | |||
231 | ||| Ordinary columns are rendered unchanged, while `JSONColumn` expressions are wrapped with `JSON_SERIALIZE(... RETURNING CLOB)`.
232 | |||
233 | ||| The returned row is decoded using the `FromRow` implementation for the requested result type.
234 | |||
235 | ||| This function succeeds only when the query returns exactly one row.
236 | |||
237 | ||| It returns an `OracleError` if the query returns no rows or more than one row.
238 | |||
239 | ||| Any Oracle error encountered while preparing, binding, executing, or fetching the query is returned as `Left OracleError`.
240 | |||
246 | --------------------------------------------------------------------------------
247 | -- JSON Query
248 | --------------------------------------------------------------------------------
250 | ||| Execute a JSON query and return the serialized JSON document.
251 | |||
252 | ||| The JSONQuery expression is wrapped internally as:
253 | |||
254 | ||| JSON_SERIALIZE(expression RETURNING CLOB)
255 | |||
256 | ||| The query must return exactly one row containing one non-null JSON value.
257 | |||
258 | ||| The returned JSON is represented as a String so that it can be decoded using the Idris2 JSON library.
259 | |||
260 | ||| Example:
261 | |||
262 | ||| queryJSON conn
263 | ||| (MkJSONQuery
264 | ||| "payload"
265 | ||| "documents WHERE id = :id"
266 | ||| [MkBindParameter "id" (OracleNumber 42)])
267 | |||
278 | pure $
279 | Left $
280 | MkOracleError
282 | "JSON query returned no rows"
283 | "Oracle.Query.queryJSON"
284 | False
288 | pure $
289 | Left $
290 | MkOracleError
292 | "JSON query returned no columns"
293 | "Oracle.Query.queryJSON"
294 | False
296 | pure $
297 | Left $
298 | MkOracleError
300 | "JSON query returned NULL"
301 | "Oracle.Query.queryJSON"
302 | False
306 | pure $
307 | Left $
308 | MkOracleError
310 | "JSON query returned an OracleString"
311 | "Oracle.Query.queryJSON"
312 | False
314 | pure $
315 | Left $
316 | MkOracleError
318 | "JSON query returned an unexpected value type"
319 | "queryJSON"
320 | False
322 | pure $
323 | Left $
324 | MkOracleError
326 | "JSON query returned more than one row; use queryJSONList"
327 | "queryJSON"
328 | False
330 | ||| Execute a JSON query and return all serialized JSON documents.
331 | |||
332 | ||| Each row must contain exactly one non-null JSON value.
333 | |||
334 | ||| This is the multi-row counterpart to queryJSON.
335 | |||
345 | where
359 | pure $
360 | Left $
361 | MkOracleError
363 | "JSON query returned an OracleString"
364 | "Oracle.Query.queryJSONList"
365 | False
367 | pure $
368 | Left $
369 | MkOracleError
371 | "JSON query returned NULL"
372 | "Oracle.Query.queryJSONList"
373 | False
375 | pure $
376 | Left $
377 | MkOracleError
379 | "JSON query returned no columns"
380 | "Oracle.Query.queryJSONList"
381 | False
383 | pure $
384 | Left $
385 | MkOracleError
387 | "JSON query returned an unexpected number or type of columns"
388 | "Oracle.Query.queryJSONList"
389 | False
391 | ||| Execute a JSON query and decode the resulting JSON document.
392 | |||
393 | ||| The result is decoded using the FromJSON implementation for `a`.
394 | |||
395 | ||| This allows callers to query Oracle JSON directly into an Idris data type with a derived FromJSON implementation.
396 | |||
407 | pure $
408 | Left $
409 | MkOracleError
412 | "Oracle.Query.queryJSONAs"
413 | False
417 | ||| Execute a JSON query and decode all resulting JSON documents.
418 | |||
419 | ||| Each row is decoded using the FromJSON implementation for `a`.
420 | |||
430 | where
437 | pure $
438 | Left $
439 | MkOracleError
442 | "Oracle.Query.queryJSONListAs"
443 | False
452 | --------------------------------------------------------------------------------
453 | -- Execute Statement That Returns No Rows
454 | --------------------------------------------------------------------------------
456 | ||| Execute a statement that does not return rows.
457 | |||
458 | ||| This function is intended for:
459 | ||| - INSERT
460 | ||| - UPDATE
461 | ||| - DELETE
462 | ||| - MERGE
463 | ||| - DDL statements
464 | |||
465 | ||| The statement is automatically:
466 | ||| 1. Prepared.
467 | ||| 2. Bound.
468 | ||| 3. Executed.
469 | ||| 4. Released.
470 | |||
471 | ||| Example:
472 | |||
473 | ||| ```idris
474 | ||| execute_
475 | ||| conn
476 | ||| "insert into employees(id,name)
477 | ||| values (:id,:name)"
478 | ||| [ MkBindParameter "id"
479 | ||| (OracleInt 1)
480 | ||| , MkBindParameter "name"
481 | ||| (OracleString "Alice")
482 | ||| ]
483 | ||| ```
484 | |||
485 | export