rev |
line source |
jbe/bsw@0
|
1 #!/usr/bin/env lua
|
jbe/bsw@0
|
2
|
jbe/bsw@0
|
3
|
jbe/bsw@0
|
4 ---------------------------
|
jbe/bsw@0
|
5 -- module initialization --
|
jbe/bsw@0
|
6 ---------------------------
|
jbe/bsw@0
|
7
|
jbe/bsw@0
|
8 local _G = _G
|
jbe/bsw@0
|
9 local _VERSION = _VERSION
|
jbe/bsw@0
|
10 local assert = assert
|
jbe/bsw@0
|
11 local error = error
|
jbe/bsw@0
|
12 local getmetatable = getmetatable
|
jbe/bsw@0
|
13 local ipairs = ipairs
|
jbe/bsw@0
|
14 local next = next
|
jbe/bsw@0
|
15 local pairs = pairs
|
jbe/bsw@0
|
16 local print = print
|
jbe/bsw@0
|
17 local rawequal = rawequal
|
jbe/bsw@0
|
18 local rawget = rawget
|
jbe@64
|
19 local rawlen = rawlen
|
jbe/bsw@0
|
20 local rawset = rawset
|
jbe/bsw@0
|
21 local select = select
|
jbe/bsw@0
|
22 local setmetatable = setmetatable
|
jbe/bsw@0
|
23 local tonumber = tonumber
|
jbe/bsw@0
|
24 local tostring = tostring
|
jbe/bsw@0
|
25 local type = type
|
jbe/bsw@0
|
26
|
jbe/bsw@0
|
27 local math = math
|
jbe/bsw@0
|
28 local string = string
|
jbe/bsw@0
|
29 local table = table
|
jbe/bsw@0
|
30
|
jbe/bsw@0
|
31 local add = table.insert
|
jbe/bsw@0
|
32
|
jbe@64
|
33 local _M = require("mondelefant_native")
|
jbe@64
|
34 if _ENV then
|
jbe@64
|
35 _ENV = _M
|
jbe@64
|
36 else
|
jbe@64
|
37 _G[...] = _M
|
jbe@64
|
38 setfenv(1, _M)
|
jbe@64
|
39 end
|
jbe/bsw@0
|
40
|
jbe@421
|
41 -- TODO: remove following downward-compatibility code
|
jbe@421
|
42 -- for mondelefant.connect{...} function:
|
jbe@421
|
43
|
jbe@421
|
44 do
|
jbe@421
|
45 local original_connect_function = connect
|
jbe@421
|
46 function connect(args)
|
jbe@421
|
47 if args.engine == "postgresql" then
|
jbe@421
|
48 local newargs = {}
|
jbe@421
|
49 for k, v in pairs(args) do
|
jbe@421
|
50 if k ~= "engine" then newargs[k] = v end
|
jbe@421
|
51 end
|
jbe@421
|
52 return original_connect_function(newargs)
|
jbe@421
|
53 else
|
jbe@421
|
54 return original_connect_function(args)
|
jbe@421
|
55 end
|
jbe@421
|
56 end
|
jbe@421
|
57 end
|
jbe@421
|
58
|
jbe/bsw@0
|
59
|
jbe/bsw@0
|
60
|
jbe/bsw@0
|
61 ---------------
|
jbe/bsw@0
|
62 -- selectors --
|
jbe/bsw@0
|
63 ---------------
|
jbe/bsw@0
|
64
|
jbe/bsw@0
|
65 selector_metatable = {}
|
jbe/bsw@0
|
66 selector_prototype = {}
|
jbe/bsw@0
|
67 selector_metatable.__index = selector_prototype
|
jbe/bsw@0
|
68
|
jbe/bsw@0
|
69 local function init_selector(self, db_conn)
|
jbe/bsw@0
|
70 self._db_conn = db_conn
|
jbe/bsw@0
|
71 self._mode = "list"
|
jbe@39
|
72 self._with = { sep = ", " }
|
jbe/bsw@0
|
73 self._fields = { sep = ", " }
|
jbe/bsw@0
|
74 self._distinct = false
|
jbe/bsw@0
|
75 self._distinct_on = {sep = ", ", expression}
|
jbe/bsw@0
|
76 self._from = { sep = " " }
|
jbe/bsw@4
|
77 self._where = { sep = ") AND (" }
|
jbe/bsw@0
|
78 self._group_by = { sep = ", " }
|
jbe/bsw@4
|
79 self._having = { sep = ") AND (" }
|
jbe/bsw@0
|
80 self._combine = { sep = " " }
|
jbe/bsw@0
|
81 self._order_by = { sep = ", " }
|
jbe/bsw@0
|
82 self._limit = nil
|
jbe/bsw@0
|
83 self._offset = nil
|
jbe/bsw@4
|
84 self._read_lock = { sep = ", " }
|
jbe/bsw@4
|
85 self._write_lock = { sep = ", " }
|
jbe/bsw@0
|
86 self._class = nil
|
jbe/bsw@0
|
87 self._attach = nil
|
jbe/bsw@0
|
88 return self
|
jbe/bsw@0
|
89 end
|
jbe/bsw@0
|
90
|
jbe@23
|
91 --[[--
|
jbe@23
|
92 selector = -- new selector
|
jbe@23
|
93 <db_handle>:new_selector()
|
jbe@23
|
94
|
jbe@23
|
95 Creates a new selector to operate on the given database handle.
|
jbe@23
|
96 --]]--
|
jbe/bsw@0
|
97 function connection_prototype:new_selector()
|
jbe/bsw@0
|
98 return init_selector(setmetatable({}, selector_metatable), self)
|
jbe/bsw@0
|
99 end
|
jbe@23
|
100 --//--
|
jbe/bsw@0
|
101
|
jbe@23
|
102 --[[--
|
jbe@23
|
103 db_handle = -- handle of database connection
|
jbe@23
|
104 <db_selector>:get_db_conn()
|
jbe@23
|
105
|
jbe@23
|
106 Returns the database connection handle used by a selector.
|
jbe@23
|
107
|
jbe@23
|
108 --]]--
|
jbe/bsw@0
|
109 function selector_prototype:get_db_conn()
|
jbe/bsw@0
|
110 return self._db_conn
|
jbe/bsw@0
|
111 end
|
jbe@23
|
112 --//--
|
jbe/bsw@0
|
113
|
jbe/bsw@0
|
114 -- TODO: selector clone?
|
jbe/bsw@0
|
115
|
jbe@23
|
116 --[[--
|
jbe@23
|
117 db_selector = -- same selector returned
|
jbe@23
|
118 <db_selector>:single_object_mode()
|
jbe@23
|
119
|
jbe@23
|
120 Sets selector to single object mode (mode "object" passed to "query" method of database handle). The selector is modified and returned.
|
jbe@23
|
121
|
jbe@23
|
122 --]]--
|
jbe/bsw@0
|
123 function selector_prototype:single_object_mode()
|
jbe/bsw@0
|
124 self._mode = "object"
|
jbe/bsw@0
|
125 return self
|
jbe/bsw@0
|
126 end
|
jbe@23
|
127 --//--
|
jbe/bsw@0
|
128
|
jbe@23
|
129 --[[--
|
jbe@23
|
130 db_selector = -- same selector returned
|
jbe@23
|
131 <db_selector>:optional_object_mode()
|
jbe@23
|
132
|
jbe@23
|
133 Sets selector to single object mode (mode "opt_object" passed to "query" method of database handle). The selector is modified and returned.
|
jbe@23
|
134
|
jbe@23
|
135 --]]--
|
jbe/bsw@0
|
136 function selector_prototype:optional_object_mode()
|
jbe/bsw@0
|
137 self._mode = "opt_object"
|
jbe/bsw@0
|
138 return self
|
jbe/bsw@0
|
139 end
|
jbe@23
|
140 --//--
|
jbe/bsw@0
|
141
|
jbe@23
|
142 --[[--
|
jbe@23
|
143 db_selector = -- same selector returned
|
jbe@23
|
144 <db_selector>:empty_list_mode()
|
jbe@23
|
145
|
jbe@23
|
146 Sets selector to empty list mode. The selector is modified and returned. When using the selector, no SQL query will be issued, but instead an empty database result list is returned.
|
jbe@23
|
147
|
jbe@23
|
148 --]]--
|
jbe/bsw@0
|
149 function selector_prototype:empty_list_mode()
|
jbe/bsw@0
|
150 self._mode = "empty_list"
|
jbe/bsw@0
|
151 return self
|
jbe/bsw@0
|
152 end
|
jbe@23
|
153 --//--
|
jbe/bsw@0
|
154
|
jbe@23
|
155 --[[--
|
jbe@39
|
156 db_selector =
|
jbe@39
|
157 <db_selector>:add_with(
|
jbe@39
|
158 expression = expression,
|
jbe@39
|
159 selector = selector
|
jbe@39
|
160 )
|
jbe@39
|
161
|
jbe@39
|
162 Adds an WITH RECURSIVE expression to the selector. The selector is modified and returned.
|
jbe@39
|
163 --]]--
|
jbe@39
|
164 function selector_prototype:add_with(expression, selector)
|
jbe@39
|
165 add(self._with, {"$ AS ($)", {expression}, {selector}})
|
jbe@39
|
166 return self
|
jbe@39
|
167 end
|
jbe@39
|
168 --//--
|
jbe@39
|
169
|
jbe@39
|
170 --[[--
|
jbe@23
|
171 db_selector = -- same selector returned
|
jbe@23
|
172 <db_selector>:add_distinct_on(
|
jbe@23
|
173 expression -- expression as passed to "assemble_command"
|
jbe@23
|
174 )
|
jbe@23
|
175
|
jbe@23
|
176 Adds an DISTINCT ON expression to the selector. The selector is modified and returned.
|
jbe@23
|
177
|
jbe@23
|
178 --]]--
|
jbe/bsw@0
|
179 function selector_prototype:add_distinct_on(expression)
|
jbe/bsw@0
|
180 if self._distinct then
|
jbe/bsw@0
|
181 error("Can not combine DISTINCT with DISTINCT ON.")
|
jbe/bsw@0
|
182 end
|
jbe/bsw@0
|
183 add(self._distinct_on, expression)
|
jbe/bsw@0
|
184 return self
|
jbe/bsw@0
|
185 end
|
jbe@23
|
186 --//--
|
jbe/bsw@0
|
187
|
jbe@23
|
188 --[[--
|
jbe@23
|
189 db_selector = -- same selector returned
|
jbe@23
|
190 <db_selector>:set_distinct()
|
jbe@23
|
191
|
jbe@23
|
192 Sets selector to perform a SELECT DISTINCT instead of SELECT (ALL). The selector is modified and returned. This mode can not be combined with DISTINCT ON.
|
jbe@23
|
193
|
jbe@23
|
194 --]]--
|
jbe/bsw@0
|
195 function selector_prototype:set_distinct()
|
jbe/bsw@0
|
196 if #self._distinct_on > 0 then
|
jbe/bsw@0
|
197 error("Can not combine DISTINCT with DISTINCT ON.")
|
jbe/bsw@0
|
198 end
|
jbe/bsw@0
|
199 self._distinct = true
|
jbe/bsw@0
|
200 return self
|
jbe/bsw@0
|
201 end
|
jbe@23
|
202 --//--
|
jbe/bsw@0
|
203
|
jbe@23
|
204 --[[--
|
jbe@23
|
205 db_selector = -- same selector returned
|
jbe@23
|
206 <db_selector>:add_from(
|
jbe@23
|
207 expression, -- expression as passed to "assemble_command"
|
jbe@23
|
208 alias, -- optional alias expression as passed to "assemble_command"
|
jbe@23
|
209 condition -- optional condition expression as passed to "assemble_command"
|
jbe@23
|
210 )
|
jbe@23
|
211
|
jbe@23
|
212 Adds expressions for FROM clause to the selector. The selector is modified and returned. If an additional condition is given, an INNER JOIN will be used, otherwise a CROSS JOIN.
|
jbe@23
|
213
|
jbe@23
|
214 This method is identical to "join".
|
jbe@23
|
215
|
jbe@23
|
216 --]]--
|
jbe/bsw@0
|
217 function selector_prototype:add_from(expression, alias, condition)
|
jbe/bsw@0
|
218 local first = (#self._from == 0)
|
jbe/bsw@0
|
219 if not first then
|
jbe/bsw@0
|
220 if condition then
|
jbe/bsw@0
|
221 add(self._from, "INNER JOIN")
|
jbe/bsw@0
|
222 else
|
jbe/bsw@0
|
223 add(self._from, "CROSS JOIN")
|
jbe/bsw@0
|
224 end
|
jbe/bsw@0
|
225 end
|
jbe/bsw@0
|
226 if getmetatable(expression) == selector_metatable then
|
jbe/bsw@0
|
227 if alias then
|
jbe/bsw@0
|
228 add(self._from, {'($) AS "$"', {expression}, {alias}})
|
jbe/bsw@0
|
229 else
|
jbe/bsw@0
|
230 add(self._from, {'($) AS "subquery"', {expression}})
|
jbe/bsw@0
|
231 end
|
jbe/bsw@0
|
232 else
|
jbe/bsw@0
|
233 if alias then
|
jbe/bsw@0
|
234 add(self._from, {'$ AS "$"', {expression}, {alias}})
|
jbe/bsw@0
|
235 else
|
jbe/bsw@0
|
236 add(self._from, expression)
|
jbe/bsw@0
|
237 end
|
jbe/bsw@0
|
238 end
|
jbe/bsw@0
|
239 if condition then
|
jbe/bsw@0
|
240 if first then
|
jbe@55
|
241 self:add_where(condition)
|
jbe/bsw@0
|
242 else
|
jbe/bsw@0
|
243 add(self._from, "ON")
|
jbe/bsw@0
|
244 add(self._from, condition)
|
jbe/bsw@0
|
245 end
|
jbe/bsw@0
|
246 end
|
jbe/bsw@0
|
247 return self
|
jbe/bsw@0
|
248 end
|
jbe@23
|
249 --//--
|
jbe/bsw@0
|
250
|
jbe@23
|
251 --[[--
|
jbe@23
|
252 db_selector = -- same selector returned
|
jbe@23
|
253 <db_selector>:add_where(
|
jbe@23
|
254 expression -- expression as passed to "assemble_command"
|
jbe@23
|
255 )
|
jbe@23
|
256
|
jbe@23
|
257 Adds expressions for WHERE clause to the selector. The selector is modified and returned. Multiple calls cause expressions to be AND-combined.
|
jbe@23
|
258
|
jbe@23
|
259 --]]--
|
jbe/bsw@0
|
260 function selector_prototype:add_where(expression)
|
jbe/bsw@0
|
261 add(self._where, expression)
|
jbe/bsw@0
|
262 return self
|
jbe/bsw@0
|
263 end
|
jbe@23
|
264 --//--
|
jbe/bsw@0
|
265
|
jbe@23
|
266 --[[--
|
jbe@23
|
267 db_selector = -- same selector returned
|
jbe@23
|
268 <db_selector>:add_group_by(
|
jbe@23
|
269 expression -- expression as passed to "assemble_command"
|
jbe@23
|
270 )
|
jbe@23
|
271
|
jbe@23
|
272 Adds expressions for GROUP BY clause to the selector. The selector is modified and returned.
|
jbe@23
|
273
|
jbe@23
|
274 --]]--
|
jbe/bsw@0
|
275 function selector_prototype:add_group_by(expression)
|
jbe/bsw@0
|
276 add(self._group_by, expression)
|
jbe/bsw@0
|
277 return self
|
jbe/bsw@0
|
278 end
|
jbe@23
|
279 --//--
|
jbe/bsw@0
|
280
|
jbe@23
|
281 --[[--
|
jbe@23
|
282 db_selector = -- same selector returned
|
jbe@23
|
283 <db_selector>:add_having(
|
jbe@23
|
284 expression -- expression as passed to "assemble_command"
|
jbe@23
|
285 )
|
jbe@23
|
286
|
jbe@23
|
287 Adds expressions for HAVING clause to the selector. The selector is modified and returned. Multiple calls cause expressions to be AND-combined.
|
jbe@23
|
288
|
jbe@23
|
289 --]]--
|
jbe/bsw@0
|
290 function selector_prototype:add_having(expression)
|
jbe/bsw@0
|
291 add(self._having, expression)
|
jbe/bsw@0
|
292 return self
|
jbe/bsw@0
|
293 end
|
jbe@23
|
294 --//--
|
jbe/bsw@0
|
295
|
jbe@23
|
296 --[[--
|
jbe@23
|
297 db_selector = -- same selector returned
|
jbe@23
|
298 <db_selector>:add_combine(
|
jbe@23
|
299 expression -- expression as passed to "assemble_command"
|
jbe@23
|
300 )
|
jbe@23
|
301
|
jbe@23
|
302 This function is used for UNION/INTERSECT/EXCEPT clauses. It does not need to be called directly. Use "union", "union_all", "intersect", "intersect_all", "except" and "except_all" instead.
|
jbe@23
|
303
|
jbe@23
|
304 --]]--
|
jbe/bsw@0
|
305 function selector_prototype:add_combine(expression)
|
jbe/bsw@0
|
306 add(self._combine, expression)
|
jbe/bsw@0
|
307 return self
|
jbe/bsw@0
|
308 end
|
jbe@23
|
309 --//--
|
jbe/bsw@0
|
310
|
jbe@23
|
311 --[[--
|
jbe@23
|
312 db_selector = -- same selector returned
|
jbe@23
|
313 <db_selector>:add_order_by(
|
jbe@23
|
314 expression -- expression as passed to "assemble_command"
|
jbe@23
|
315 )
|
jbe@23
|
316
|
jbe@23
|
317 Adds expressions for ORDER BY clause to the selector. The selector is modified and returned.
|
jbe@23
|
318
|
jbe@23
|
319 --]]--
|
jbe/bsw@0
|
320 function selector_prototype:add_order_by(expression)
|
jbe/bsw@0
|
321 add(self._order_by, expression)
|
jbe/bsw@0
|
322 return self
|
jbe/bsw@0
|
323 end
|
jbe@23
|
324 --//--
|
jbe/bsw@0
|
325
|
jbe@23
|
326 --[[--
|
jbe@23
|
327 db_selector = -- same selector returned
|
jbe@23
|
328 <db_selector>:limit(
|
jbe@23
|
329 count -- integer used as LIMIT
|
jbe@23
|
330 )
|
jbe@23
|
331
|
jbe@23
|
332 Limits the number of rows to a given number, by using LIMIT. The selector is modified and returned.
|
jbe@23
|
333
|
jbe@23
|
334 --]]--
|
jbe/bsw@0
|
335 function selector_prototype:limit(count)
|
jbe/bsw@0
|
336 if type(count) ~= "number" or count % 1 ~= 0 then
|
jbe/bsw@0
|
337 error("LIMIT must be an integer.")
|
jbe/bsw@0
|
338 end
|
jbe/bsw@0
|
339 self._limit = count
|
jbe/bsw@0
|
340 return self
|
jbe/bsw@0
|
341 end
|
jbe@23
|
342 --//--
|
jbe/bsw@0
|
343
|
jbe@23
|
344 --[[--
|
jbe@23
|
345 db_selector = -- same selector returned
|
jbe@23
|
346 <db_selector>:offset(
|
jbe@23
|
347 count -- integer used as OFFSET
|
jbe@23
|
348 )
|
jbe@23
|
349
|
jbe@23
|
350 Skips a given number of rows, by using OFFSET. The selector is modified and returned.
|
jbe@23
|
351
|
jbe@23
|
352 --]]--
|
jbe/bsw@0
|
353 function selector_prototype:offset(count)
|
jbe/bsw@0
|
354 if type(count) ~= "number" or count % 1 ~= 0 then
|
jbe/bsw@0
|
355 error("OFFSET must be an integer.")
|
jbe/bsw@0
|
356 end
|
jbe/bsw@0
|
357 self._offset = count
|
jbe/bsw@0
|
358 return self
|
jbe/bsw@0
|
359 end
|
jbe@23
|
360 --//--
|
jbe/bsw@0
|
361
|
jbe@23
|
362 --[[--
|
jbe@23
|
363 db_selector = -- same selector returned
|
jbe@23
|
364 <db_selector>:for_share()
|
jbe@23
|
365
|
jbe@23
|
366 Adds FOR SHARE to the statement, to share-lock all rows read. The selector is modified and returned.
|
jbe@23
|
367
|
jbe@23
|
368 --]]--
|
jbe/bsw@4
|
369 function selector_prototype:for_share()
|
jbe/bsw@4
|
370 self._read_lock.all = true
|
jbe/bsw@4
|
371 return self
|
jbe/bsw@4
|
372 end
|
jbe@23
|
373 --//--
|
jbe/bsw@4
|
374
|
jbe@23
|
375 --[[--
|
jbe@23
|
376 db_selector = -- same selector returned
|
jbe@23
|
377 <db_selector>:for_share_of(
|
jbe@23
|
378 expression -- expression as passed to "assemble_command"
|
jbe@23
|
379 )
|
jbe@23
|
380
|
jbe@23
|
381 Adds FOR SHARE OF to the statement, to share-lock all rows read by the named table(s). The selector is modified and returned.
|
jbe@23
|
382
|
jbe@23
|
383 --]]--
|
jbe/bsw@4
|
384 function selector_prototype:for_share_of(expression)
|
jbe/bsw@4
|
385 add(self._read_lock, expression)
|
jbe/bsw@4
|
386 return self
|
jbe/bsw@4
|
387 end
|
jbe@23
|
388 --//--
|
jbe/bsw@4
|
389
|
jbe@23
|
390 --[[--
|
jbe@23
|
391 db_selector = -- same selector returned
|
jbe@23
|
392 <db_selector>:for_update()
|
jbe@23
|
393
|
jbe@23
|
394 Adds FOR UPDATE to the statement, to exclusivly lock all rows read. The selector is modified and returned.
|
jbe@23
|
395
|
jbe@23
|
396 --]]--
|
jbe/bsw@4
|
397 function selector_prototype:for_update()
|
jbe/bsw@4
|
398 self._write_lock.all = true
|
jbe/bsw@4
|
399 return self
|
jbe/bsw@4
|
400 end
|
jbe@23
|
401 --//--
|
jbe/bsw@4
|
402
|
jbe@23
|
403 --[[--
|
jbe@23
|
404 db_selector = -- same selector returned
|
jbe@23
|
405 <db_selector>:for_update_of(
|
jbe@23
|
406 expression -- expression as passed to "assemble_command"
|
jbe@23
|
407 )
|
jbe@23
|
408
|
jbe@23
|
409 Adds FOR SHARE OF to the statement, to exclusivly lock all rows read by the named table(s). The selector is modified and returned.
|
jbe@23
|
410
|
jbe@23
|
411 --]]--
|
jbe/bsw@4
|
412 function selector_prototype:for_update_of(expression)
|
jbe/bsw@4
|
413 add(self._write_lock, expression)
|
jbe/bsw@4
|
414 return self
|
jbe/bsw@4
|
415 end
|
jbe@23
|
416 --//--
|
jbe/bsw@4
|
417
|
jbe@23
|
418 --[[--
|
jbe@23
|
419 db_selector = -- same selector returned
|
jbe@23
|
420 <db_selector>:reset_fields()
|
jbe@23
|
421
|
jbe@23
|
422 This method removes all fields added by method "add_field". The selector is modified and returned.
|
jbe@23
|
423
|
jbe@23
|
424 --]]--
|
jbe/bsw@0
|
425 function selector_prototype:reset_fields()
|
jbe/bsw@0
|
426 for idx in ipairs(self._fields) do
|
jbe/bsw@0
|
427 self._fields[idx] = nil
|
jbe/bsw@0
|
428 end
|
jbe/bsw@0
|
429 return self
|
jbe/bsw@0
|
430 end
|
jbe@23
|
431 --//--
|
jbe/bsw@0
|
432
|
jbe@23
|
433 --[[--
|
jbe@23
|
434 db_selector = -- same selector returned
|
jbe@23
|
435 <db_selector>:add_field(
|
jbe@23
|
436 expression, -- expression as passed to "assemble_command"
|
jbe@23
|
437 alias, -- optional alias expression as passed to "assemble_command"
|
jbe@23
|
438 option_list -- optional list of options (may contain strings "distinct" or "grouped")
|
jbe@23
|
439 )
|
jbe@23
|
440
|
jbe@23
|
441 Adds fields to the selector. The selector is modified and returned. The third argument can be a list of options. If option "distinct" is given, then "add_distinct_on" will be executed for the given field or alias. If option "grouped" is given, then "add_group_by" will be executed for the given field or alias.
|
jbe@23
|
442
|
jbe@23
|
443 --]]--
|
jbe/bsw@0
|
444 function selector_prototype:add_field(expression, alias, options)
|
jbe/bsw@0
|
445 if alias then
|
jbe/bsw@0
|
446 add(self._fields, {'$ AS "$"', {expression}, {alias}})
|
jbe/bsw@0
|
447 else
|
jbe/bsw@0
|
448 add(self._fields, expression)
|
jbe/bsw@0
|
449 end
|
jbe/bsw@0
|
450 if options then
|
jbe/bsw@0
|
451 for i, option in ipairs(options) do
|
jbe/bsw@0
|
452 if option == "distinct" then
|
jbe/bsw@0
|
453 if alias then
|
jbe/bsw@0
|
454 self:add_distinct_on('"' .. alias .. '"')
|
jbe/bsw@0
|
455 else
|
jbe/bsw@0
|
456 self:add_distinct_on(expression)
|
jbe/bsw@0
|
457 end
|
jbe/bsw@0
|
458 elseif option == "grouped" then
|
jbe/bsw@0
|
459 if alias then
|
jbe/bsw@0
|
460 self:add_group_by('"' .. alias .. '"')
|
jbe/bsw@0
|
461 else
|
jbe/bsw@0
|
462 self:add_group_by(expression)
|
jbe/bsw@0
|
463 end
|
jbe/bsw@0
|
464 else
|
jbe/bsw@0
|
465 error("Unknown option '" .. option .. "' to add_field method.")
|
jbe/bsw@0
|
466 end
|
jbe/bsw@0
|
467 end
|
jbe/bsw@0
|
468 end
|
jbe/bsw@0
|
469 return self
|
jbe/bsw@0
|
470 end
|
jbe@23
|
471 --//--
|
jbe/bsw@0
|
472
|
jbe@23
|
473 --[[--
|
jbe@23
|
474 db_selector = -- same selector returned
|
jbe@23
|
475 <db_selector>:join(
|
jbe@23
|
476 expression, -- expression as passed to "assemble_command"
|
jbe@23
|
477 alias, -- optional alias expression as passed to "assemble_command"
|
jbe@23
|
478 condition -- optional condition expression as passed to "assemble_command"
|
jbe@23
|
479 )
|
jbe@23
|
480
|
jbe@23
|
481 Adds expressions for FROM clause to the selector. The selector is modified and returned. If an additional condition is given, an INNER JOIN will be used, otherwise a CROSS JOIN.
|
jbe@23
|
482
|
jbe@23
|
483 This method is identical to "add_from".
|
jbe@23
|
484
|
jbe@23
|
485 --]]--
|
jbe/bsw@0
|
486 function selector_prototype:join(...) -- NOTE: alias for add_from
|
jbe/bsw@0
|
487 return self:add_from(...)
|
jbe/bsw@0
|
488 end
|
jbe@23
|
489 --//--
|
jbe/bsw@0
|
490
|
jbe@23
|
491 --[[--
|
jbe@23
|
492 db_selector = -- same selector returned
|
jbe@23
|
493 <db_selector>:from(
|
jbe@23
|
494 expression, -- expression as passed to "assemble_command"
|
jbe@23
|
495 alias, -- optional alias expression as passed to "assemble_command"
|
jbe@23
|
496 condition -- optional condition expression as passed to "assemble_command"
|
jbe@23
|
497 )
|
jbe@23
|
498
|
jbe@23
|
499 Adds the first expression for FROM clause to the selector. The selector is modified and returned. If an additional condition is given, an INNER JOIN will be used, otherwise a CROSS JOIN.
|
jbe@23
|
500
|
jbe@23
|
501 This method is identical to "add_from" or "join", except that an error is thrown, if there is already any FROM expression existent.
|
jbe@23
|
502
|
jbe@23
|
503 --]]--
|
jbe/bsw@0
|
504 function selector_prototype:from(expression, alias, condition)
|
jbe/bsw@0
|
505 if #self._from > 0 then
|
jbe/bsw@0
|
506 error("From-clause already existing (hint: try join).")
|
jbe/bsw@0
|
507 end
|
jbe/bsw@0
|
508 return self:join(expression, alias, condition)
|
jbe/bsw@0
|
509 end
|
jbe@23
|
510 --//--
|
jbe/bsw@0
|
511
|
jbe@23
|
512 --[[--
|
jbe@23
|
513 db_selector = -- same selector returned
|
jbe@23
|
514 <db_selector>:left_join(
|
jbe@23
|
515 expression, -- expression as passed to "assemble_command"
|
jbe@23
|
516 alias, -- optional alias expression as passed to "assemble_command"
|
jbe@23
|
517 condition -- optional condition expression as passed to "assemble_command"
|
jbe@23
|
518 )
|
jbe@23
|
519
|
jbe@23
|
520 Adds expressions for FROM clause to the selector using a LEFT OUTER JOIN. The selector is modified and returned.
|
jbe@23
|
521
|
jbe@23
|
522 --]]--
|
jbe/bsw@0
|
523 function selector_prototype:left_join(expression, alias, condition)
|
jbe/bsw@0
|
524 local first = (#self._from == 0)
|
jbe/bsw@0
|
525 if not first then
|
jbe/bsw@0
|
526 add(self._from, "LEFT OUTER JOIN")
|
jbe/bsw@0
|
527 end
|
jbe/bsw@0
|
528 if alias then
|
jbe/bsw@0
|
529 add(self._from, {'$ AS "$"', {expression}, {alias}})
|
jbe/bsw@0
|
530 else
|
jbe/bsw@0
|
531 add(self._from, expression)
|
jbe/bsw@0
|
532 end
|
jbe/bsw@0
|
533 if condition then
|
jbe/bsw@0
|
534 if first then
|
jbe@55
|
535 self:add_where(condition)
|
jbe/bsw@0
|
536 else
|
jbe/bsw@0
|
537 add(self._from, "ON")
|
jbe/bsw@0
|
538 add(self._from, condition)
|
jbe/bsw@0
|
539 end
|
jbe/bsw@0
|
540 end
|
jbe/bsw@0
|
541 return self
|
jbe/bsw@0
|
542 end
|
jbe@23
|
543 --//--
|
jbe/bsw@0
|
544
|
jbe@23
|
545 --[[--
|
jbe@23
|
546 db_selector = -- same selector returned
|
jbe@23
|
547 <db_selector>:union(
|
jbe@23
|
548 expression -- expression or selector without ORDER BY, LIMIT, FOR UPDATE or FOR SHARE
|
jbe@23
|
549 )
|
jbe@23
|
550
|
jbe@348
|
551 This method adds a UNION clause to the given selector. The selector is modified and returned. The selector (or expression) passed as argument to this function shall not contain any ORDER BY, LIMIT, FOR UPDATE or FOR SHARE clauses.
|
jbe@23
|
552
|
jbe@23
|
553 --]]--
|
jbe/bsw@0
|
554 function selector_prototype:union(expression)
|
jbe/bsw@0
|
555 self:add_combine{"UNION $", {expression}}
|
jbe/bsw@0
|
556 return self
|
jbe/bsw@0
|
557 end
|
jbe@23
|
558 --//--
|
jbe/bsw@0
|
559
|
jbe@23
|
560 --[[--
|
jbe@23
|
561 db_selector = -- same selector returned
|
jbe@23
|
562 <db_selector>:union_all(
|
jbe@23
|
563 expression -- expression or selector without ORDER BY, LIMIT, FOR UPDATE or FOR SHARE
|
jbe@23
|
564 )
|
jbe@23
|
565
|
jbe@348
|
566 This method adds a UNION ALL clause to the given selector. The selector is modified and returned. The selector (or expression) passed as argument to this function shall not contain any ORDER BY, LIMIT, FOR UPDATE or FOR SHARE clauses.
|
jbe@23
|
567
|
jbe@23
|
568 --]]--
|
jbe/bsw@0
|
569 function selector_prototype:union_all(expression)
|
jbe/bsw@0
|
570 self:add_combine{"UNION ALL $", {expression}}
|
jbe/bsw@0
|
571 return self
|
jbe/bsw@0
|
572 end
|
jbe@23
|
573 --//--
|
jbe/bsw@0
|
574
|
jbe@23
|
575 --[[--
|
jbe@23
|
576 db_selector = -- same selector returned
|
jbe@23
|
577 <db_selector>:intersect(
|
jbe@23
|
578 expression -- expression or selector without ORDER BY, LIMIT, FOR UPDATE or FOR SHARE
|
jbe@23
|
579 )
|
jbe@23
|
580
|
jbe@23
|
581 This method adds an INTERSECT clause to the given selector. The selector is modified and returned. The selector (or expression) passed as argument to this function shall not contain any ORDER BY, LIMIT, FOR UPDATE or FOR SHARE clauses.
|
jbe@23
|
582
|
jbe@23
|
583 --]]--
|
jbe/bsw@0
|
584 function selector_prototype:intersect(expression)
|
jbe/bsw@0
|
585 self:add_combine{"INTERSECT $", {expression}}
|
jbe/bsw@0
|
586 return self
|
jbe/bsw@0
|
587 end
|
jbe@23
|
588 --//--
|
jbe/bsw@0
|
589
|
jbe@23
|
590 --[[--
|
jbe@23
|
591 db_selector = -- same selector returned
|
jbe@23
|
592 <db_selector>:intersect_all(
|
jbe@23
|
593 expression -- expression or selector without ORDER BY, LIMIT, FOR UPDATE or FOR SHARE
|
jbe@23
|
594 )
|
jbe@23
|
595
|
jbe@23
|
596 This method adds an INTERSECT ALL clause to the given selector. The selector is modified and returned. The selector (or expression) passed as argument to this function shall not contain any ORDER BY, LIMIT, FOR UPDATE or FOR SHARE clauses.
|
jbe@23
|
597
|
jbe@23
|
598 --]]--
|
jbe/bsw@0
|
599 function selector_prototype:intersect_all(expression)
|
jbe/bsw@0
|
600 self:add_combine{"INTERSECT ALL $", {expression}}
|
jbe/bsw@0
|
601 return self
|
jbe/bsw@0
|
602 end
|
jbe@23
|
603 --//--
|
jbe/bsw@0
|
604
|
jbe@23
|
605 --[[--
|
jbe@23
|
606 db_selector = -- same selector returned
|
jbe@23
|
607 <db_selector>:except(
|
jbe@23
|
608 expression -- expression or selector without ORDER BY, LIMIT, FOR UPDATE or FOR SHARE
|
jbe@23
|
609 )
|
jbe@23
|
610
|
jbe@23
|
611 This method adds an EXCEPT clause to the given selector. The selector is modified and returned. The selector (or expression) passed as argument to this function shall not contain any ORDER BY, LIMIT, FOR UPDATE or FOR SHARE clauses.
|
jbe@23
|
612
|
jbe@23
|
613 --]]--
|
jbe/bsw@0
|
614 function selector_prototype:except(expression)
|
jbe/bsw@0
|
615 self:add_combine{"EXCEPT $", {expression}}
|
jbe/bsw@0
|
616 return self
|
jbe/bsw@0
|
617 end
|
jbe@23
|
618 --//--
|
jbe/bsw@0
|
619
|
jbe@23
|
620 --[[--
|
jbe@23
|
621 db_selector = -- same selector returned
|
jbe@23
|
622 <db_selector>:except_all(
|
jbe@23
|
623 expression -- expression or selector without ORDER BY, LIMIT, FOR UPDATE or FOR SHARE
|
jbe@23
|
624 )
|
jbe@23
|
625
|
jbe@23
|
626 This method adds an EXCEPT ALL clause to the given selector. The selector is modified and returned. The selector (or expression) passed as argument to this function shall not contain any ORDER BY, LIMIT, FOR UPDATE or FOR SHARE clauses.
|
jbe@23
|
627
|
jbe@23
|
628 --]]--
|
jbe/bsw@0
|
629 function selector_prototype:except_all(expression)
|
jbe/bsw@0
|
630 self:add_combine{"EXCEPT ALL $", {expression}}
|
jbe/bsw@0
|
631 return self
|
jbe/bsw@0
|
632 end
|
jbe@23
|
633 --//--
|
jbe/bsw@0
|
634
|
jbe@23
|
635 --[[--
|
jbe@23
|
636 db_selector = -- same selector returned
|
jbe@23
|
637 <db_selector>:set_class(
|
jbe@23
|
638 class -- database class (model)
|
jbe@23
|
639 )
|
jbe@23
|
640
|
jbe@23
|
641 This method makes the selector to return database result lists or objects of the given database class (model). The selector is modified and returned.
|
jbe@23
|
642
|
jbe@23
|
643 --]]--
|
jbe/bsw@0
|
644 function selector_prototype:set_class(class)
|
jbe/bsw@0
|
645 self._class = class
|
jbe/bsw@0
|
646 return self
|
jbe/bsw@0
|
647 end
|
jbe@23
|
648 --//--
|
jbe/bsw@0
|
649
|
jbe@23
|
650 --[[--
|
jbe@23
|
651 db_selector = -- same selector returned
|
jbe@23
|
652 <db_selector>:attach(
|
jbe@23
|
653 mode, -- attachment type: "11" one to one, "1m" one to many, "m1" many to one
|
jbe@23
|
654 data2, -- other database result list or object, the results of this selector shall be attached with
|
jbe@23
|
655 field1, -- field name(s) in result list or object of this selector used for attaching
|
jbe@23
|
656 field2, -- field name(s) in "data2" used for attaching
|
jbe@23
|
657 ref1, -- name of reference field in the results of this selector after attaching
|
jbe@23
|
658 ref2 -- name of reference field in "data2" after attaching
|
jbe@23
|
659 )
|
jbe@23
|
660
|
jbe@23
|
661 This method causes database result lists or objects of this selector to be attached with other database result lists after execution. This method does not need to be called directly.
|
jbe@23
|
662
|
jbe@23
|
663 --]]--
|
jbe/bsw@0
|
664 function selector_prototype:attach(mode, data2, field1, field2, ref1, ref2)
|
jbe/bsw@0
|
665 self._attach = {
|
jbe/bsw@0
|
666 mode = mode,
|
jbe/bsw@0
|
667 data2 = data2,
|
jbe/bsw@0
|
668 field1 = field1,
|
jbe/bsw@0
|
669 field2 = field2,
|
jbe/bsw@0
|
670 ref1 = ref1,
|
jbe/bsw@0
|
671 ref2 = ref2
|
jbe/bsw@0
|
672 }
|
jbe/bsw@0
|
673 return self
|
jbe/bsw@0
|
674 end
|
jbe@23
|
675 --//--
|
jbe/bsw@0
|
676
|
jbe/bsw@0
|
677 function selector_metatable:__tostring()
|
jbe/bsw@0
|
678 local parts = {sep = " "}
|
jbe@39
|
679 if #self._with > 0 then
|
jbe@39
|
680 add(parts, {"WITH RECURSIVE $", self._with})
|
jbe@39
|
681 end
|
jbe/bsw@0
|
682 add(parts, "SELECT")
|
jbe/bsw@0
|
683 if self._distinct then
|
jbe/bsw@0
|
684 add(parts, "DISTINCT")
|
jbe/bsw@0
|
685 elseif #self._distinct_on > 0 then
|
jbe/bsw@0
|
686 add(parts, {"DISTINCT ON ($)", self._distinct_on})
|
jbe/bsw@0
|
687 end
|
jbe/bsw@0
|
688 add(parts, {"$", self._fields})
|
jbe/bsw@0
|
689 if #self._from > 0 then
|
jbe/bsw@0
|
690 add(parts, {"FROM $", self._from})
|
jbe/bsw@0
|
691 end
|
jbe/bsw@0
|
692 if #self._mode == "empty_list" then
|
jbe/bsw@0
|
693 add(parts, "WHERE FALSE")
|
jbe/bsw@0
|
694 elseif #self._where > 0 then
|
jbe/bsw@4
|
695 add(parts, {"WHERE ($)", self._where})
|
jbe/bsw@0
|
696 end
|
jbe/bsw@0
|
697 if #self._group_by > 0 then
|
jbe/bsw@0
|
698 add(parts, {"GROUP BY $", self._group_by})
|
jbe/bsw@0
|
699 end
|
jbe/bsw@0
|
700 if #self._having > 0 then
|
jbe/bsw@4
|
701 add(parts, {"HAVING ($)", self._having})
|
jbe/bsw@0
|
702 end
|
jbe/bsw@0
|
703 for i, v in ipairs(self._combine) do
|
jbe/bsw@0
|
704 add(parts, v)
|
jbe/bsw@0
|
705 end
|
jbe/bsw@0
|
706 if #self._order_by > 0 then
|
jbe/bsw@0
|
707 add(parts, {"ORDER BY $", self._order_by})
|
jbe/bsw@0
|
708 end
|
jbe/bsw@0
|
709 if self._mode == "empty_list" then
|
jbe/bsw@0
|
710 add(parts, "LIMIT 0")
|
jbe/bsw@0
|
711 elseif self._mode ~= "list" then
|
jbe/bsw@0
|
712 add(parts, "LIMIT 1")
|
jbe/bsw@0
|
713 elseif self._limit then
|
jbe/bsw@0
|
714 add(parts, "LIMIT " .. self._limit)
|
jbe/bsw@0
|
715 end
|
jbe/bsw@0
|
716 if self._offset then
|
jbe/bsw@0
|
717 add(parts, "OFFSET " .. self._offset)
|
jbe/bsw@0
|
718 end
|
jbe/bsw@4
|
719 if self._write_lock.all then
|
jbe/bsw@4
|
720 add(parts, "FOR UPDATE")
|
jbe/bsw@4
|
721 else
|
jbe/bsw@4
|
722 if self._read_lock.all then
|
jbe/bsw@4
|
723 add(parts, "FOR SHARE")
|
jbe/bsw@4
|
724 elseif #self._read_lock > 0 then
|
jbe/bsw@4
|
725 add(parts, {"FOR SHARE OF $", self._read_lock})
|
jbe/bsw@4
|
726 end
|
jbe/bsw@4
|
727 if #self._write_lock > 0 then
|
jbe/bsw@4
|
728 add(parts, {"FOR UPDATE OF $", self._write_lock})
|
jbe/bsw@4
|
729 end
|
jbe/bsw@4
|
730 end
|
jbe/bsw@0
|
731 return self._db_conn:assemble_command{"$", parts}
|
jbe/bsw@0
|
732 end
|
jbe/bsw@0
|
733
|
jbe@23
|
734 --[[--
|
jbe@23
|
735 db_error, -- database error object, or nil in case of success
|
jbe@23
|
736 result = -- database result list or object
|
jbe@23
|
737 <db_selector>:try_exec()
|
jbe@23
|
738
|
jbe@23
|
739 This method executes the selector on its database. First return value is an error object or nil in case of success. Second return value is the result list or object.
|
jbe@23
|
740
|
jbe@23
|
741 --]]--
|
jbe/bsw@0
|
742 function selector_prototype:try_exec()
|
jbe/bsw@0
|
743 if self._mode == "empty_list" then
|
jbe/bsw@0
|
744 if self._class then
|
jbe/bsw@0
|
745 return nil, self._class:create_list()
|
jbe/bsw@0
|
746 else
|
jbe/bsw@0
|
747 return nil, self._db_conn:create_list()
|
jbe/bsw@0
|
748 end
|
jbe/bsw@0
|
749 end
|
jbe/bsw@0
|
750 local db_error, db_result = self._db_conn:try_query(self, self._mode)
|
jbe/bsw@0
|
751 if db_error then
|
jbe/bsw@0
|
752 return db_error
|
jbe/bsw@0
|
753 elseif db_result then
|
jbe/bsw@0
|
754 if self._class then set_class(db_result, self._class) end
|
jbe/bsw@0
|
755 if self._attach then
|
jbe/bsw@0
|
756 attach(
|
jbe/bsw@0
|
757 self._attach.mode,
|
jbe/bsw@0
|
758 db_result,
|
jbe/bsw@0
|
759 self._attach.data2,
|
jbe/bsw@0
|
760 self._attach.field1,
|
jbe/bsw@0
|
761 self._attach.field2,
|
jbe/bsw@0
|
762 self._attach.ref1,
|
jbe/bsw@0
|
763 self._attach.ref2
|
jbe/bsw@0
|
764 )
|
jbe/bsw@0
|
765 end
|
jbe/bsw@0
|
766 return nil, db_result
|
jbe/bsw@0
|
767 else
|
jbe/bsw@0
|
768 return nil
|
jbe/bsw@0
|
769 end
|
jbe/bsw@0
|
770 end
|
jbe@23
|
771 --//--
|
jbe/bsw@0
|
772
|
jbe@23
|
773 --[[--
|
jbe@23
|
774 result = -- database result list or object
|
jbe@23
|
775 <db_selector>:exec()
|
jbe@23
|
776
|
jbe@23
|
777 This method executes the selector on its database. The result list or object is returned on success, otherwise an error is thrown.
|
jbe@23
|
778
|
jbe@23
|
779 --]]--
|
jbe/bsw@0
|
780 function selector_prototype:exec()
|
jbe/bsw@0
|
781 local db_error, result = self:try_exec()
|
jbe/bsw@0
|
782 if db_error then
|
jbe/bsw@0
|
783 db_error:escalate()
|
jbe/bsw@0
|
784 else
|
jbe/bsw@0
|
785 return result
|
jbe/bsw@0
|
786 end
|
jbe/bsw@0
|
787 end
|
jbe@23
|
788 --//--
|
jbe/bsw@0
|
789
|
jbe@23
|
790 --[[--
|
jbe@23
|
791 count = -- number of rows returned
|
jbe@23
|
792 <db_selector>:count()
|
jbe@23
|
793
|
jbe@23
|
794 This function wraps the given selector inside a subquery to count the number of rows returned by the database. NOTE: The result is cached inside the selector, thus the selector should NOT be modified afterwards.
|
jbe@23
|
795
|
jbe@23
|
796 --]]--
|
jbe/bsw@4
|
797 function selector_prototype:count()
|
jbe/bsw@4
|
798 if not self._count then
|
jbe/bsw@4
|
799 local count_selector = self:get_db_conn():new_selector()
|
jbe/bsw@4
|
800 count_selector:add_field('count(1)')
|
jbe/bsw@4
|
801 count_selector:add_from(self)
|
jbe/bsw@4
|
802 count_selector:single_object_mode()
|
jbe/bsw@4
|
803 self._count = count_selector:exec().count
|
jbe/bsw@4
|
804 end
|
jbe/bsw@4
|
805 return self._count
|
jbe/bsw@4
|
806 end
|
jbe@23
|
807 --//--
|
jbe/bsw@4
|
808
|
jbe/bsw@0
|
809
|
jbe/bsw@0
|
810
|
jbe/bsw@0
|
811 -----------------
|
jbe/bsw@0
|
812 -- attachments --
|
jbe/bsw@0
|
813 -----------------
|
jbe/bsw@0
|
814
|
jbe/bsw@0
|
815 local function attach_key(row, fields)
|
jbe/bsw@0
|
816 local t = type(fields)
|
jbe/bsw@0
|
817 if t == "string" then
|
jbe/bsw@0
|
818 return tostring(row[fields])
|
jbe/bsw@0
|
819 elseif t == "table" then
|
jbe/bsw@0
|
820 local r = {}
|
jbe/bsw@0
|
821 for idx, field in ipairs(fields) do
|
jbe/bsw@0
|
822 r[idx] = string.format("%q", row[field])
|
jbe/bsw@0
|
823 end
|
jbe/bsw@0
|
824 return table.concat(r)
|
jbe/bsw@0
|
825 else
|
jbe/bsw@0
|
826 error("Field information for 'mondelefant.attach' is neither a string nor a table.")
|
jbe/bsw@0
|
827 end
|
jbe/bsw@0
|
828 end
|
jbe/bsw@0
|
829
|
jbe@23
|
830 --[[--
|
jbe@23
|
831 mondelefant.attach(
|
jbe@23
|
832 mode, -- attachment type: "11" one to one, "1m" one to many, "m1" many to one
|
jbe@23
|
833 data1, -- first database result list or object
|
jbe@23
|
834 data2, -- second database result list or object
|
jbe@23
|
835 key1, -- field name(s) in first result list or object used for attaching
|
jbe@23
|
836 key2, -- field name(s) in second result list or object used for attaching
|
jbe@23
|
837 ref1, -- name of reference field to be set in first database result list or object
|
jbe@23
|
838 ref2 -- name of reference field to be set in second database result list or object
|
jbe@23
|
839 )
|
jbe@23
|
840
|
jbe@23
|
841 This function attaches database result lists/objects with each other. It does not need to be called directly.
|
jbe@23
|
842
|
jbe@23
|
843 --]]--
|
jbe/bsw@0
|
844 function attach(mode, data1, data2, key1, key2, ref1, ref2)
|
jbe/bsw@0
|
845 local many1, many2
|
jbe/bsw@0
|
846 if mode == "11" then
|
jbe/bsw@0
|
847 many1 = false
|
jbe/bsw@0
|
848 many2 = false
|
jbe/bsw@0
|
849 elseif mode == "1m" then
|
jbe/bsw@0
|
850 many1 = false
|
jbe/bsw@0
|
851 many2 = true
|
jbe/bsw@0
|
852 elseif mode == "m1" then
|
jbe/bsw@0
|
853 many1 = true
|
jbe/bsw@0
|
854 many2 = false
|
jbe/bsw@0
|
855 elseif mode == "mm" then
|
jbe/bsw@0
|
856 many1 = true
|
jbe/bsw@0
|
857 many2 = true
|
jbe/bsw@0
|
858 else
|
jbe/bsw@0
|
859 error("Unknown mode specified for 'mondelefant.attach'.")
|
jbe/bsw@0
|
860 end
|
jbe/bsw@0
|
861 local list1, list2
|
jbe/bsw@0
|
862 if data1._type == "object" then
|
jbe/bsw@0
|
863 list1 = { data1 }
|
jbe/bsw@0
|
864 elseif data1._type == "list" then
|
jbe/bsw@0
|
865 list1 = data1
|
jbe/bsw@0
|
866 else
|
jbe/bsw@0
|
867 error("First result data given to 'mondelefant.attach' is invalid.")
|
jbe/bsw@0
|
868 end
|
jbe/bsw@0
|
869 if data2._type == "object" then
|
jbe/bsw@0
|
870 list2 = { data2 }
|
jbe/bsw@0
|
871 elseif data2._type == "list" then
|
jbe/bsw@0
|
872 list2 = data2
|
jbe/bsw@0
|
873 else
|
jbe/bsw@0
|
874 error("Second result data given to 'mondelefant.attach' is invalid.")
|
jbe/bsw@0
|
875 end
|
jbe/bsw@0
|
876 local hash1 = {}
|
jbe/bsw@0
|
877 local hash2 = {}
|
jbe/bsw@0
|
878 if ref2 then
|
jbe/bsw@0
|
879 for i, row in ipairs(list1) do
|
jbe/bsw@0
|
880 local key = attach_key(row, key1)
|
jbe/bsw@0
|
881 local list = hash1[key]
|
jbe/bsw@0
|
882 if not list then list = {}; hash1[key] = list end
|
jbe/bsw@0
|
883 list[#list + 1] = row
|
jbe/bsw@0
|
884 end
|
jbe/bsw@0
|
885 end
|
jbe/bsw@0
|
886 if ref1 then
|
jbe/bsw@0
|
887 for i, row in ipairs(list2) do
|
jbe/bsw@0
|
888 local key = attach_key(row, key2)
|
jbe/bsw@0
|
889 local list = hash2[key]
|
jbe/bsw@0
|
890 if not list then list = {}; hash2[key] = list end
|
jbe/bsw@0
|
891 list[#list + 1] = row
|
jbe/bsw@0
|
892 end
|
jbe/bsw@0
|
893 for i, row in ipairs(list1) do
|
jbe/bsw@0
|
894 local key = attach_key(row, key1)
|
jbe/bsw@0
|
895 local matching_rows = hash2[key]
|
jbe/bsw@0
|
896 if many2 then
|
jbe/bsw@0
|
897 local list = data2._connection:create_list(matching_rows)
|
jbe/bsw@0
|
898 list._class = data2._class
|
jbe/bsw@0
|
899 row._ref[ref1] = list
|
jbe/bsw@0
|
900 elseif matching_rows and #matching_rows == 1 then
|
jbe/bsw@0
|
901 row._ref[ref1] = matching_rows[1]
|
jbe/bsw@0
|
902 else
|
jbe/bsw@0
|
903 row._ref[ref1] = false
|
jbe/bsw@0
|
904 end
|
jbe/bsw@0
|
905 end
|
jbe/bsw@0
|
906 end
|
jbe/bsw@0
|
907 if ref2 then
|
jbe/bsw@0
|
908 for i, row in ipairs(list2) do
|
jbe/bsw@0
|
909 local key = attach_key(row, key2)
|
jbe/bsw@0
|
910 local matching_rows = hash1[key]
|
jbe/bsw@0
|
911 if many1 then
|
jbe/bsw@0
|
912 local list = data1._connection:create_list(matching_rows)
|
jbe/bsw@0
|
913 list._class = data1._class
|
jbe/bsw@0
|
914 row._ref[ref2] = list
|
jbe/bsw@0
|
915 elseif matching_rows and #matching_rows == 1 then
|
jbe/bsw@0
|
916 row._ref[ref2] = matching_rows[1]
|
jbe/bsw@0
|
917 else
|
jbe/bsw@0
|
918 row._ref[ref2] = false
|
jbe/bsw@0
|
919 end
|
jbe/bsw@0
|
920 end
|
jbe/bsw@0
|
921 end
|
jbe/bsw@0
|
922 end
|
jbe@23
|
923 --//--
|
jbe/bsw@0
|
924
|
jbe/bsw@0
|
925
|
jbe/bsw@0
|
926
|
jbe/bsw@0
|
927 ------------------
|
jbe/bsw@0
|
928 -- model system --
|
jbe/bsw@0
|
929 ------------------
|
jbe/bsw@0
|
930
|
jbe@23
|
931 --[[--
|
jbe@23
|
932 <db_class>.primary_key
|
jbe@23
|
933
|
jbe@23
|
934 Primary key of a database class (model). Defaults to "id".
|
jbe@23
|
935
|
jbe@376
|
936 If the primary key is a tuple, then a sequence (table with integer keys mapped to the column names) must be used. If the primary key is contained in a JSON document within a table column, then a special object with the following fields is expected: {json_doc = "column_name", key = "field_name_within_json_object", type = "postgresql_type"}.
|
jbe@376
|
937
|
jbe@23
|
938 --]]--
|
jbe/bsw@0
|
939 class_prototype.primary_key = "id"
|
jbe@23
|
940 --//--
|
jbe/bsw@0
|
941
|
jbe@23
|
942 --[[--
|
jbe@376
|
943 <db_class>.document_column
|
jbe@376
|
944
|
jbe@376
|
945 Optional column name to redirect key lookups to. This can be used to allow for an easier access to fields of a JSON document.
|
jbe@376
|
946
|
jbe@376
|
947 --]]--
|
jbe@376
|
948 class_prototype.document_column = nil
|
jbe@376
|
949 --//--
|
jbe@376
|
950
|
jbe@376
|
951 --[[--
|
jbe@23
|
952 db_handle = -- database connection handle used by this class
|
jbe@23
|
953 <db_class>:get_db_conn()
|
jbe@23
|
954
|
jbe@23
|
955 By implementing this method for a particular model or overwriting it in the default prototype "mondelefant.class_prototype", classes are connected with a particular database. This method needs to return a database connection handle. If it is not overwritten, an error is thrown, when invoking this method.
|
jbe@23
|
956
|
jbe@23
|
957 --]]--
|
jbe/bsw@0
|
958 function class_prototype:get_db_conn()
|
jbe/bsw@0
|
959 error(
|
jbe/bsw@0
|
960 "Method mondelefant class(_prototype):get_db_conn() " ..
|
jbe/bsw@0
|
961 "has to be implemented."
|
jbe/bsw@0
|
962 )
|
jbe/bsw@0
|
963 end
|
jbe@23
|
964 --//--
|
jbe/bsw@0
|
965
|
jbe@23
|
966 --[[--
|
jbe@23
|
967 string = -- string of form '"schemaname"."tablename"' or '"tablename"'
|
jbe@23
|
968 <db_class>:get_qualified_table()
|
jbe@23
|
969
|
jbe@23
|
970 This method returns a string with the (double quoted) qualified table name used to store objects of this class.
|
jbe@23
|
971
|
jbe@23
|
972 --]]--
|
jbe/bsw@0
|
973 function class_prototype:get_qualified_table()
|
jbe/bsw@0
|
974 if not self.table then error "Table unknown." end
|
jbe/bsw@0
|
975 if self.schema then
|
jbe/bsw@0
|
976 return '"' .. self.schema .. '"."' .. self.table .. '"'
|
jbe/bsw@0
|
977 else
|
jbe/bsw@0
|
978 return '"' .. self.table .. '"'
|
jbe/bsw@0
|
979 end
|
jbe/bsw@0
|
980 end
|
jbe@23
|
981 --]]--
|
jbe/bsw@0
|
982
|
jbe@23
|
983 --[[--
|
jbe@23
|
984 string = -- single quoted string of form "'schemaname.tablename'" or "'tablename'"
|
jbe@23
|
985 <db_class>:get_qualified_table_literal()
|
jbe@23
|
986
|
jbe@23
|
987 This method returns a string with an SQL literal representing the given table. It causes ambiguities when the table name contains a dot (".") character.
|
jbe@23
|
988
|
jbe@23
|
989 --]]--
|
jbe/bsw@0
|
990 function class_prototype:get_qualified_table_literal()
|
jbe/bsw@0
|
991 if not self.table then error "Table unknown." end
|
jbe/bsw@0
|
992 if self.schema then
|
jbe/bsw@0
|
993 return self.schema .. '.' .. self.table
|
jbe/bsw@0
|
994 else
|
jbe/bsw@0
|
995 return self.table
|
jbe/bsw@0
|
996 end
|
jbe/bsw@0
|
997 end
|
jbe@23
|
998 --//--
|
jbe/bsw@0
|
999
|
jbe@23
|
1000 --[[--
|
jbe@23
|
1001 list = -- list of column names of primary key
|
jbe@23
|
1002 <db_class>:get_primary_key_list()
|
jbe@23
|
1003
|
jbe@23
|
1004 This method returns a list of column names of the primary key.
|
jbe@23
|
1005
|
jbe@23
|
1006 --]]--
|
jbe/bsw@0
|
1007 function class_prototype:get_primary_key_list()
|
jbe/bsw@0
|
1008 local primary_key = self.primary_key
|
jbe/bsw@0
|
1009 if type(primary_key) == "string" then
|
jbe/bsw@0
|
1010 return {primary_key}
|
jbe/bsw@0
|
1011 else
|
jbe/bsw@0
|
1012 return primary_key
|
jbe/bsw@0
|
1013 end
|
jbe/bsw@0
|
1014 end
|
jbe@23
|
1015 --//--
|
jbe/bsw@0
|
1016
|
jbe@23
|
1017 --[[--
|
jbe@23
|
1018 columns = -- list of columns
|
jbe@23
|
1019 <db_class>:get_columns()
|
jbe@23
|
1020
|
jbe@23
|
1021 This method returns a list of column names of the table used for the class.
|
jbe@23
|
1022
|
jbe@23
|
1023 --]]--
|
jbe/bsw@0
|
1024 function class_prototype:get_columns()
|
jbe/bsw@0
|
1025 if self._columns then
|
jbe/bsw@0
|
1026 return self._columns
|
jbe/bsw@0
|
1027 end
|
jbe/bsw@0
|
1028 local selector = self:get_db_conn():new_selector()
|
jbe/bsw@0
|
1029 selector:set_class(self)
|
jbe/bsw@0
|
1030 selector:from(self:get_qualified_table())
|
jbe/bsw@0
|
1031 selector:add_field("*")
|
jbe/bsw@0
|
1032 selector:add_where("FALSE")
|
jbe/bsw@0
|
1033 local db_result = selector:exec()
|
jbe/bsw@0
|
1034 local connection = db_result._connection
|
jbe/bsw@0
|
1035 local columns = {}
|
jbe/bsw@0
|
1036 for idx, info in ipairs(db_result._column_info) do
|
jbe/bsw@0
|
1037 local key = info.field_name
|
jbe/bsw@0
|
1038 local value = {
|
jbe/bsw@0
|
1039 name = key,
|
jbe/bsw@0
|
1040 type = connection.type_mappings[info.type]
|
jbe/bsw@0
|
1041 }
|
jbe/bsw@0
|
1042 columns[key] = value
|
jbe/bsw@0
|
1043 table.insert(columns, value)
|
jbe/bsw@0
|
1044 end
|
jbe/bsw@0
|
1045 self._columns = columns
|
jbe/bsw@0
|
1046 return columns
|
jbe/bsw@0
|
1047 end
|
jbe@25
|
1048 --//--
|
jbe/bsw@0
|
1049
|
jbe@23
|
1050 --[[--
|
jbe@23
|
1051 selector = -- new selector for selecting objects of this class
|
jbe@23
|
1052 <db_class>:new_selector(
|
jbe@23
|
1053 db_conn -- optional(!) database connection handle, defaults to result of :get_db_conn()
|
jbe@23
|
1054 )
|
jbe@23
|
1055
|
jbe@23
|
1056 This method creates a new selector for selecting objects of the class.
|
jbe@23
|
1057
|
jbe@23
|
1058 --]]--
|
jbe/bsw@0
|
1059 function class_prototype:new_selector(db_conn)
|
jbe/bsw@0
|
1060 local selector = (db_conn or self:get_db_conn()):new_selector()
|
jbe/bsw@0
|
1061 selector:set_class(self)
|
jbe/bsw@0
|
1062 selector:from(self:get_qualified_table())
|
jbe/bsw@0
|
1063 selector:add_field(self:get_qualified_table() .. ".*")
|
jbe/bsw@0
|
1064 return selector
|
jbe/bsw@0
|
1065 end
|
jbe@23
|
1066 --//--
|
jbe/bsw@0
|
1067
|
jbe@23
|
1068 --[[--
|
jbe@23
|
1069 db_list = -- database result being an empty list
|
jbe@23
|
1070 <db_class>:create_list()
|
jbe@23
|
1071
|
jbe@23
|
1072 Creates an empty database result representing a list of objects of the given class.
|
jbe@23
|
1073
|
jbe@23
|
1074 --]]--
|
jbe/bsw@0
|
1075 function class_prototype:create_list()
|
jbe/bsw@0
|
1076 local list = self:get_db_conn():create_list()
|
jbe/bsw@0
|
1077 list._class = self
|
jbe/bsw@0
|
1078 return list
|
jbe/bsw@0
|
1079 end
|
jbe@23
|
1080 --//--
|
jbe/bsw@0
|
1081
|
jbe@23
|
1082 --[[--
|
jbe@23
|
1083 db_object = -- database object (instance of model)
|
jbe@23
|
1084 <db_class>:new()
|
jbe@23
|
1085
|
jbe@23
|
1086 Creates a new object of the given class.
|
jbe@23
|
1087
|
jbe@23
|
1088 --]]--
|
jbe/bsw@0
|
1089 function class_prototype:new()
|
jbe/bsw@0
|
1090 local object = self:get_db_conn():create_object()
|
jbe/bsw@0
|
1091 object._class = self
|
jbe/bsw@0
|
1092 object._new = true
|
jbe/bsw@0
|
1093 return object
|
jbe/bsw@0
|
1094 end
|
jbe@23
|
1095 --//--
|
jbe/bsw@0
|
1096
|
jbe@23
|
1097 --[[--
|
jbe@464
|
1098 <db_object>:upsert_mode()
|
jbe@464
|
1099
|
jbe@464
|
1100 Enables UPSERT mode for an existing (new) database object. Note that only new objects can use the UPSERT mode, i.e. it is not possible to call this method on objects returned from a database query.
|
jbe@464
|
1101
|
jbe@464
|
1102 --]]--
|
jbe@464
|
1103 function class_prototype.object:upsert_mode()
|
jbe@464
|
1104 if not self._new then
|
jbe@464
|
1105 error("Upsert mode requires a new object and cannot be used on objects returned from a database query.")
|
jbe@464
|
1106 end
|
jbe@464
|
1107 self._upsert = true
|
jbe@464
|
1108 return self
|
jbe@464
|
1109 end
|
jbe@464
|
1110 --//--
|
jbe@464
|
1111
|
jbe@464
|
1112 --[[--
|
jbe@23
|
1113 db_error = -- database error object, or nil in case of success
|
jbe@23
|
1114 <db_object>:try_save()
|
jbe@23
|
1115
|
jbe@23
|
1116 This method saves changes to an object in the database. Returns nil on success, otherwise an error object is returned.
|
jbe@23
|
1117
|
jbe@23
|
1118 --]]--
|
jbe/bsw@0
|
1119 function class_prototype.object:try_save()
|
jbe/bsw@0
|
1120 if not self._class then
|
jbe/bsw@0
|
1121 error("Cannot save object: No class information available.")
|
jbe/bsw@0
|
1122 end
|
jbe/bsw@0
|
1123 local primary_key = self._class:get_primary_key_list()
|
jbe/bsw@0
|
1124 if self._new then
|
jbe/bsw@0
|
1125 local fields = {sep = ", "}
|
jbe/bsw@0
|
1126 local values = {sep = ", "}
|
jbe@375
|
1127 for key in pairs(self._dirty or {}) do
|
jbe/bsw@0
|
1128 add(fields, {'"$"', {key}})
|
jbe@437
|
1129 add(values, {'?', self._col[key]})
|
jbe/bsw@0
|
1130 end
|
jbe@464
|
1131 local upsert
|
jbe@464
|
1132 if self._upsert then
|
jbe@464
|
1133 local upsert_keys = {sep = ", "}
|
jbe@464
|
1134 if primary_key.json_doc then
|
jbe@464
|
1135 upsert_keys[1] = {
|
jbe@464
|
1136 '("$"->>?)::$',
|
jbe@464
|
1137 {primary_key.json_doc}, primary_key.key, {primary_key.type}
|
jbe@464
|
1138 }
|
jbe@464
|
1139 else
|
jbe@464
|
1140 for idx, value in ipairs(primary_key) do
|
jbe@464
|
1141 upsert_keys[idx] = {'"$"', {value}}
|
jbe@464
|
1142 end
|
jbe@464
|
1143 end
|
jbe@464
|
1144 local upsert_sets = {sep = ", "}
|
jbe@464
|
1145 for key in pairs(self._dirty) do
|
jbe@464
|
1146 add(upsert_sets, {'"$" = ?', {key}, self._col[key]})
|
jbe@464
|
1147 end
|
jbe@464
|
1148 upsert = {{ 'ON CONFLICT ($) DO UPDATE SET $ RETURNING', upsert_keys, upsert_sets }}
|
jbe@464
|
1149 else
|
jbe@464
|
1150 upsert = 'RETURNING'
|
jbe@464
|
1151 end
|
jbe@464
|
1152 local returning = { sep = ", " }
|
jbe@464
|
1153 if primary_key.json_doc then
|
jbe@464
|
1154 returning[1] = {
|
jbe@464
|
1155 '("$"->>?)::$ AS "json_key"',
|
jbe@464
|
1156 {primary_key.json_doc}, primary_key.key, {primary_key.type}
|
jbe@464
|
1157 }
|
jbe@464
|
1158 else
|
jbe@464
|
1159 for idx, value in ipairs(primary_key) do
|
jbe@464
|
1160 returning[idx] = '"' .. value .. '"'
|
jbe@464
|
1161 end
|
jbe@464
|
1162 end
|
jbe@373
|
1163 local db_error, db_result
|
jbe@373
|
1164 if #fields == 0 then
|
jbe@373
|
1165 db_error, db_result = self._connection:try_query(
|
jbe/bsw@0
|
1166 {
|
jbe@464
|
1167 'INSERT INTO $ DEFAULT VALUES $ $',
|
jbe@373
|
1168 {self._class:get_qualified_table()},
|
jbe@464
|
1169 upsert,
|
jbe@464
|
1170 returning
|
jbe@373
|
1171 },
|
jbe@373
|
1172 "object"
|
jbe@373
|
1173 )
|
jbe@373
|
1174 else
|
jbe@373
|
1175 db_error, db_result = self._connection:try_query(
|
jbe@373
|
1176 {
|
jbe@464
|
1177 'INSERT INTO $ ($) VALUES ($) $ $',
|
jbe/bsw@0
|
1178 {self._class:get_qualified_table()},
|
jbe/bsw@0
|
1179 fields,
|
jbe/bsw@0
|
1180 values,
|
jbe@464
|
1181 upsert,
|
jbe@464
|
1182 returning
|
jbe/bsw@0
|
1183 },
|
jbe/bsw@0
|
1184 "object"
|
jbe/bsw@0
|
1185 )
|
jbe@373
|
1186 end
|
jbe@373
|
1187 if db_error then
|
jbe@373
|
1188 return db_error
|
jbe@373
|
1189 end
|
jbe@373
|
1190 if primary_key.json_doc then
|
jbe@438
|
1191 self._col[primary_key.json_doc][primary_key.key] = db_result.json_key
|
jbe/bsw@0
|
1192 else
|
jbe/bsw@0
|
1193 for idx, value in ipairs(primary_key) do
|
jbe/bsw@0
|
1194 self[value] = db_result[value]
|
jbe/bsw@0
|
1195 end
|
jbe/bsw@0
|
1196 end
|
jbe@464
|
1197 self._new = false -- TODO: keep _new in case of upsert mode?
|
jbe/bsw@0
|
1198 else
|
jbe@464
|
1199 local update_sets = {sep = ", "}
|
jbe@375
|
1200 for key, mutability_state in pairs(self._dirty or {}) do
|
jbe@375
|
1201 if
|
jbe@375
|
1202 mutability_state == true or (
|
jbe@375
|
1203 verify_mutability_state and
|
jbe@437
|
1204 verify_mutability_state(self._col[key], mutability_state)
|
jbe@375
|
1205 )
|
jbe@375
|
1206 then
|
jbe@464
|
1207 add(update_sets, {'"$" = ?', {key}, self._col[key]})
|
jbe@375
|
1208 self._dirty[key] = true -- always dirty in case of later error
|
jbe@375
|
1209 end
|
jbe/bsw@0
|
1210 end
|
jbe@464
|
1211 if #update_sets >= 1 then
|
jbe/bsw@0
|
1212 local primary_key_compare = {sep = " AND "}
|
jbe@373
|
1213 if primary_key.json_doc then
|
jbe@373
|
1214 primary_key_compare[1] = {
|
jbe@373
|
1215 '("$"->>?)::$ = ?',
|
jbe@373
|
1216 {primary_key.json_doc}, primary_key.key, {primary_key.type},
|
jbe@438
|
1217 self._col[primary_key.json_doc][primary_key.key]
|
jbe/bsw@0
|
1218 }
|
jbe@373
|
1219 else
|
jbe@373
|
1220 for idx, value in ipairs(primary_key) do
|
jbe@373
|
1221 primary_key_compare[idx] = {
|
jbe@373
|
1222 "$ = ?",
|
jbe@373
|
1223 {'"' .. value .. '"'},
|
jbe@373
|
1224 self[value]
|
jbe@373
|
1225 }
|
jbe@373
|
1226 end
|
jbe/bsw@0
|
1227 end
|
jbe/bsw@0
|
1228 local db_error = self._connection:try_query{
|
jbe/bsw@0
|
1229 'UPDATE $ SET $ WHERE $',
|
jbe/bsw@0
|
1230 {self._class:get_qualified_table()},
|
jbe@464
|
1231 update_sets,
|
jbe/bsw@0
|
1232 primary_key_compare
|
jbe/bsw@0
|
1233 }
|
jbe/bsw@0
|
1234 if db_error then
|
jbe/bsw@0
|
1235 return db_error
|
jbe/bsw@0
|
1236 end
|
jbe/bsw@0
|
1237 end
|
jbe/bsw@0
|
1238 end
|
jbe@375
|
1239 for key in pairs(self._dirty or {}) do
|
jbe@375
|
1240 if save_mutability_state then
|
jbe@375
|
1241 self._dirty[key] =
|
jbe@437
|
1242 save_mutability_state and save_mutability_state(self._col[key]) or nil
|
jbe@375
|
1243 end
|
jbe@375
|
1244 end
|
jbe/bsw@0
|
1245 return nil
|
jbe/bsw@0
|
1246 end
|
jbe@23
|
1247 --//--
|
jbe/bsw@0
|
1248
|
jbe@23
|
1249 --[[--
|
jbe@23
|
1250 <db_object>:save()
|
jbe@23
|
1251
|
jbe@23
|
1252 This method saves changes to an object in the database. Throws error, unless successful.
|
jbe@23
|
1253
|
jbe@23
|
1254 --]]--
|
jbe/bsw@0
|
1255 function class_prototype.object:save()
|
jbe/bsw@0
|
1256 local db_error = self:try_save()
|
jbe/bsw@0
|
1257 if db_error then
|
jbe/bsw@0
|
1258 db_error:escalate()
|
jbe/bsw@0
|
1259 end
|
jbe/bsw@0
|
1260 return self
|
jbe/bsw@0
|
1261 end
|
jbe@23
|
1262 --//--
|
jbe/bsw@0
|
1263
|
jbe@23
|
1264 --[[--
|
jbe@23
|
1265 db_error = -- database error object, or nil in case of success
|
jbe@23
|
1266 <db_object>:try_destroy()
|
jbe@23
|
1267
|
jbe@23
|
1268 This method deletes an object in the database. Returns nil on success, otherwise an error object is returned.
|
jbe@23
|
1269
|
jbe@23
|
1270 --]]--
|
jbe/bsw@0
|
1271 function class_prototype.object:try_destroy()
|
jbe/bsw@0
|
1272 if not self._class then
|
jbe/bsw@0
|
1273 error("Cannot destroy object: No class information available.")
|
jbe/bsw@0
|
1274 end
|
jbe/bsw@0
|
1275 local primary_key = self._class:get_primary_key_list()
|
jbe/bsw@0
|
1276 local primary_key_compare = {sep = " AND "}
|
jbe@373
|
1277 if primary_key.json_doc then
|
jbe@373
|
1278 primary_key_compare[1] = {
|
jbe@373
|
1279 '("$"->>?)::$ = ?',
|
jbe@373
|
1280 {primary_key.json_doc}, primary_key.key, {primary_key.type},
|
bsw@462
|
1281 self._col[primary_key.json_doc][primary_key.key]
|
jbe/bsw@0
|
1282 }
|
jbe@373
|
1283 else
|
jbe@373
|
1284 for idx, value in ipairs(primary_key) do
|
jbe@373
|
1285 primary_key_compare[idx] = {
|
jbe@373
|
1286 "$ = ?",
|
jbe@373
|
1287 {'"' .. value .. '"'},
|
jbe@373
|
1288 self[value]
|
jbe@373
|
1289 }
|
jbe@373
|
1290 end
|
jbe/bsw@0
|
1291 end
|
jbe/bsw@0
|
1292 return self._connection:try_query{
|
jbe/bsw@0
|
1293 'DELETE FROM $ WHERE $',
|
jbe/bsw@0
|
1294 {self._class:get_qualified_table()},
|
jbe/bsw@0
|
1295 primary_key_compare
|
jbe/bsw@0
|
1296 }
|
jbe/bsw@0
|
1297 end
|
jbe@23
|
1298 --//--
|
jbe/bsw@0
|
1299
|
jbe@23
|
1300 --[[--
|
jbe@23
|
1301 <db_object>:destroy()
|
jbe@23
|
1302
|
jbe@23
|
1303 This method deletes an object in the database. Throws error, unless successful.
|
jbe@23
|
1304
|
jbe@23
|
1305 --]]--
|
jbe/bsw@0
|
1306 function class_prototype.object:destroy()
|
jbe/bsw@0
|
1307 local db_error = self:try_destroy()
|
jbe/bsw@0
|
1308 if db_error then
|
jbe/bsw@0
|
1309 db_error:escalate()
|
jbe/bsw@0
|
1310 end
|
jbe/bsw@0
|
1311 return self
|
jbe/bsw@0
|
1312 end
|
jbe@23
|
1313 --//--
|
jbe/bsw@0
|
1314
|
jbe@23
|
1315 --[[--
|
jbe@23
|
1316 db_selector =
|
jbe@23
|
1317 <db_list>:get_reference_selector(
|
jbe@23
|
1318 ref_name, -- name of reference (e.g. "children")
|
jbe@23
|
1319 options, -- table options passed to the reference loader (e.g. { order = ... })
|
jbe@23
|
1320 ref_alias, -- optional alias for the reference (e.g. "ordered_children")
|
jbe@23
|
1321 back_ref_alias -- back reference name (e.g. "parent")
|
jbe@23
|
1322 )
|
jbe@23
|
1323
|
jbe@23
|
1324 This method returns a special selector for selecting referenced objects. It is prepared in a way, that on execution of the selector, all returned objects are attached with the objects of the existent list. The "ref" and "back_ref" arguments passed to "add_reference" are used for the attachment, unless aliases are given with "ref_alias" and "back_ref_alias". If "options" are set, these options are passed to the reference loader. The default reference loader supports only one option named "order". If "order" is set to nil, the default order is used, if "order" is set to false, no ORDER BY statment is included in the selector, otherwise the given expression is used for ordering.
|
jbe@23
|
1325
|
jbe@23
|
1326 This method is not only available for database result lists but also for database result objects.
|
jbe@23
|
1327
|
jbe@23
|
1328 --]]--
|
jbe/bsw@0
|
1329 function class_prototype.list:get_reference_selector(
|
jbe/bsw@0
|
1330 ref_name, options, ref_alias, back_ref_alias
|
jbe/bsw@0
|
1331 )
|
jbe/bsw@0
|
1332 local ref_info = self._class.references[ref_name]
|
jbe/bsw@0
|
1333 if not ref_info then
|
jbe/bsw@0
|
1334 error('Reference with name "' .. ref_name .. '" not found.')
|
jbe/bsw@0
|
1335 end
|
jbe/bsw@0
|
1336 local selector = ref_info.selector_generator(self, options or {})
|
jbe/bsw@0
|
1337 local mode = ref_info.mode
|
jbe/bsw@0
|
1338 if mode == "mm" or mode == "1m" then
|
jbe/bsw@0
|
1339 mode = "m1"
|
jbe/bsw@0
|
1340 elseif mode == "m1" then
|
jbe/bsw@0
|
1341 mode = "1m"
|
jbe/bsw@0
|
1342 end
|
jbe/bsw@0
|
1343 local ref_alias = ref_alias
|
jbe/bsw@0
|
1344 if ref_alias == false then
|
jbe/bsw@0
|
1345 ref_alias = nil
|
jbe/bsw@0
|
1346 elseif ref_alias == nil then
|
jbe/bsw@0
|
1347 ref_alias = ref_name
|
jbe/bsw@0
|
1348 end
|
jbe/bsw@0
|
1349 local back_ref_alias
|
jbe/bsw@0
|
1350 if back_ref_alias == false then
|
jbe/bsw@0
|
1351 back_ref_alias = nil
|
jbe/bsw@0
|
1352 elseif back_ref_alias == nil then
|
jbe/bsw@0
|
1353 back_ref_alias = ref_info.back_ref
|
jbe/bsw@0
|
1354 end
|
jbe/bsw@0
|
1355 selector:attach(
|
jbe/bsw@0
|
1356 mode,
|
jbe/bsw@0
|
1357 self,
|
jbe/bsw@0
|
1358 ref_info.that_key, ref_info.this_key,
|
jbe/bsw@0
|
1359 back_ref_alias or ref_info.back_ref, ref_alias or ref_name
|
jbe/bsw@0
|
1360 )
|
jbe/bsw@0
|
1361 return selector
|
jbe/bsw@0
|
1362 end
|
jbe@23
|
1363 --//--
|
jbe/bsw@0
|
1364
|
jbe@23
|
1365 --[[--
|
jbe@23
|
1366 db_list_or_object =
|
jbe@23
|
1367 <db_list>:load(
|
jbe@23
|
1368 ref_name, -- name of reference (e.g. "children")
|
jbe@23
|
1369 options, -- table options passed to the reference loader (e.g. { order = ... })
|
jbe@23
|
1370 ref_alias, -- optional alias for the reference (e.g. "ordered_children")
|
jbe@23
|
1371 back_ref_alias -- back reference name (e.g. "parent")
|
jbe@23
|
1372 )
|
jbe@23
|
1373
|
jbe@23
|
1374 This method loads referenced objects and attaches them with the objects of the existent list. The "ref" and "back_ref" arguments passed to "add_reference" are used for the attachment, unless aliases are given with "ref_alias" and "back_ref_alias". If "options" are set, these options are passed to the reference loader. The default reference loader supports only one option named "order". If "order" is set to nil, the default order is used, if "order" is set to false, no ORDER BY statment is included in the selector, otherwise the given expression is used for ordering.
|
jbe@23
|
1375
|
jbe@23
|
1376 This method is not only available for database result lists but also for database result objects.
|
jbe@23
|
1377
|
jbe@23
|
1378 --]]--
|
jbe/bsw@0
|
1379 function class_prototype.list.load(...)
|
jbe/bsw@0
|
1380 return class_prototype.list.get_reference_selector(...):exec()
|
jbe/bsw@0
|
1381 end
|
jbe@23
|
1382 --//--
|
jbe/bsw@0
|
1383
|
jbe@23
|
1384 --[[--
|
jbe@23
|
1385 db_object =
|
jbe@23
|
1386 <db_object>:get_reference_selector(
|
jbe@23
|
1387 ref_name, -- name of reference (e.g. "children")
|
jbe@23
|
1388 options, -- table options passed to the reference loader (e.g. { order = ... })
|
jbe@23
|
1389 ref_alias, -- optional alias for the reference (e.g. "ordered_children")
|
jbe@23
|
1390 back_ref_alias -- back reference name (e.g. "parent")
|
jbe@23
|
1391 )
|
jbe@23
|
1392
|
jbe@23
|
1393 This method returns a special selector for selecting referenced objects. It is prepared in a way, that on execution of the selector, all returned objects are attached with the objects of the existent list. The "ref" and "back_ref" arguments passed to "add_reference" are used for the attachment, unless aliases are given with "ref_alias" and "back_ref_alias". If "options" are set, these options are passed to the reference loader. The default reference loader supports only one option named "order". If "order" is set to nil, the default order is used, if "order" is set to false, no ORDER BY statment is included in the selector, otherwise the given expression is used for ordering.
|
jbe@23
|
1394
|
jbe@23
|
1395 This method is not only available for database result objects but also for database result lists.
|
jbe@23
|
1396
|
jbe@23
|
1397 --]]--
|
jbe/bsw@0
|
1398 function class_prototype.object:get_reference_selector(...)
|
jbe/bsw@0
|
1399 local list = self._class:create_list()
|
jbe/bsw@0
|
1400 list[1] = self
|
jbe/bsw@0
|
1401 return list:get_reference_selector(...)
|
jbe/bsw@0
|
1402 end
|
jbe@23
|
1403 --//--
|
jbe/bsw@0
|
1404
|
jbe@23
|
1405 --[[--
|
jbe@23
|
1406 db_list_or_object =
|
jbe@23
|
1407 <db_object>:load(
|
jbe@23
|
1408 ref_name, -- name of reference (e.g. "children")
|
jbe@23
|
1409 options, -- table options passed to the reference loader (e.g. { order = ... })
|
jbe@23
|
1410 ref_alias, -- optional alias for the reference (e.g. "ordered_children")
|
jbe@23
|
1411 back_ref_alias -- back reference name (e.g. "parent")
|
jbe@23
|
1412 )
|
jbe@23
|
1413
|
jbe@23
|
1414 This method loads referenced objects and attaches them with the objects of the existent list. The "ref" and "back_ref" arguments passed to "add_reference" are used for the attachment, unless aliases are given with "ref_alias" and "back_ref_alias". If "options" are set, these options are passed to the reference loader. The default reference loader supports only one option named "order". If "order" is set to nil, the default order is used, if "order" is set to false, no ORDER BY statment is included in the selector, otherwise the given expression is used for ordering.
|
jbe@23
|
1415
|
jbe@23
|
1416 This method is not only available for database result objects but also for database result lists. Calling this method for objects is unneccessary, unless additional options and/or an alias is used.
|
jbe@23
|
1417
|
jbe@23
|
1418 --]]--
|
jbe/bsw@0
|
1419 function class_prototype.object.load(...)
|
jbe/bsw@0
|
1420 return class_prototype.object.get_reference_selector(...):exec()
|
jbe/bsw@0
|
1421 end
|
jbe@23
|
1422 --//--
|
jbe/bsw@0
|
1423
|
jbe@23
|
1424 --[[--
|
jbe@23
|
1425 db_class = -- same class returned
|
jbe@23
|
1426 <db_class>:add_reference{
|
jbe@23
|
1427 mode = mode, -- "11", "1m", "m1", or "mm" (one/many to one/many)
|
jbe@23
|
1428 to = to, -- referenced class (model), optionally as string or function returning the value (avoids autoload)
|
jbe@23
|
1429 this_key = this_key, -- name of key in this class (model)
|
jbe@23
|
1430 that_key = that_key, -- name of key in the other class (model) ("to" argument)
|
jbe@23
|
1431 ref = ref, -- name of reference in this class, referring to the other class
|
jbe@23
|
1432 back_ref = back_ref, -- name of reference in other class, referring to this class
|
jbe@23
|
1433 default_order = default_order, -- expression as passed to "assemble_command" used for sorting
|
jbe@23
|
1434 selector_generator = selector_generator, -- alternative function used as selector generator (use only, when you know what you are doing)
|
jbe@23
|
1435 connected_by_table = connected_by_table, -- connecting table used for many to many relations
|
jbe@23
|
1436 connected_by_this_key = connected_by_this_key, -- key in connecting table referring to "this_key" of this class (model)
|
jbe@23
|
1437 connected_by_that_key = connected_by_that_key -- key in connecting table referring to "that_key" in other class (model) ("to" argument)
|
jbe@23
|
1438 }
|
jbe/bsw@0
|
1439
|
jbe@23
|
1440 Denotes a reference from one database class to another database class (model to model relation). There are 4 possible types of references: one-to-one (mode = "11"), one-to-many (mode = "1m"), many-to-one ("m1"), and many-to-many ("mm"). References usually should be defined in both models, which are related to each other, with mirrored mode (i.e. "1m" in one model, and "m1" in the other). One-to-one and one-to-many references may have a "back_ref" setting, which causes that loaded objects of the referenced class, refer back to the originating object. One-to-many and many-to-many references may have a "default_order" setting, which selects the default order for selected objects. When adding a many-to-many reference, the argument "connected_by_table", "connected_by_this_key" and "connected_by_that_key" must be set additionally.
|
jbe@23
|
1441
|
jbe@23
|
1442 --]]--
|
jbe/bsw@0
|
1443 function class_prototype:add_reference(args)
|
jbe/bsw@0
|
1444 local selector_generator = args.selector_generator
|
jbe/bsw@0
|
1445 local mode = args.mode
|
jbe/bsw@0
|
1446 local to = args.to
|
jbe/bsw@0
|
1447 local this_key = args.this_key
|
jbe/bsw@0
|
1448 local that_key = args.that_key
|
jbe/bsw@0
|
1449 local connected_by_table = args.connected_by_table -- TODO: split to table and schema
|
jbe/bsw@0
|
1450 local connected_by_this_key = args.connected_by_this_key
|
jbe/bsw@0
|
1451 local connected_by_that_key = args.connected_by_that_key
|
jbe/bsw@0
|
1452 local ref = args.ref
|
jbe/bsw@0
|
1453 local back_ref = args.back_ref
|
jbe/bsw@0
|
1454 local default_order = args.default_order
|
jbe/bsw@0
|
1455 local model
|
jbe/bsw@0
|
1456 local function get_model()
|
jbe/bsw@0
|
1457 if not model then
|
jbe/bsw@0
|
1458 if type(to) == "string" then
|
jbe/bsw@0
|
1459 model = _G
|
jbe/bsw@0
|
1460 for path_element in string.gmatch(to, "[^.]+") do
|
jbe/bsw@0
|
1461 model = model[path_element]
|
jbe/bsw@0
|
1462 end
|
jbe/bsw@0
|
1463 elseif type(to) == "function" then
|
jbe/bsw@0
|
1464 model = to()
|
jbe/bsw@0
|
1465 else
|
jbe/bsw@0
|
1466 model = to
|
jbe/bsw@0
|
1467 end
|
jbe/bsw@0
|
1468 end
|
jbe/bsw@0
|
1469 if not model or model == _G then
|
jbe/bsw@0
|
1470 error("Could not get model for reference.")
|
jbe/bsw@0
|
1471 end
|
jbe/bsw@0
|
1472 return model
|
jbe/bsw@0
|
1473 end
|
jbe/bsw@0
|
1474 self.references[ref] = {
|
jbe/bsw@0
|
1475 mode = mode,
|
jbe/bsw@0
|
1476 this_key = this_key,
|
jbe/bsw@0
|
1477 that_key = connected_by_table and "mm_ref_" or that_key,
|
jbe/bsw@0
|
1478 ref = ref,
|
jbe/bsw@0
|
1479 back_ref = back_ref,
|
jbe/bsw@0
|
1480 selector_generator = selector_generator or function(list, options)
|
jbe/bsw@0
|
1481 -- TODO: support tuple keys
|
jbe/bsw@0
|
1482 local options = options or {}
|
jbe/bsw@0
|
1483 local model = get_model()
|
jbe/bsw@0
|
1484 -- TODO: too many records cause PostgreSQL command stack overflow
|
jbe/bsw@0
|
1485 local ids = { sep = ", " }
|
jbe/bsw@0
|
1486 for i, object in ipairs(list) do
|
jbe/bsw@0
|
1487 local id = object[this_key]
|
jbe/bsw@0
|
1488 if id ~= nil then
|
jbe/bsw@0
|
1489 ids[#ids+1] = {"?", id}
|
jbe/bsw@0
|
1490 end
|
jbe/bsw@0
|
1491 end
|
jbe/bsw@0
|
1492 if #ids == 0 then
|
jbe/bsw@0
|
1493 return model:new_selector():empty_list_mode()
|
jbe/bsw@0
|
1494 end
|
jbe/bsw@0
|
1495 local selector = model:new_selector()
|
jbe/bsw@0
|
1496 if connected_by_table then
|
jbe/bsw@0
|
1497 selector:join(
|
jbe/bsw@0
|
1498 connected_by_table,
|
jbe/bsw@0
|
1499 nil,
|
jbe/bsw@0
|
1500 {
|
jbe/bsw@0
|
1501 '$."$" = $."$"',
|
jbe/bsw@0
|
1502 {connected_by_table},
|
jbe/bsw@0
|
1503 {connected_by_that_key},
|
jbe/bsw@0
|
1504 {model:get_qualified_table()},
|
jbe/bsw@0
|
1505 {that_key}
|
jbe/bsw@0
|
1506 }
|
jbe/bsw@0
|
1507 )
|
jbe/bsw@0
|
1508 selector:add_field(
|
jbe/bsw@0
|
1509 {
|
jbe/bsw@0
|
1510 '$."$"',
|
jbe/bsw@0
|
1511 {connected_by_table},
|
jbe/bsw@0
|
1512 {connected_by_this_key}
|
jbe/bsw@0
|
1513 },
|
jbe/bsw@0
|
1514 'mm_ref_'
|
jbe/bsw@0
|
1515 )
|
jbe/bsw@0
|
1516 selector:add_where{
|
jbe/bsw@0
|
1517 '$."$" IN ($)',
|
jbe/bsw@0
|
1518 {connected_by_table},
|
jbe/bsw@0
|
1519 {connected_by_this_key},
|
jbe/bsw@0
|
1520 ids
|
jbe/bsw@0
|
1521 }
|
jbe/bsw@0
|
1522 else
|
jbe/bsw@6
|
1523 selector:add_where{'$."$" IN ($)', {model:get_qualified_table()}, {that_key}, ids}
|
jbe/bsw@0
|
1524 end
|
jbe/bsw@0
|
1525 if options.order == nil and default_order then
|
jbe/bsw@0
|
1526 selector:add_order_by(default_order)
|
jbe/bsw@0
|
1527 elseif options.order then
|
jbe/bsw@0
|
1528 selector:add_order_by(options.order)
|
jbe/bsw@0
|
1529 end
|
jbe/bsw@0
|
1530 return selector
|
jbe/bsw@0
|
1531 end
|
jbe/bsw@0
|
1532 }
|
jbe/bsw@0
|
1533 if mode == "m1" or mode == "11" then
|
jbe/bsw@0
|
1534 self.foreign_keys[this_key] = ref
|
jbe/bsw@0
|
1535 end
|
jbe/bsw@0
|
1536 return self
|
jbe/bsw@0
|
1537 end
|
jbe@23
|
1538 --//--
|
jbe@23
|
1539
|
jbe@67
|
1540 return _M
|
jbe@67
|
1541
|