| 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 
 |