webmcp

annotate libraries/mondelefant/mondelefant.lua @ 39:56648d7917b1

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

Impressum / About Us