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