webmcp
annotate libraries/mondelefant/mondelefant.lua @ 2:72860d232f32
Version 1.0.2
Fixed bug with explicit garbage collection (requests > 256kB caused an error)
Views prefixed with an underscore can't be called externally
ui.paginate now displays the last page, if the selected page number is too high.
Fixed bug with explicit garbage collection (requests > 256kB caused an error)
Views prefixed with an underscore can't be called externally
ui.paginate now displays the last page, if the selected page number is too high.
| author | jbe/bsw |
|---|---|
| date | Thu Dec 10 12:00:00 2009 +0100 (2009-12-10) |
| parents | 9fdfb27f8e67 |
| children | 5e32ef998acf |
| 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 |