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