webmcp
annotate libraries/mondelefant/mondelefant.lua @ 6:5cba83b3f411
Version 1.0.6
Bugfix: class_prototype:add_reference{...} uses now qualified names in SQL queries to allow JOINs
Fixes in the documentation of slot.put_into and trace.debug
Bugfix: class_prototype:add_reference{...} uses now qualified names in SQL queries to allow JOINs
Fixes in the documentation of slot.put_into and trace.debug
author | jbe/bsw |
---|---|
date | Fri Jan 22 12:00:00 2010 +0100 (2010-01-22) |
parents | 5e32ef998acf |
children | 3a6fe8663b26 |
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 |