]> git.evergreen-ils.org Git - Evergreen.git/blob - docs/TechRef/qstore/qstore.adoc
db5c713e826d20d632743f7d2dd9e7047ac38a4c
[Evergreen.git] / docs / TechRef / qstore / qstore.adoc
1 Qstore
2 ======
3
4 The qstore server (open-ils.qstore) executes database queries that have
5 been previously defined, in an abstract form, within the database
6 itself.  Such queries may be used for periodic reporting, ad hoc
7 investigations, or automated updates.
8
9 In some cases a query may be qualified by values to be supplied at
10 execution time, called bind variables.  For example, a query may be
11 limited to a particular org unit, or a short list of user ids.
12
13 Before executing a query, the qstore client must open an application
14 session with the qstore server by sending it a CONNECT message.  Then it
15 must perform a series of steps to set up the query, execute it, and get
16 the results.  Finally, the client may close the query explicitly with an
17 additional method call.  Alternatively it may simply close the
18 application session by sending a DISCONNECT message to the server.
19
20 Here is a brief summary of the available methods.  Each will be
21 discussed in greater detail in a later section:
22
23 .  open-ils.qstore.prepare – load the query
24 .  open-ils.qstore.sql – return the query as SQL
25 .  open-ils.qstore.param_list – return a list of bind variables
26 .  open-ils.qstore.bind_param – assign values to one or more bind
27 variables
28 .  open-ils.qstore.execute – execute the query and return the results
29 .  open-ils.qstore.execute.atomic – an atomic version of the execute
30 method
31 .  open-ils.qstore.columns – return a list of column names for the
32 results
33 .  open-ils.qstore.finish – close a query, freeing any associated
34 resources
35 .  open-ils.qstore.messages – return any error message for a specified
36 query
37
38 The prepare method
39 ------------------
40 Summary: Load a specified query.   Return a list of bind variables, and
41 a token by which the query may be referenced in future method calls.
42
43 Parameter: The id of a row in the query.stored_query table, identifying
44 the stored query..
45
46 Returns: A JSON object with two members:
47
48 .  “token”: a text string to be used as a token for identifying the
49 query in future method calls.  This string is designed to be unique, but
50 otherwise has no very useful meaning.
51 .  “bind_variables”: a (possibly empty) JSON object defining any bind
52 variables required by the query.  See the discussion of the bind_param
53 method.
54
55 A client may juggle multiple queries in the same application session,
56 using the corresponding tokens to identify the query to which each
57 method call applies.
58
59 The sql method
60 --------------
61 Summary: Return the query as an SQL string.
62
63 Parameter: The token returned previously by the prepare method for the
64 same query.
65
66 Returns: A string containing the generated SQL corresponding to the
67 stored query.  It will incorporate the specified values of any bind
68 variables.  If no value has been assigned to a given bind variable, and
69 there is no default value for it, then the generated SQL will include
70 the name of the bind variable wherever it appears, preceded by a colon
71 to distinguish it from a column name.  The user may review such a query
72 but cannot execute it.
73
74 The param_list method
75 ---------------------
76 Summary: Returns information about the bind variables required by the
77 query, if any.
78
79 Parameter: The token returned previously by the prepare method for the
80 same query.
81
82 Returns: A JSON object keyed on the bind variable name.  The data
83 associated with each name is another JSON object, with the following
84 entries:
85
86 .  “label”: the contents of query.bind_variable.label for this
87 variable.  This is the identifier usually shown to the user for this
88 variable.
89 .  “type”: the contents of query.bind_variable.type for this variable.
90 It is one of “string”, “number”, “string_list”, or “number_list”.
91 .  “description”: the contents of query.bind_variable.description for
92 this variable.
93 .  “default_value”: the value that will be assigned to the variable if
94 the user doesn't override it.
95 .  “actual_value”: the value assigned by the user, overriding any
96 default.
97
98 Depending on the type, the default or actual value of a bind variable
99 may be a string, a number, a JSON array of strings, or a JSON array of
100 numbers.
101
102 If a given variable has no default value, then there will be no entry
103 for “default_value”.  On the other hand if the default value is a null,
104 then there will be an entry for “default_value” whose associated data is
105 a JSON null.  Likewise for “actual_value”.
106
107 The bind_param method
108 ---------------------
109 Summary: Assign a value to one or more bind variables.
110
111 Parameter: This is the only qstore method that requires two parameters:
112
113 .  The token returned previously by the prepare method for the same
114 query.
115 .  A (possibly empty) JSON object keyed on bind variable name.  The
116 value associated with each bind variable name is the value to be
117 assigned to the corresponding bind variable, overriding any default, and
118 replacing any value previously assigned.
119
120 The execute method
121 ------------------
122 Summary: Execute the specified query, and return the results.
123
124 Parameter: The token returned previously by the prepare method for the
125 same query.
126
127 Returns: Zero or more responses, each containing one row returned by the
128 query.  Each row is represented by a JSON array of values.
129
130 The execute.atomic method
131 -------------------------
132 Summary: Execute the specified query, and return the results.
133
134 Parameter: The token returned previously by the prepare method for the
135 same query.
136
137 Returns: A JSON array containing zero or more entries.  Each entry
138 represents a row as a JSON array of values.
139
140 The columns method
141 ------------------
142 Summary: Return the column names assigned by PostgreSQL to the result
143 set of the query.
144
145 Parameter: The token returned previously by the prepare method for the
146 same query.
147
148 Returns: An array of strings, each string being a column name.
149
150 The finish method
151 -----------------
152 Summary: Close a query, freeing any resources associated with it, and
153 rendering the token invalid for future method calls.
154
155 Parameter: The token returned previously by the prepare method for the
156 same query.
157
158 Closing the application session will finish any unfinished queries for
159 that session.
160
161 The messages method
162 -------------------
163 Summary: Return any error messages associated with a query.
164
165 Parameter: The token returned previously by the prepare method for the
166 same query.
167
168 Returns: A JSON array of strings, each string being an error message
169 issued in connection with the specified query.  The messages appear in
170 the order in which they were issued.  Typically the first message
171 describes the error as it was first encountered, and subsequent messages
172 describe the context in which the error occurred.
173
174 The messages returned include all those issued for the specified query,
175 including any issued for previous method calls.  Since currently there
176 is no method for purging error messages, they just accumulate.
177
178 In many cases (but not all), qstore writes similar messages to its log.
179  The messages method is based on the notion that most users shouldn't
180 have to examine log files.  They may however need help in interpreting
181 the error messages.
182
183 Example
184
185 The following srfsh session illustrates these methods.  After opening an
186 application session, we prepare query 12:
187
188 ----
189 srfsh# open open-ils.qstore
190
191 Service open-ils.qstore opened
192
193 srfsh# request open-ils.qstore open-ils.qstore.prepare 12
194
195 Received Data: {
196   "token":"1_1279135310_6487",
197   "bind_variables":{
198     "ou":{
199       "label":"lib",
200       "type":"number",
201       "description":"org unit"
202     }
203   }
204 }
205 ----
206
207 The server concocts a unique token, "1_1279135310_6487”, that we will
208 use henceforth to identify this query.  We could also prepare an
209 unrelated query, or even the same query more than once, and qstore would
210 assign each of them a different token and keep them all straight.  For
211 this example, though, we'll stick to the one query.
212
213 This query has one bind variable named “ou”, whose value needs to be
214 numeric.  The label, “lib”, is a short name for handy reference
215 (although in this case it's actually longer than the name used
216 internally).  The description tells us that it identifies an org unit.
217  In a real example the description probably should be more verbose, so
218 that user could see it in a tool tip (for example) and figure out what
219 to do.
220
221 In this case there is no default value, i.e. there is no entry for
222 “default_value”.  That means we can't run the query yet – but we can
223 still look at the SQL for it:
224
225 ----
226 srfsh# request open-ils.qstore open-ils.qstore.sql "1_1279135310_6487"
227
228 Received Data: "SELECT\n   \"aou\".id,\n   \"aou\".name,\n  
229 \"aou\".shortname,\n  
230
231 \"aou\".opac_visible,\n   \"aou\".parent_ou \nFROM\n   actor.org_unit AS
232 \"aou\" \nWHERE\n   \"aou\".id = :ou;\n"
233 ----
234
235 When we call the sql method, we pass it the token that the prepare
236 method assigned earlier.  The server returns the generated SQL, trying
237 to make it readable by inserting newlines and indentation.  In srfsh,
238 though, the output is pretty ugly because all the newlines and quotation
239 marks are escaped within a JSON string.  In this document, at least,
240 it's line-wrapped so that it fits on the page.  In a proper GUI it
241 should look much nicer.
242
243 At the end of the SQL, the query refers to the bind variablee as “:ou”,
244 the variable preceded by a colon.  The colon indicates that “ou” is a
245 variable name rather than a column name.  It also makes the SQL invalid
246 until we replace the variable with a real value.  Let's do that now, and
247 then look at the SQL again:
248
249 ----
250 srfsh# request open-ils.qstore open-ils.qstore.bind_param \
251 "1_1279135310_6487" {"ou":3}
252
253 srfsh# request open-ils.qstore open-ils.qstore.sql "1_1279135310_6487"
254
255 Received Data: "SELECT\n   \"aou\".id,\n   \"aou\".name,\n  
256 \"aou\".shortname,\n  
257 \"aou\".opac_visible,\n   \"aou\".parent_ou \nFROM\n   actor.org_unit
258 AS \"aou\" \nWHERE\n   \"aou\".id = 3;\n"
259 ----
260
261 When we call the bind_param method we pass not only the token but also a
262 JSON object assigning a value to one or more bind variables – just one
263 in this case.  Now the generated SQL looks normal.
264
265 We can also verify the substitution by calling the param_list method:
266
267 ----
268 srfsh# request open-ils.qstore open-ils.qstore.param_list \
269 "1_1279135310_6487"
270
271 Received Data: {
272   "ou":{
273     "label":"lib",
274     "type":"number",
275     "description":"org unit",
276     "actual_value":3
277   }
278 }
279 ----
280
281 Now we see an “actual_value” of 3.  The SQL is ready to go.
282
283 ----
284 srfsh# request open-ils.qstore open-ils.qstore.execute
285 "1_1279135310_6487"
286
287 Received Data: [
288   3,
289   "Example System 2",
290   "SYS2",
291   "t",
292   1
293 ]
294 ----
295
296 This query returns only one row, so we get only one response.  If there
297 were multiple rows, srfsh would display multiple lines of “Received
298 Data.”  The one response is a JSON array of column values.
299
300 Here's the atomic version of the same method call:
301
302 ----
303 srfsh# request open-ils.qstore open-ils.qstore.execute.atomic \
304 "1_1279135310_6487"
305
306 Received Data: [
307   [
308     3,
309     "Example System 2",
310     "SYS2",
311     "t",
312     1
313   ]
314 ]
315 ----
316
317 The difference isn't obvious because there's only one row, but notice
318 the an extra layer of square brackets.  This result is an array of
319 arrays of column values.  If there were multiple rows, they'd all be in
320 the same array.
321
322 This response does not identify the columns.  For that we must make
323 another call:
324
325 ----
326 srfsh# request open-ils.qstore open-ils.qstore.columns
327 "1_1279135310_6487"
328
329 Received Data: [
330   "id",
331   "name",
332   "shortname",
333   "opac_visible",
334   "parent_ou"
335 ]
336 ----
337
338 The result is a JSON array of column names.  These are the same names
339 that you would get if you ran the query in psql.  They may not be unique
340 or even helpful.  Ideally the query should assign good column aliases,
341 but if it doesn't, you have to take what you can get.
342
343 Now let's make a mistake, just so that we can see an error message.
344  We're going to assign a value to a bind variable that doesn't exist,
345 and then ask for any error messages:
346
347 ----
348 srfsh# request open-ils.qstore open-ils.qstore.bind_param
349 "1_1279135310_6487" {"goober":3}
350
351 Received Exception:
352 Name: osrfMethodException
353 Status: Unable to apply values to bind variables
354 Status: 400
355 Received Exception:
356 Name: osrfMethodException
357 Status: An unknown server error occurred
358 Status: 404
359
360 srfsh# request open-ils.qstore open-ils.qstore.messages
361 "1_1279135310_6487"
362
363 Received Data: [
364   "Can't assign value to bind variable \"goober\": no such variable"
365 ]
366 ----
367
368 The result is a JSON array of error messages as strings.  In this case
369 there's only one message.  In other cases there may be a series of
370 messages, the first one describing the error at the lowest level, and
371 the rest providing additional context.
372
373 Now that we're done with this query, we can shut it down:
374
375 ----
376 srfsh# request open-ils.qstore open-ils.qstore.finish
377 "1_1279135310_6487"
378
379 Received no data from server
380
381 srfsh# close open-ils.qstore
382
383 Service "open-ils.qstore" closed
384
385 srfsh# exit
386 ----
387
388 The finish method closes the query and frees associated memory.  In this
389 case we could have skipped it, because we immediately close the session
390 anyway, thereby closing any outstanding queries.