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/bsw@0
|
61 self._fields = { sep = ", " }
|
jbe/bsw@0
|
62 self._distinct = false
|
jbe/bsw@0
|
63 self._distinct_on = {sep = ", ", expression}
|
jbe/bsw@0
|
64 self._from = { sep = " " }
|
jbe/bsw@4
|
65 self._where = { sep = ") AND (" }
|
jbe/bsw@0
|
66 self._group_by = { sep = ", " }
|
jbe/bsw@4
|
67 self._having = { sep = ") AND (" }
|
jbe/bsw@0
|
68 self._combine = { sep = " " }
|
jbe/bsw@0
|
69 self._order_by = { sep = ", " }
|
jbe/bsw@0
|
70 self._limit = nil
|
jbe/bsw@0
|
71 self._offset = nil
|
jbe/bsw@4
|
72 self._read_lock = { sep = ", " }
|
jbe/bsw@4
|
73 self._write_lock = { sep = ", " }
|
jbe/bsw@0
|
74 self._class = nil
|
jbe/bsw@0
|
75 self._attach = nil
|
jbe/bsw@0
|
76 return self
|
jbe/bsw@0
|
77 end
|
jbe/bsw@0
|
78
|
jbe/bsw@0
|
79 function connection_prototype:new_selector()
|
jbe/bsw@0
|
80 return init_selector(setmetatable({}, selector_metatable), self)
|
jbe/bsw@0
|
81 end
|
jbe/bsw@0
|
82
|
jbe/bsw@0
|
83 function selector_prototype:get_db_conn()
|
jbe/bsw@0
|
84 return self._db_conn
|
jbe/bsw@0
|
85 end
|
jbe/bsw@0
|
86
|
jbe/bsw@0
|
87 -- TODO: selector clone?
|
jbe/bsw@0
|
88
|
jbe/bsw@0
|
89 function selector_prototype:single_object_mode()
|
jbe/bsw@0
|
90 self._mode = "object"
|
jbe/bsw@0
|
91 return self
|
jbe/bsw@0
|
92 end
|
jbe/bsw@0
|
93
|
jbe/bsw@0
|
94 function selector_prototype:optional_object_mode()
|
jbe/bsw@0
|
95 self._mode = "opt_object"
|
jbe/bsw@0
|
96 return self
|
jbe/bsw@0
|
97 end
|
jbe/bsw@0
|
98
|
jbe/bsw@0
|
99 function selector_prototype:empty_list_mode()
|
jbe/bsw@0
|
100 self._mode = "empty_list"
|
jbe/bsw@0
|
101 return self
|
jbe/bsw@0
|
102 end
|
jbe/bsw@0
|
103
|
jbe/bsw@0
|
104 function selector_prototype:add_distinct_on(expression)
|
jbe/bsw@0
|
105 if self._distinct then
|
jbe/bsw@0
|
106 error("Can not combine DISTINCT with DISTINCT ON.")
|
jbe/bsw@0
|
107 end
|
jbe/bsw@0
|
108 add(self._distinct_on, expression)
|
jbe/bsw@0
|
109 return self
|
jbe/bsw@0
|
110 end
|
jbe/bsw@0
|
111
|
jbe/bsw@0
|
112 function selector_prototype:set_distinct()
|
jbe/bsw@0
|
113 if #self._distinct_on > 0 then
|
jbe/bsw@0
|
114 error("Can not combine DISTINCT with DISTINCT ON.")
|
jbe/bsw@0
|
115 end
|
jbe/bsw@0
|
116 self._distinct = true
|
jbe/bsw@0
|
117 return self
|
jbe/bsw@0
|
118 end
|
jbe/bsw@0
|
119
|
jbe/bsw@0
|
120 function selector_prototype:add_from(expression, alias, condition)
|
jbe/bsw@0
|
121 local first = (#self._from == 0)
|
jbe/bsw@0
|
122 if not first then
|
jbe/bsw@0
|
123 if condition then
|
jbe/bsw@0
|
124 add(self._from, "INNER JOIN")
|
jbe/bsw@0
|
125 else
|
jbe/bsw@0
|
126 add(self._from, "CROSS JOIN")
|
jbe/bsw@0
|
127 end
|
jbe/bsw@0
|
128 end
|
jbe/bsw@0
|
129 if getmetatable(expression) == selector_metatable then
|
jbe/bsw@0
|
130 if alias then
|
jbe/bsw@0
|
131 add(self._from, {'($) AS "$"', {expression}, {alias}})
|
jbe/bsw@0
|
132 else
|
jbe/bsw@0
|
133 add(self._from, {'($) AS "subquery"', {expression}})
|
jbe/bsw@0
|
134 end
|
jbe/bsw@0
|
135 else
|
jbe/bsw@0
|
136 if alias then
|
jbe/bsw@0
|
137 add(self._from, {'$ AS "$"', {expression}, {alias}})
|
jbe/bsw@0
|
138 else
|
jbe/bsw@0
|
139 add(self._from, expression)
|
jbe/bsw@0
|
140 end
|
jbe/bsw@0
|
141 end
|
jbe/bsw@0
|
142 if condition then
|
jbe/bsw@0
|
143 if first then
|
jbe/bsw@0
|
144 self:condition(condition)
|
jbe/bsw@0
|
145 else
|
jbe/bsw@0
|
146 add(self._from, "ON")
|
jbe/bsw@0
|
147 add(self._from, condition)
|
jbe/bsw@0
|
148 end
|
jbe/bsw@0
|
149 end
|
jbe/bsw@0
|
150 return self
|
jbe/bsw@0
|
151 end
|
jbe/bsw@0
|
152
|
jbe/bsw@0
|
153 function selector_prototype:add_where(expression)
|
jbe/bsw@0
|
154 add(self._where, expression)
|
jbe/bsw@0
|
155 return self
|
jbe/bsw@0
|
156 end
|
jbe/bsw@0
|
157
|
jbe/bsw@0
|
158 function selector_prototype:add_group_by(expression)
|
jbe/bsw@0
|
159 add(self._group_by, expression)
|
jbe/bsw@0
|
160 return self
|
jbe/bsw@0
|
161 end
|
jbe/bsw@0
|
162
|
jbe/bsw@0
|
163 function selector_prototype:add_having(expression)
|
jbe/bsw@0
|
164 add(self._having, expression)
|
jbe/bsw@0
|
165 return self
|
jbe/bsw@0
|
166 end
|
jbe/bsw@0
|
167
|
jbe/bsw@0
|
168 function selector_prototype:add_combine(expression)
|
jbe/bsw@0
|
169 add(self._combine, expression)
|
jbe/bsw@0
|
170 return self
|
jbe/bsw@0
|
171 end
|
jbe/bsw@0
|
172
|
jbe/bsw@0
|
173 function selector_prototype:add_order_by(expression)
|
jbe/bsw@0
|
174 add(self._order_by, expression)
|
jbe/bsw@0
|
175 return self
|
jbe/bsw@0
|
176 end
|
jbe/bsw@0
|
177
|
jbe/bsw@0
|
178 function selector_prototype:limit(count)
|
jbe/bsw@0
|
179 if type(count) ~= "number" or count % 1 ~= 0 then
|
jbe/bsw@0
|
180 error("LIMIT must be an integer.")
|
jbe/bsw@0
|
181 end
|
jbe/bsw@0
|
182 self._limit = count
|
jbe/bsw@0
|
183 return self
|
jbe/bsw@0
|
184 end
|
jbe/bsw@0
|
185
|
jbe/bsw@0
|
186 function selector_prototype:offset(count)
|
jbe/bsw@0
|
187 if type(count) ~= "number" or count % 1 ~= 0 then
|
jbe/bsw@0
|
188 error("OFFSET must be an integer.")
|
jbe/bsw@0
|
189 end
|
jbe/bsw@0
|
190 self._offset = count
|
jbe/bsw@0
|
191 return self
|
jbe/bsw@0
|
192 end
|
jbe/bsw@0
|
193
|
jbe/bsw@4
|
194 function selector_prototype:for_share()
|
jbe/bsw@4
|
195 self._read_lock.all = true
|
jbe/bsw@4
|
196 return self
|
jbe/bsw@4
|
197 end
|
jbe/bsw@4
|
198
|
jbe/bsw@4
|
199 function selector_prototype:for_share_of(expression)
|
jbe/bsw@4
|
200 add(self._read_lock, expression)
|
jbe/bsw@4
|
201 return self
|
jbe/bsw@4
|
202 end
|
jbe/bsw@4
|
203
|
jbe/bsw@4
|
204 function selector_prototype:for_update()
|
jbe/bsw@4
|
205 self._write_lock.all = true
|
jbe/bsw@4
|
206 return self
|
jbe/bsw@4
|
207 end
|
jbe/bsw@4
|
208
|
jbe/bsw@4
|
209 function selector_prototype:for_update_of(expression)
|
jbe/bsw@4
|
210 add(self._write_lock, expression)
|
jbe/bsw@4
|
211 return self
|
jbe/bsw@4
|
212 end
|
jbe/bsw@4
|
213
|
jbe/bsw@0
|
214 function selector_prototype:reset_fields()
|
jbe/bsw@0
|
215 for idx in ipairs(self._fields) do
|
jbe/bsw@0
|
216 self._fields[idx] = nil
|
jbe/bsw@0
|
217 end
|
jbe/bsw@0
|
218 return self
|
jbe/bsw@0
|
219 end
|
jbe/bsw@0
|
220
|
jbe/bsw@0
|
221 function selector_prototype:add_field(expression, alias, options)
|
jbe/bsw@0
|
222 if alias then
|
jbe/bsw@0
|
223 add(self._fields, {'$ AS "$"', {expression}, {alias}})
|
jbe/bsw@0
|
224 else
|
jbe/bsw@0
|
225 add(self._fields, expression)
|
jbe/bsw@0
|
226 end
|
jbe/bsw@0
|
227 if options then
|
jbe/bsw@0
|
228 for i, option in ipairs(options) do
|
jbe/bsw@0
|
229 if option == "distinct" then
|
jbe/bsw@0
|
230 if alias then
|
jbe/bsw@0
|
231 self:add_distinct_on('"' .. alias .. '"')
|
jbe/bsw@0
|
232 else
|
jbe/bsw@0
|
233 self:add_distinct_on(expression)
|
jbe/bsw@0
|
234 end
|
jbe/bsw@0
|
235 elseif option == "grouped" then
|
jbe/bsw@0
|
236 if alias then
|
jbe/bsw@0
|
237 self:add_group_by('"' .. alias .. '"')
|
jbe/bsw@0
|
238 else
|
jbe/bsw@0
|
239 self:add_group_by(expression)
|
jbe/bsw@0
|
240 end
|
jbe/bsw@0
|
241 else
|
jbe/bsw@0
|
242 error("Unknown option '" .. option .. "' to add_field method.")
|
jbe/bsw@0
|
243 end
|
jbe/bsw@0
|
244 end
|
jbe/bsw@0
|
245 end
|
jbe/bsw@0
|
246 return self
|
jbe/bsw@0
|
247 end
|
jbe/bsw@0
|
248
|
jbe/bsw@0
|
249 function selector_prototype:join(...) -- NOTE: alias for add_from
|
jbe/bsw@0
|
250 return self:add_from(...)
|
jbe/bsw@0
|
251 end
|
jbe/bsw@0
|
252
|
jbe/bsw@0
|
253 function selector_prototype:from(expression, alias, condition)
|
jbe/bsw@0
|
254 if #self._from > 0 then
|
jbe/bsw@0
|
255 error("From-clause already existing (hint: try join).")
|
jbe/bsw@0
|
256 end
|
jbe/bsw@0
|
257 return self:join(expression, alias, condition)
|
jbe/bsw@0
|
258 end
|
jbe/bsw@0
|
259
|
jbe/bsw@0
|
260 function selector_prototype:left_join(expression, alias, condition)
|
jbe/bsw@0
|
261 local first = (#self._from == 0)
|
jbe/bsw@0
|
262 if not first then
|
jbe/bsw@0
|
263 add(self._from, "LEFT OUTER JOIN")
|
jbe/bsw@0
|
264 end
|
jbe/bsw@0
|
265 if alias then
|
jbe/bsw@0
|
266 add(self._from, {'$ AS "$"', {expression}, {alias}})
|
jbe/bsw@0
|
267 else
|
jbe/bsw@0
|
268 add(self._from, expression)
|
jbe/bsw@0
|
269 end
|
jbe/bsw@0
|
270 if condition then
|
jbe/bsw@0
|
271 if first then
|
jbe/bsw@0
|
272 self:condition(condition)
|
jbe/bsw@0
|
273 else
|
jbe/bsw@0
|
274 add(self._from, "ON")
|
jbe/bsw@0
|
275 add(self._from, condition)
|
jbe/bsw@0
|
276 end
|
jbe/bsw@0
|
277 end
|
jbe/bsw@0
|
278 return self
|
jbe/bsw@0
|
279 end
|
jbe/bsw@0
|
280
|
jbe/bsw@0
|
281 function selector_prototype:union(expression)
|
jbe/bsw@0
|
282 self:add_combine{"UNION $", {expression}}
|
jbe/bsw@0
|
283 return self
|
jbe/bsw@0
|
284 end
|
jbe/bsw@0
|
285
|
jbe/bsw@0
|
286 function selector_prototype:union_all(expression)
|
jbe/bsw@0
|
287 self:add_combine{"UNION ALL $", {expression}}
|
jbe/bsw@0
|
288 return self
|
jbe/bsw@0
|
289 end
|
jbe/bsw@0
|
290
|
jbe/bsw@0
|
291 function selector_prototype:intersect(expression)
|
jbe/bsw@0
|
292 self:add_combine{"INTERSECT $", {expression}}
|
jbe/bsw@0
|
293 return self
|
jbe/bsw@0
|
294 end
|
jbe/bsw@0
|
295
|
jbe/bsw@0
|
296 function selector_prototype:intersect_all(expression)
|
jbe/bsw@0
|
297 self:add_combine{"INTERSECT ALL $", {expression}}
|
jbe/bsw@0
|
298 return self
|
jbe/bsw@0
|
299 end
|
jbe/bsw@0
|
300
|
jbe/bsw@0
|
301 function selector_prototype:except(expression)
|
jbe/bsw@0
|
302 self:add_combine{"EXCEPT $", {expression}}
|
jbe/bsw@0
|
303 return self
|
jbe/bsw@0
|
304 end
|
jbe/bsw@0
|
305
|
jbe/bsw@0
|
306 function selector_prototype:except_all(expression)
|
jbe/bsw@0
|
307 self:add_combine{"EXCEPT ALL $", {expression}}
|
jbe/bsw@0
|
308 return self
|
jbe/bsw@0
|
309 end
|
jbe/bsw@0
|
310
|
jbe/bsw@0
|
311 function selector_prototype:set_class(class)
|
jbe/bsw@0
|
312 self._class = class
|
jbe/bsw@0
|
313 return self
|
jbe/bsw@0
|
314 end
|
jbe/bsw@0
|
315
|
jbe/bsw@0
|
316 function selector_prototype:attach(mode, data2, field1, field2, ref1, ref2)
|
jbe/bsw@0
|
317 self._attach = {
|
jbe/bsw@0
|
318 mode = mode,
|
jbe/bsw@0
|
319 data2 = data2,
|
jbe/bsw@0
|
320 field1 = field1,
|
jbe/bsw@0
|
321 field2 = field2,
|
jbe/bsw@0
|
322 ref1 = ref1,
|
jbe/bsw@0
|
323 ref2 = ref2
|
jbe/bsw@0
|
324 }
|
jbe/bsw@0
|
325 return self
|
jbe/bsw@0
|
326 end
|
jbe/bsw@0
|
327
|
jbe/bsw@0
|
328 -- TODO: many-to-many relations
|
jbe/bsw@0
|
329
|
jbe/bsw@0
|
330 function selector_metatable:__tostring()
|
jbe/bsw@0
|
331 local parts = {sep = " "}
|
jbe/bsw@0
|
332 add(parts, "SELECT")
|
jbe/bsw@0
|
333 if self._distinct then
|
jbe/bsw@0
|
334 add(parts, "DISTINCT")
|
jbe/bsw@0
|
335 elseif #self._distinct_on > 0 then
|
jbe/bsw@0
|
336 add(parts, {"DISTINCT ON ($)", self._distinct_on})
|
jbe/bsw@0
|
337 end
|
jbe/bsw@0
|
338 add(parts, {"$", self._fields})
|
jbe/bsw@0
|
339 if #self._from > 0 then
|
jbe/bsw@0
|
340 add(parts, {"FROM $", self._from})
|
jbe/bsw@0
|
341 end
|
jbe/bsw@0
|
342 if #self._mode == "empty_list" then
|
jbe/bsw@0
|
343 add(parts, "WHERE FALSE")
|
jbe/bsw@0
|
344 elseif #self._where > 0 then
|
jbe/bsw@4
|
345 add(parts, {"WHERE ($)", self._where})
|
jbe/bsw@0
|
346 end
|
jbe/bsw@0
|
347 if #self._group_by > 0 then
|
jbe/bsw@0
|
348 add(parts, {"GROUP BY $", self._group_by})
|
jbe/bsw@0
|
349 end
|
jbe/bsw@0
|
350 if #self._having > 0 then
|
jbe/bsw@4
|
351 add(parts, {"HAVING ($)", self._having})
|
jbe/bsw@0
|
352 end
|
jbe/bsw@0
|
353 for i, v in ipairs(self._combine) do
|
jbe/bsw@0
|
354 add(parts, v)
|
jbe/bsw@0
|
355 end
|
jbe/bsw@0
|
356 if #self._order_by > 0 then
|
jbe/bsw@0
|
357 add(parts, {"ORDER BY $", self._order_by})
|
jbe/bsw@0
|
358 end
|
jbe/bsw@0
|
359 if self._mode == "empty_list" then
|
jbe/bsw@0
|
360 add(parts, "LIMIT 0")
|
jbe/bsw@0
|
361 elseif self._mode ~= "list" then
|
jbe/bsw@0
|
362 add(parts, "LIMIT 1")
|
jbe/bsw@0
|
363 elseif self._limit then
|
jbe/bsw@0
|
364 add(parts, "LIMIT " .. self._limit)
|
jbe/bsw@0
|
365 end
|
jbe/bsw@0
|
366 if self._offset then
|
jbe/bsw@0
|
367 add(parts, "OFFSET " .. self._offset)
|
jbe/bsw@0
|
368 end
|
jbe/bsw@4
|
369 if self._write_lock.all then
|
jbe/bsw@4
|
370 add(parts, "FOR UPDATE")
|
jbe/bsw@4
|
371 else
|
jbe/bsw@4
|
372 if self._read_lock.all then
|
jbe/bsw@4
|
373 add(parts, "FOR SHARE")
|
jbe/bsw@4
|
374 elseif #self._read_lock > 0 then
|
jbe/bsw@4
|
375 add(parts, {"FOR SHARE OF $", self._read_lock})
|
jbe/bsw@4
|
376 end
|
jbe/bsw@4
|
377 if #self._write_lock > 0 then
|
jbe/bsw@4
|
378 add(parts, {"FOR UPDATE OF $", self._write_lock})
|
jbe/bsw@4
|
379 end
|
jbe/bsw@4
|
380 end
|
jbe/bsw@0
|
381 return self._db_conn:assemble_command{"$", parts}
|
jbe/bsw@0
|
382 end
|
jbe/bsw@0
|
383
|
jbe/bsw@0
|
384 function selector_prototype:try_exec()
|
jbe/bsw@0
|
385 if self._mode == "empty_list" then
|
jbe/bsw@0
|
386 if self._class then
|
jbe/bsw@0
|
387 return nil, self._class:create_list()
|
jbe/bsw@0
|
388 else
|
jbe/bsw@0
|
389 return nil, self._db_conn:create_list()
|
jbe/bsw@0
|
390 end
|
jbe/bsw@0
|
391 end
|
jbe/bsw@0
|
392 local db_error, db_result = self._db_conn:try_query(self, self._mode)
|
jbe/bsw@0
|
393 if db_error then
|
jbe/bsw@0
|
394 return db_error
|
jbe/bsw@0
|
395 elseif db_result then
|
jbe/bsw@0
|
396 if self._class then set_class(db_result, self._class) end
|
jbe/bsw@0
|
397 if self._attach then
|
jbe/bsw@0
|
398 attach(
|
jbe/bsw@0
|
399 self._attach.mode,
|
jbe/bsw@0
|
400 db_result,
|
jbe/bsw@0
|
401 self._attach.data2,
|
jbe/bsw@0
|
402 self._attach.field1,
|
jbe/bsw@0
|
403 self._attach.field2,
|
jbe/bsw@0
|
404 self._attach.ref1,
|
jbe/bsw@0
|
405 self._attach.ref2
|
jbe/bsw@0
|
406 )
|
jbe/bsw@0
|
407 end
|
jbe/bsw@0
|
408 return nil, db_result
|
jbe/bsw@0
|
409 else
|
jbe/bsw@0
|
410 return nil
|
jbe/bsw@0
|
411 end
|
jbe/bsw@0
|
412 end
|
jbe/bsw@0
|
413
|
jbe/bsw@0
|
414 function selector_prototype:exec()
|
jbe/bsw@0
|
415 local db_error, result = self:try_exec()
|
jbe/bsw@0
|
416 if db_error then
|
jbe/bsw@0
|
417 db_error:escalate()
|
jbe/bsw@0
|
418 else
|
jbe/bsw@0
|
419 return result
|
jbe/bsw@0
|
420 end
|
jbe/bsw@0
|
421 end
|
jbe/bsw@0
|
422
|
jbe/bsw@4
|
423 -- NOTE: This function caches the result!
|
jbe/bsw@4
|
424 function selector_prototype:count()
|
jbe/bsw@4
|
425 if not self._count then
|
jbe/bsw@4
|
426 local count_selector = self:get_db_conn():new_selector()
|
jbe/bsw@4
|
427 count_selector:add_field('count(1)')
|
jbe/bsw@4
|
428 count_selector:add_from(self)
|
jbe/bsw@4
|
429 count_selector:single_object_mode()
|
jbe/bsw@4
|
430 self._count = count_selector:exec().count
|
jbe/bsw@4
|
431 end
|
jbe/bsw@4
|
432 return self._count
|
jbe/bsw@4
|
433 end
|
jbe/bsw@4
|
434
|
jbe/bsw@0
|
435
|
jbe/bsw@0
|
436
|
jbe/bsw@0
|
437 -----------------
|
jbe/bsw@0
|
438 -- attachments --
|
jbe/bsw@0
|
439 -----------------
|
jbe/bsw@0
|
440
|
jbe/bsw@0
|
441 local function attach_key(row, fields)
|
jbe/bsw@0
|
442 local t = type(fields)
|
jbe/bsw@0
|
443 if t == "string" then
|
jbe/bsw@0
|
444 return tostring(row[fields])
|
jbe/bsw@0
|
445 elseif t == "table" then
|
jbe/bsw@0
|
446 local r = {}
|
jbe/bsw@0
|
447 for idx, field in ipairs(fields) do
|
jbe/bsw@0
|
448 r[idx] = string.format("%q", row[field])
|
jbe/bsw@0
|
449 end
|
jbe/bsw@0
|
450 return table.concat(r)
|
jbe/bsw@0
|
451 else
|
jbe/bsw@0
|
452 error("Field information for 'mondelefant.attach' is neither a string nor a table.")
|
jbe/bsw@0
|
453 end
|
jbe/bsw@0
|
454 end
|
jbe/bsw@0
|
455
|
jbe/bsw@0
|
456 function attach(mode, data1, data2, key1, key2, ref1, ref2)
|
jbe/bsw@0
|
457 local many1, many2
|
jbe/bsw@0
|
458 if mode == "11" then
|
jbe/bsw@0
|
459 many1 = false
|
jbe/bsw@0
|
460 many2 = false
|
jbe/bsw@0
|
461 elseif mode == "1m" then
|
jbe/bsw@0
|
462 many1 = false
|
jbe/bsw@0
|
463 many2 = true
|
jbe/bsw@0
|
464 elseif mode == "m1" then
|
jbe/bsw@0
|
465 many1 = true
|
jbe/bsw@0
|
466 many2 = false
|
jbe/bsw@0
|
467 elseif mode == "mm" then
|
jbe/bsw@0
|
468 many1 = true
|
jbe/bsw@0
|
469 many2 = true
|
jbe/bsw@0
|
470 else
|
jbe/bsw@0
|
471 error("Unknown mode specified for 'mondelefant.attach'.")
|
jbe/bsw@0
|
472 end
|
jbe/bsw@0
|
473 local list1, list2
|
jbe/bsw@0
|
474 if data1._type == "object" then
|
jbe/bsw@0
|
475 list1 = { data1 }
|
jbe/bsw@0
|
476 elseif data1._type == "list" then
|
jbe/bsw@0
|
477 list1 = data1
|
jbe/bsw@0
|
478 else
|
jbe/bsw@0
|
479 error("First result data given to 'mondelefant.attach' is invalid.")
|
jbe/bsw@0
|
480 end
|
jbe/bsw@0
|
481 if data2._type == "object" then
|
jbe/bsw@0
|
482 list2 = { data2 }
|
jbe/bsw@0
|
483 elseif data2._type == "list" then
|
jbe/bsw@0
|
484 list2 = data2
|
jbe/bsw@0
|
485 else
|
jbe/bsw@0
|
486 error("Second result data given to 'mondelefant.attach' is invalid.")
|
jbe/bsw@0
|
487 end
|
jbe/bsw@0
|
488 local hash1 = {}
|
jbe/bsw@0
|
489 local hash2 = {}
|
jbe/bsw@0
|
490 if ref2 then
|
jbe/bsw@0
|
491 for i, row in ipairs(list1) do
|
jbe/bsw@0
|
492 local key = attach_key(row, key1)
|
jbe/bsw@0
|
493 local list = hash1[key]
|
jbe/bsw@0
|
494 if not list then list = {}; hash1[key] = list end
|
jbe/bsw@0
|
495 list[#list + 1] = row
|
jbe/bsw@0
|
496 end
|
jbe/bsw@0
|
497 end
|
jbe/bsw@0
|
498 if ref1 then
|
jbe/bsw@0
|
499 for i, row in ipairs(list2) do
|
jbe/bsw@0
|
500 local key = attach_key(row, key2)
|
jbe/bsw@0
|
501 local list = hash2[key]
|
jbe/bsw@0
|
502 if not list then list = {}; hash2[key] = list end
|
jbe/bsw@0
|
503 list[#list + 1] = row
|
jbe/bsw@0
|
504 end
|
jbe/bsw@0
|
505 for i, row in ipairs(list1) do
|
jbe/bsw@0
|
506 local key = attach_key(row, key1)
|
jbe/bsw@0
|
507 local matching_rows = hash2[key]
|
jbe/bsw@0
|
508 if many2 then
|
jbe/bsw@0
|
509 local list = data2._connection:create_list(matching_rows)
|
jbe/bsw@0
|
510 list._class = data2._class
|
jbe/bsw@0
|
511 row._ref[ref1] = list
|
jbe/bsw@0
|
512 elseif matching_rows and #matching_rows == 1 then
|
jbe/bsw@0
|
513 row._ref[ref1] = matching_rows[1]
|
jbe/bsw@0
|
514 else
|
jbe/bsw@0
|
515 row._ref[ref1] = false
|
jbe/bsw@0
|
516 end
|
jbe/bsw@0
|
517 end
|
jbe/bsw@0
|
518 end
|
jbe/bsw@0
|
519 if ref2 then
|
jbe/bsw@0
|
520 for i, row in ipairs(list2) do
|
jbe/bsw@0
|
521 local key = attach_key(row, key2)
|
jbe/bsw@0
|
522 local matching_rows = hash1[key]
|
jbe/bsw@0
|
523 if many1 then
|
jbe/bsw@0
|
524 local list = data1._connection:create_list(matching_rows)
|
jbe/bsw@0
|
525 list._class = data1._class
|
jbe/bsw@0
|
526 row._ref[ref2] = list
|
jbe/bsw@0
|
527 elseif matching_rows and #matching_rows == 1 then
|
jbe/bsw@0
|
528 row._ref[ref2] = matching_rows[1]
|
jbe/bsw@0
|
529 else
|
jbe/bsw@0
|
530 row._ref[ref2] = false
|
jbe/bsw@0
|
531 end
|
jbe/bsw@0
|
532 end
|
jbe/bsw@0
|
533 end
|
jbe/bsw@0
|
534 end
|
jbe/bsw@0
|
535
|
jbe/bsw@0
|
536
|
jbe/bsw@0
|
537
|
jbe/bsw@0
|
538 ------------------
|
jbe/bsw@0
|
539 -- model system --
|
jbe/bsw@0
|
540 ------------------
|
jbe/bsw@0
|
541
|
jbe/bsw@0
|
542 class_prototype.primary_key = "id"
|
jbe/bsw@0
|
543
|
jbe/bsw@0
|
544 function class_prototype:get_db_conn()
|
jbe/bsw@0
|
545 error(
|
jbe/bsw@0
|
546 "Method mondelefant class(_prototype):get_db_conn() " ..
|
jbe/bsw@0
|
547 "has to be implemented."
|
jbe/bsw@0
|
548 )
|
jbe/bsw@0
|
549 end
|
jbe/bsw@0
|
550
|
jbe/bsw@0
|
551 function class_prototype:get_qualified_table()
|
jbe/bsw@0
|
552 if not self.table then error "Table unknown." end
|
jbe/bsw@0
|
553 if self.schema then
|
jbe/bsw@0
|
554 return '"' .. self.schema .. '"."' .. self.table .. '"'
|
jbe/bsw@0
|
555 else
|
jbe/bsw@0
|
556 return '"' .. self.table .. '"'
|
jbe/bsw@0
|
557 end
|
jbe/bsw@0
|
558 end
|
jbe/bsw@0
|
559
|
jbe/bsw@0
|
560 function class_prototype:get_qualified_table_literal()
|
jbe/bsw@0
|
561 if not self.table then error "Table unknown." end
|
jbe/bsw@0
|
562 if self.schema then
|
jbe/bsw@0
|
563 return self.schema .. '.' .. self.table
|
jbe/bsw@0
|
564 else
|
jbe/bsw@0
|
565 return self.table
|
jbe/bsw@0
|
566 end
|
jbe/bsw@0
|
567 end
|
jbe/bsw@0
|
568
|
jbe/bsw@0
|
569 function class_prototype:get_primary_key_list()
|
jbe/bsw@0
|
570 local primary_key = self.primary_key
|
jbe/bsw@0
|
571 if type(primary_key) == "string" then
|
jbe/bsw@0
|
572 return {primary_key}
|
jbe/bsw@0
|
573 else
|
jbe/bsw@0
|
574 return primary_key
|
jbe/bsw@0
|
575 end
|
jbe/bsw@0
|
576 end
|
jbe/bsw@0
|
577
|
jbe/bsw@0
|
578 function class_prototype:get_columns()
|
jbe/bsw@0
|
579 if self._columns then
|
jbe/bsw@0
|
580 return self._columns
|
jbe/bsw@0
|
581 end
|
jbe/bsw@0
|
582 local selector = self:get_db_conn():new_selector()
|
jbe/bsw@0
|
583 selector:set_class(self)
|
jbe/bsw@0
|
584 selector:from(self:get_qualified_table())
|
jbe/bsw@0
|
585 selector:add_field("*")
|
jbe/bsw@0
|
586 selector:add_where("FALSE")
|
jbe/bsw@0
|
587 local db_result = selector:exec()
|
jbe/bsw@0
|
588 local connection = db_result._connection
|
jbe/bsw@0
|
589 local columns = {}
|
jbe/bsw@0
|
590 for idx, info in ipairs(db_result._column_info) do
|
jbe/bsw@0
|
591 local key = info.field_name
|
jbe/bsw@0
|
592 local value = {
|
jbe/bsw@0
|
593 name = key,
|
jbe/bsw@0
|
594 type = connection.type_mappings[info.type]
|
jbe/bsw@0
|
595 }
|
jbe/bsw@0
|
596 columns[key] = value
|
jbe/bsw@0
|
597 table.insert(columns, value)
|
jbe/bsw@0
|
598 end
|
jbe/bsw@0
|
599 self._columns = columns
|
jbe/bsw@0
|
600 return columns
|
jbe/bsw@0
|
601 end
|
jbe/bsw@0
|
602
|
jbe/bsw@0
|
603 function class_prototype:new_selector(db_conn)
|
jbe/bsw@0
|
604 local selector = (db_conn or self:get_db_conn()):new_selector()
|
jbe/bsw@0
|
605 selector:set_class(self)
|
jbe/bsw@0
|
606 selector:from(self:get_qualified_table())
|
jbe/bsw@0
|
607 selector:add_field(self:get_qualified_table() .. ".*")
|
jbe/bsw@0
|
608 return selector
|
jbe/bsw@0
|
609 end
|
jbe/bsw@0
|
610
|
jbe/bsw@0
|
611 function class_prototype:create_list()
|
jbe/bsw@0
|
612 local list = self:get_db_conn():create_list()
|
jbe/bsw@0
|
613 list._class = self
|
jbe/bsw@0
|
614 return list
|
jbe/bsw@0
|
615 end
|
jbe/bsw@0
|
616
|
jbe/bsw@0
|
617 function class_prototype:new()
|
jbe/bsw@0
|
618 local object = self:get_db_conn():create_object()
|
jbe/bsw@0
|
619 object._class = self
|
jbe/bsw@0
|
620 object._new = true
|
jbe/bsw@0
|
621 return object
|
jbe/bsw@0
|
622 end
|
jbe/bsw@0
|
623
|
jbe/bsw@0
|
624 function class_prototype.object:try_save()
|
jbe/bsw@0
|
625 if not self._class then
|
jbe/bsw@0
|
626 error("Cannot save object: No class information available.")
|
jbe/bsw@0
|
627 end
|
jbe/bsw@0
|
628 local primary_key = self._class:get_primary_key_list()
|
jbe/bsw@0
|
629 local primary_key_sql = { sep = ", " }
|
jbe/bsw@0
|
630 for idx, value in ipairs(primary_key) do
|
jbe/bsw@0
|
631 primary_key_sql[idx] = '"' .. value .. '"'
|
jbe/bsw@0
|
632 end
|
jbe/bsw@0
|
633 if self._new then
|
jbe/bsw@0
|
634 local fields = {sep = ", "}
|
jbe/bsw@0
|
635 local values = {sep = ", "}
|
jbe/bsw@0
|
636 for key, dummy in pairs(self._dirty or {}) do
|
jbe/bsw@0
|
637 add(fields, {'"$"', {key}})
|
jbe/bsw@0
|
638 add(values, {'?', self[key]})
|
jbe/bsw@0
|
639 end
|
jbe/bsw@0
|
640 if compat_returning then -- compatibility for PostgreSQL 8.1
|
jbe/bsw@0
|
641 local db_error, db_result1, db_result2 = self._connection:try_query(
|
jbe/bsw@0
|
642 {
|
jbe/bsw@0
|
643 'INSERT INTO $ ($) VALUES ($)',
|
jbe/bsw@0
|
644 {self._class:get_qualified_table()},
|
jbe/bsw@0
|
645 fields,
|
jbe/bsw@0
|
646 values,
|
jbe/bsw@0
|
647 primary_key_sql
|
jbe/bsw@0
|
648 },
|
jbe/bsw@0
|
649 "list",
|
jbe/bsw@0
|
650 {
|
jbe/bsw@0
|
651 'SELECT currval(?)',
|
jbe/bsw@0
|
652 self._class.table .. '_id_seq'
|
jbe/bsw@0
|
653 },
|
jbe/bsw@0
|
654 "object"
|
jbe/bsw@0
|
655 )
|
jbe/bsw@0
|
656 if db_error then
|
jbe/bsw@0
|
657 return db_error
|
jbe/bsw@0
|
658 end
|
jbe/bsw@0
|
659 self.id = db_result2.id
|
jbe/bsw@0
|
660 else
|
jbe/bsw@0
|
661 local db_error, db_result = self._connection:try_query(
|
jbe/bsw@0
|
662 {
|
jbe/bsw@0
|
663 'INSERT INTO $ ($) VALUES ($) RETURNING ($)',
|
jbe/bsw@0
|
664 {self._class:get_qualified_table()},
|
jbe/bsw@0
|
665 fields,
|
jbe/bsw@0
|
666 values,
|
jbe/bsw@0
|
667 primary_key_sql
|
jbe/bsw@0
|
668 },
|
jbe/bsw@0
|
669 "object"
|
jbe/bsw@0
|
670 )
|
jbe/bsw@0
|
671 if db_error then
|
jbe/bsw@0
|
672 return db_error
|
jbe/bsw@0
|
673 end
|
jbe/bsw@0
|
674 for idx, value in ipairs(primary_key) do
|
jbe/bsw@0
|
675 self[value] = db_result[value]
|
jbe/bsw@0
|
676 end
|
jbe/bsw@0
|
677 end
|
jbe/bsw@0
|
678 self._new = false
|
jbe/bsw@0
|
679 else
|
jbe/bsw@0
|
680 local command_sets = {sep = ", "}
|
jbe/bsw@0
|
681 for key, dummy in pairs(self._dirty or {}) do
|
jbe/bsw@0
|
682 add(command_sets, {'"$" = ?', {key}, self[key]})
|
jbe/bsw@0
|
683 end
|
jbe/bsw@0
|
684 if #command_sets >= 1 then
|
jbe/bsw@0
|
685 local primary_key_compare = {sep = " AND "}
|
jbe/bsw@0
|
686 for idx, value in ipairs(primary_key) do
|
jbe/bsw@0
|
687 primary_key_compare[idx] = {
|
jbe/bsw@0
|
688 "$ = ?",
|
jbe/bsw@0
|
689 {'"' .. value .. '"'},
|
jbe/bsw@0
|
690 self[value]
|
jbe/bsw@0
|
691 }
|
jbe/bsw@0
|
692 end
|
jbe/bsw@0
|
693 local db_error = self._connection:try_query{
|
jbe/bsw@0
|
694 'UPDATE $ SET $ WHERE $',
|
jbe/bsw@0
|
695 {self._class:get_qualified_table()},
|
jbe/bsw@0
|
696 command_sets,
|
jbe/bsw@0
|
697 primary_key_compare
|
jbe/bsw@0
|
698 }
|
jbe/bsw@0
|
699 if db_error then
|
jbe/bsw@0
|
700 return db_error
|
jbe/bsw@0
|
701 end
|
jbe/bsw@0
|
702 end
|
jbe/bsw@0
|
703 end
|
jbe/bsw@0
|
704 return nil
|
jbe/bsw@0
|
705 end
|
jbe/bsw@0
|
706
|
jbe/bsw@0
|
707 function class_prototype.object:save()
|
jbe/bsw@0
|
708 local db_error = self:try_save()
|
jbe/bsw@0
|
709 if db_error then
|
jbe/bsw@0
|
710 db_error:escalate()
|
jbe/bsw@0
|
711 end
|
jbe/bsw@0
|
712 return self
|
jbe/bsw@0
|
713 end
|
jbe/bsw@0
|
714
|
jbe/bsw@0
|
715 function class_prototype.object:try_destroy()
|
jbe/bsw@0
|
716 if not self._class then
|
jbe/bsw@0
|
717 error("Cannot destroy object: No class information available.")
|
jbe/bsw@0
|
718 end
|
jbe/bsw@0
|
719 local primary_key = self._class:get_primary_key_list()
|
jbe/bsw@0
|
720 local primary_key_compare = {sep = " AND "}
|
jbe/bsw@0
|
721 for idx, value in ipairs(primary_key) do
|
jbe/bsw@0
|
722 primary_key_compare[idx] = {
|
jbe/bsw@0
|
723 "$ = ?",
|
jbe/bsw@0
|
724 {'"' .. value .. '"'},
|
jbe/bsw@0
|
725 self[value]
|
jbe/bsw@0
|
726 }
|
jbe/bsw@0
|
727 end
|
jbe/bsw@0
|
728 return self._connection:try_query{
|
jbe/bsw@0
|
729 'DELETE FROM $ WHERE $',
|
jbe/bsw@0
|
730 {self._class:get_qualified_table()},
|
jbe/bsw@0
|
731 primary_key_compare
|
jbe/bsw@0
|
732 }
|
jbe/bsw@0
|
733 end
|
jbe/bsw@0
|
734
|
jbe/bsw@0
|
735 function class_prototype.object:destroy()
|
jbe/bsw@0
|
736 local db_error = self:try_destroy()
|
jbe/bsw@0
|
737 if db_error then
|
jbe/bsw@0
|
738 db_error:escalate()
|
jbe/bsw@0
|
739 end
|
jbe/bsw@0
|
740 return self
|
jbe/bsw@0
|
741 end
|
jbe/bsw@0
|
742
|
jbe/bsw@0
|
743 function class_prototype.list:get_reference_selector(
|
jbe/bsw@0
|
744 ref_name, options, ref_alias, back_ref_alias
|
jbe/bsw@0
|
745 )
|
jbe/bsw@0
|
746 local ref_info = self._class.references[ref_name]
|
jbe/bsw@0
|
747 if not ref_info then
|
jbe/bsw@0
|
748 error('Reference with name "' .. ref_name .. '" not found.')
|
jbe/bsw@0
|
749 end
|
jbe/bsw@0
|
750 local selector = ref_info.selector_generator(self, options or {})
|
jbe/bsw@0
|
751 local mode = ref_info.mode
|
jbe/bsw@0
|
752 if mode == "mm" or mode == "1m" then
|
jbe/bsw@0
|
753 mode = "m1"
|
jbe/bsw@0
|
754 elseif mode == "m1" then
|
jbe/bsw@0
|
755 mode = "1m"
|
jbe/bsw@0
|
756 end
|
jbe/bsw@0
|
757 local ref_alias = ref_alias
|
jbe/bsw@0
|
758 if ref_alias == false then
|
jbe/bsw@0
|
759 ref_alias = nil
|
jbe/bsw@0
|
760 elseif ref_alias == nil then
|
jbe/bsw@0
|
761 ref_alias = ref_name
|
jbe/bsw@0
|
762 end
|
jbe/bsw@0
|
763 local back_ref_alias
|
jbe/bsw@0
|
764 if back_ref_alias == false then
|
jbe/bsw@0
|
765 back_ref_alias = nil
|
jbe/bsw@0
|
766 elseif back_ref_alias == nil then
|
jbe/bsw@0
|
767 back_ref_alias = ref_info.back_ref
|
jbe/bsw@0
|
768 end
|
jbe/bsw@0
|
769 selector:attach(
|
jbe/bsw@0
|
770 mode,
|
jbe/bsw@0
|
771 self,
|
jbe/bsw@0
|
772 ref_info.that_key, ref_info.this_key,
|
jbe/bsw@0
|
773 back_ref_alias or ref_info.back_ref, ref_alias or ref_name
|
jbe/bsw@0
|
774 )
|
jbe/bsw@0
|
775 return selector
|
jbe/bsw@0
|
776 end
|
jbe/bsw@0
|
777
|
jbe/bsw@0
|
778 function class_prototype.list.load(...)
|
jbe/bsw@0
|
779 return class_prototype.list.get_reference_selector(...):exec()
|
jbe/bsw@0
|
780 end
|
jbe/bsw@0
|
781
|
jbe/bsw@0
|
782 function class_prototype.object:get_reference_selector(...)
|
jbe/bsw@0
|
783 local list = self._class:create_list()
|
jbe/bsw@0
|
784 list[1] = self
|
jbe/bsw@0
|
785 return list:get_reference_selector(...)
|
jbe/bsw@0
|
786 end
|
jbe/bsw@0
|
787
|
jbe/bsw@0
|
788 function class_prototype.object.load(...)
|
jbe/bsw@0
|
789 return class_prototype.object.get_reference_selector(...):exec()
|
jbe/bsw@0
|
790 end
|
jbe/bsw@0
|
791
|
jbe/bsw@0
|
792
|
jbe/bsw@0
|
793 function class_prototype:add_reference(args)
|
jbe/bsw@0
|
794 local selector_generator = args.selector_generator
|
jbe/bsw@0
|
795 local mode = args.mode
|
jbe/bsw@0
|
796 local to = args.to
|
jbe/bsw@0
|
797 local this_key = args.this_key
|
jbe/bsw@0
|
798 local that_key = args.that_key
|
jbe/bsw@0
|
799 local connected_by_table = args.connected_by_table -- TODO: split to table and schema
|
jbe/bsw@0
|
800 local connected_by_this_key = args.connected_by_this_key
|
jbe/bsw@0
|
801 local connected_by_that_key = args.connected_by_that_key
|
jbe/bsw@0
|
802 local ref = args.ref
|
jbe/bsw@0
|
803 local back_ref = args.back_ref
|
jbe/bsw@0
|
804 local default_order = args.default_order
|
jbe/bsw@0
|
805 local model
|
jbe/bsw@0
|
806 local function get_model()
|
jbe/bsw@0
|
807 if not model then
|
jbe/bsw@0
|
808 if type(to) == "string" then
|
jbe/bsw@0
|
809 model = _G
|
jbe/bsw@0
|
810 for path_element in string.gmatch(to, "[^.]+") do
|
jbe/bsw@0
|
811 model = model[path_element]
|
jbe/bsw@0
|
812 end
|
jbe/bsw@0
|
813 elseif type(to) == "function" then
|
jbe/bsw@0
|
814 model = to()
|
jbe/bsw@0
|
815 else
|
jbe/bsw@0
|
816 model = to
|
jbe/bsw@0
|
817 end
|
jbe/bsw@0
|
818 end
|
jbe/bsw@0
|
819 if not model or model == _G then
|
jbe/bsw@0
|
820 error("Could not get model for reference.")
|
jbe/bsw@0
|
821 end
|
jbe/bsw@0
|
822 return model
|
jbe/bsw@0
|
823 end
|
jbe/bsw@0
|
824 self.references[ref] = {
|
jbe/bsw@0
|
825 mode = mode,
|
jbe/bsw@0
|
826 this_key = this_key,
|
jbe/bsw@0
|
827 that_key = connected_by_table and "mm_ref_" or that_key,
|
jbe/bsw@0
|
828 ref = ref,
|
jbe/bsw@0
|
829 back_ref = back_ref,
|
jbe/bsw@0
|
830 selector_generator = selector_generator or function(list, options)
|
jbe/bsw@0
|
831 -- TODO: support tuple keys
|
jbe/bsw@0
|
832 local options = options or {}
|
jbe/bsw@0
|
833 local model = get_model()
|
jbe/bsw@0
|
834 -- TODO: too many records cause PostgreSQL command stack overflow
|
jbe/bsw@0
|
835 local ids = { sep = ", " }
|
jbe/bsw@0
|
836 for i, object in ipairs(list) do
|
jbe/bsw@0
|
837 local id = object[this_key]
|
jbe/bsw@0
|
838 if id ~= nil then
|
jbe/bsw@0
|
839 ids[#ids+1] = {"?", id}
|
jbe/bsw@0
|
840 end
|
jbe/bsw@0
|
841 end
|
jbe/bsw@0
|
842 if #ids == 0 then
|
jbe/bsw@0
|
843 return model:new_selector():empty_list_mode()
|
jbe/bsw@0
|
844 end
|
jbe/bsw@0
|
845 local selector = model:new_selector()
|
jbe/bsw@0
|
846 if connected_by_table then
|
jbe/bsw@0
|
847 selector:join(
|
jbe/bsw@0
|
848 connected_by_table,
|
jbe/bsw@0
|
849 nil,
|
jbe/bsw@0
|
850 {
|
jbe/bsw@0
|
851 '$."$" = $."$"',
|
jbe/bsw@0
|
852 {connected_by_table},
|
jbe/bsw@0
|
853 {connected_by_that_key},
|
jbe/bsw@0
|
854 {model:get_qualified_table()},
|
jbe/bsw@0
|
855 {that_key}
|
jbe/bsw@0
|
856 }
|
jbe/bsw@0
|
857 )
|
jbe/bsw@0
|
858 selector:add_field(
|
jbe/bsw@0
|
859 {
|
jbe/bsw@0
|
860 '$."$"',
|
jbe/bsw@0
|
861 {connected_by_table},
|
jbe/bsw@0
|
862 {connected_by_this_key}
|
jbe/bsw@0
|
863 },
|
jbe/bsw@0
|
864 'mm_ref_'
|
jbe/bsw@0
|
865 )
|
jbe/bsw@0
|
866 selector:add_where{
|
jbe/bsw@0
|
867 '$."$" IN ($)',
|
jbe/bsw@0
|
868 {connected_by_table},
|
jbe/bsw@0
|
869 {connected_by_this_key},
|
jbe/bsw@0
|
870 ids
|
jbe/bsw@0
|
871 }
|
jbe/bsw@0
|
872 else
|
jbe/bsw@6
|
873 selector:add_where{'$."$" IN ($)', {model:get_qualified_table()}, {that_key}, ids}
|
jbe/bsw@0
|
874 end
|
jbe/bsw@0
|
875 if options.order == nil and default_order then
|
jbe/bsw@0
|
876 selector:add_order_by(default_order)
|
jbe/bsw@0
|
877 elseif options.order then
|
jbe/bsw@0
|
878 selector:add_order_by(options.order)
|
jbe/bsw@0
|
879 end
|
jbe/bsw@0
|
880 return selector
|
jbe/bsw@0
|
881 end
|
jbe/bsw@0
|
882 }
|
jbe/bsw@0
|
883 if mode == "m1" or mode == "11" then
|
jbe/bsw@0
|
884 self.foreign_keys[this_key] = ref
|
jbe/bsw@0
|
885 end
|
jbe/bsw@0
|
886 return self
|
jbe/bsw@0
|
887 end
|