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