webmcp

changeset 23:3a6fe8663b26

Code cleanup and documentation added; Year in copyright notice changed to 2009-2010

Details:
- Changed quoting style in auth.openid.xrds_document{...}
- Fixed documentation for auth.openid.initiate{...}
- Added documentation for mondelefant
- Code-cleanup in mondelefant:
-- removed unneccessary lines "rows = PQntuples(res); cols = PQnfields(res);"
-- avoided extra copy of first argument (self) in mondelefant_conn_query
-- no rawget in meta-method "__index" of database result lists and objects
-- removed unreachable "return 0;" in meta-method "__newindex" of database result lists and objects
- Year in copyright notice changed to 2009-2010
- Version string changed to "1.1.1"
author jbe
date Fri Jun 04 19:00:34 2010 +0200 (2010-06-04)
parents c1f3eb9713a4
children 605488fbf809
files LICENSE doc/autodoc-footer.htmlpart doc/autodoc-header.htmlpart framework/cgi-bin/webmcp.lua framework/env/auth/openid/initiate.lua framework/env/auth/openid/xrds_document.lua libraries/mondelefant/mondelefant.lua libraries/mondelefant/mondelefant_native.autodoc.lua libraries/mondelefant/mondelefant_native.c
line diff
     1.1 --- a/LICENSE	Thu Apr 22 20:46:29 2010 +0200
     1.2 +++ b/LICENSE	Fri Jun 04 19:00:34 2010 +0200
     1.3 @@ -1,4 +1,4 @@
     1.4 -Copyright (c) 2009 Public Software Group e. V., Berlin, Germany
     1.5 +Copyright (c) 2009-2010 Public Software Group e. V., Berlin, Germany
     1.6  
     1.7  Permission is hereby granted, free of charge, to any person obtaining a
     1.8  copy of this software and associated documentation files (the "Software"),
     2.1 --- a/doc/autodoc-footer.htmlpart	Thu Apr 22 20:46:29 2010 +0200
     2.2 +++ b/doc/autodoc-footer.htmlpart	Fri Jun 04 19:00:34 2010 +0200
     2.3 @@ -1,7 +1,7 @@
     2.4      </ul>
     2.5      <hr style="margin-top: 3em;"/>
     2.6      <p style="text-align: right; font-style: italic;">
     2.7 -      Copyright (c) 2009 Public Software Group e. V., Berlin
     2.8 +      Copyright (c) 2009-2010 Public Software Group e. V., Berlin
     2.9      </p>
    2.10    </body>
    2.11  </html>
     3.1 --- a/doc/autodoc-header.htmlpart	Thu Apr 22 20:46:29 2010 +0200
     3.2 +++ b/doc/autodoc-header.htmlpart	Fri Jun 04 19:00:34 2010 +0200
     3.3 @@ -55,10 +55,10 @@
     3.4          color: #505050;
     3.5        }
     3.6      </style>
     3.7 -    <title>WebMCP 1.1.0 Documentation</title>
     3.8 +    <title>WebMCP 1.1.1 Documentation</title>
     3.9    </head>
    3.10    <body>
    3.11 -    <h1>WebMCP 1.1.0 Documentation</h1>
    3.12 +    <h1>WebMCP 1.1.1 Documentation</h1>
    3.13      <p>
    3.14        WebMCP is a completely new web development framework, and has not been extensively tested yet. The API might change at any time, but in future releases there will be a list of all changes, which break downward compatibility.
    3.15      </p>
     4.1 --- a/framework/cgi-bin/webmcp.lua	Thu Apr 22 20:46:29 2010 +0200
     4.2 +++ b/framework/cgi-bin/webmcp.lua	Fri Jun 04 19:00:34 2010 +0200
     4.3 @@ -1,6 +1,6 @@
     4.4  #!/usr/bin/env lua
     4.5  
     4.6 -_WEBMCP_VERSION = "1.1.0"
     4.7 +_WEBMCP_VERSION = "1.1.1"
     4.8  
     4.9  -- include "../lib/" in search path for libraries
    4.10  do
     5.1 --- a/framework/env/auth/openid/initiate.lua	Thu Apr 22 20:46:29 2010 +0200
     5.2 +++ b/framework/env/auth/openid/initiate.lua	Fri Jun 04 19:00:34 2010 +0200
     5.3 @@ -1,6 +1,7 @@
     5.4  --[[--
     5.5  success,                                                -- boolean indicating success or failure
     5.6 -errmsg =                                                -- error message in case of failure (TODO: not implemented yet)
     5.7 +errmsg,                                                 -- error message in case of failure
     5.8 +errcode =                                               -- error code in case of failure (TODO: not implemented yet)
     5.9  auth.openid.initiate{
    5.10    user_supplied_identifier = user_supplied_identifier,  -- string given by user
    5.11    https_as_default         = https_as_default,          -- default to https
     6.1 --- a/framework/env/auth/openid/xrds_document.lua	Thu Apr 22 20:46:29 2010 +0200
     6.2 +++ b/framework/env/auth/openid/xrds_document.lua	Fri Jun 04 19:00:34 2010 +0200
     6.3 @@ -13,20 +13,20 @@
     6.4  function auth.openid.xrds_document(args)
     6.5    slot.set_layout(nil, "application/xrds+xml")
     6.6    slot.put_into("data",
     6.7 -    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n",
     6.8 -    "<xrds:XRDS xmlns:xrds=\"xri://$xrds\" xmlns=\"xri://$xrd*($v*2.0)\">\n",
     6.9 -    "  <XRD>\n",
    6.10 -    "    <Service>\n",                                                   
    6.11 -    "      <Type>http://specs.openid.net/auth/2.0/return_to</Type>\n",
    6.12 -    "      <URI>",
    6.13 +    '<?xml version="1.0" encoding="UTF-8"?>\n',
    6.14 +    '<xrds:XRDS xmlns:xrds="xri://$xrds" xmlns="xri://$xrd*($v*2.0)">\n',
    6.15 +    '  <XRD>\n',
    6.16 +    '    <Service>\n',                                                   
    6.17 +    '      <Type>http://specs.openid.net/auth/2.0/return_to</Type>\n',
    6.18 +    '      <URI>',
    6.19      encode.url{
    6.20        base   = request.get_absolute_baseurl(),
    6.21        module = args.return_to_module,
    6.22        view   = args.return_to_view
    6.23      },
    6.24 -    "</URI>\n",
    6.25 -    "    </Service>\n",
    6.26 -    "  </XRD>\n",
    6.27 -    "</xrds:XRDS>\n"
    6.28 +    '</URI>\n',
    6.29 +    '    </Service>\n',
    6.30 +    '  </XRD>\n',
    6.31 +    '</xrds:XRDS>\n'
    6.32    )
    6.33  end
     7.1 --- a/libraries/mondelefant/mondelefant.lua	Thu Apr 22 20:46:29 2010 +0200
     7.2 +++ b/libraries/mondelefant/mondelefant.lua	Fri Jun 04 19:00:34 2010 +0200
     7.3 @@ -76,31 +76,79 @@
     7.4    return self
     7.5  end
     7.6  
     7.7 +--[[--
     7.8 +selector =                  -- new selector
     7.9 +<db_handle>:new_selector()
    7.10 +
    7.11 +Creates a new selector to operate on the given database handle.
    7.12 +--]]--
    7.13  function connection_prototype:new_selector()
    7.14    return init_selector(setmetatable({}, selector_metatable), self)
    7.15  end
    7.16 +--//--
    7.17  
    7.18 +--[[--
    7.19 +db_handle =                  -- handle of database connection
    7.20 +<db_selector>:get_db_conn()
    7.21 +
    7.22 +Returns the database connection handle used by a selector.
    7.23 +
    7.24 +--]]--
    7.25  function selector_prototype:get_db_conn()
    7.26    return self._db_conn
    7.27  end
    7.28 +--//--
    7.29  
    7.30  -- TODO: selector clone?
    7.31  
    7.32 +--[[--
    7.33 +db_selector =                       -- same selector returned
    7.34 +<db_selector>:single_object_mode()
    7.35 +
    7.36 +Sets selector to single object mode (mode "object" passed to "query" method of database handle). The selector is modified and returned.
    7.37 +
    7.38 +--]]--
    7.39  function selector_prototype:single_object_mode()
    7.40    self._mode = "object"
    7.41    return self
    7.42  end
    7.43 +--//--
    7.44  
    7.45 +--[[--
    7.46 +db_selector =                         -- same selector returned
    7.47 +<db_selector>:optional_object_mode()
    7.48 +
    7.49 +Sets selector to single object mode (mode "opt_object" passed to "query" method of database handle). The selector is modified and returned.
    7.50 +
    7.51 +--]]--
    7.52  function selector_prototype:optional_object_mode()
    7.53    self._mode = "opt_object"
    7.54    return self
    7.55  end
    7.56 +--//--
    7.57  
    7.58 +--[[--
    7.59 +db_selector =                    -- same selector returned
    7.60 +<db_selector>:empty_list_mode()
    7.61 +
    7.62 +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.
    7.63 +
    7.64 +--]]--
    7.65  function selector_prototype:empty_list_mode()
    7.66    self._mode = "empty_list"
    7.67    return self
    7.68  end
    7.69 +--//--
    7.70  
    7.71 +--[[--
    7.72 +db_selector =                   -- same selector returned
    7.73 +<db_selector>:add_distinct_on(
    7.74 +  expression                    -- expression as passed to "assemble_command"
    7.75 +)
    7.76 +
    7.77 +Adds an DISTINCT ON expression to the selector. The selector is modified and returned.
    7.78 +
    7.79 +--]]--
    7.80  function selector_prototype:add_distinct_on(expression)
    7.81    if self._distinct then
    7.82      error("Can not combine DISTINCT with DISTINCT ON.")
    7.83 @@ -108,7 +156,15 @@
    7.84    add(self._distinct_on, expression)
    7.85    return self
    7.86  end
    7.87 +--//--
    7.88  
    7.89 +--[[--
    7.90 +db_selector =                -- same selector returned
    7.91 +<db_selector>:set_distinct()
    7.92 +
    7.93 +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.
    7.94 +
    7.95 +--]]--
    7.96  function selector_prototype:set_distinct()
    7.97    if #self._distinct_on > 0 then
    7.98      error("Can not combine DISTINCT with DISTINCT ON.")
    7.99 @@ -116,7 +172,21 @@
   7.100    self._distinct = true
   7.101    return self
   7.102  end
   7.103 +--//--
   7.104  
   7.105 +--[[--
   7.106 +db_selector =             -- same selector returned
   7.107 +<db_selector>:add_from(
   7.108 +  expression,             -- expression as passed to "assemble_command"
   7.109 +  alias,                  -- optional alias expression as passed to "assemble_command"
   7.110 +  condition               -- optional condition expression as passed to "assemble_command"
   7.111 +)
   7.112 +
   7.113 +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.
   7.114 +
   7.115 +This method is identical to "join".
   7.116 +
   7.117 +--]]--
   7.118  function selector_prototype:add_from(expression, alias, condition)
   7.119    local first = (#self._from == 0)
   7.120    if not first then
   7.121 @@ -149,32 +219,92 @@
   7.122    end
   7.123    return self
   7.124  end
   7.125 +--//--
   7.126  
   7.127 +--[[--
   7.128 +db_selector =             -- same selector returned
   7.129 +<db_selector>:add_where(
   7.130 +  expression              -- expression as passed to "assemble_command"
   7.131 +)
   7.132 +
   7.133 +Adds expressions for WHERE clause to the selector. The selector is modified and returned. Multiple calls cause expressions to be AND-combined.
   7.134 +
   7.135 +--]]--
   7.136  function selector_prototype:add_where(expression)
   7.137    add(self._where, expression)
   7.138    return self
   7.139  end
   7.140 +--//--
   7.141  
   7.142 +--[[--
   7.143 +db_selector =                -- same selector returned
   7.144 +<db_selector>:add_group_by(
   7.145 +  expression                 -- expression as passed to "assemble_command"
   7.146 +)
   7.147 +
   7.148 +Adds expressions for GROUP BY clause to the selector. The selector is modified and returned.
   7.149 +
   7.150 +--]]--
   7.151  function selector_prototype:add_group_by(expression)
   7.152    add(self._group_by, expression)
   7.153    return self
   7.154  end
   7.155 +--//--
   7.156  
   7.157 +--[[--
   7.158 +db_selector =              -- same selector returned
   7.159 +<db_selector>:add_having(
   7.160 +  expression               -- expression as passed to "assemble_command"
   7.161 +)
   7.162 +
   7.163 +Adds expressions for HAVING clause to the selector. The selector is modified and returned. Multiple calls cause expressions to be AND-combined.
   7.164 +
   7.165 +--]]--
   7.166  function selector_prototype:add_having(expression)
   7.167    add(self._having, expression)
   7.168    return self
   7.169  end
   7.170 +--//--
   7.171  
   7.172 +--[[--
   7.173 +db_selector =               -- same selector returned
   7.174 +<db_selector>:add_combine(
   7.175 +  expression                -- expression as passed to "assemble_command"
   7.176 +)
   7.177 +
   7.178 +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.
   7.179 +
   7.180 +--]]--
   7.181  function selector_prototype:add_combine(expression)
   7.182    add(self._combine, expression)
   7.183    return self
   7.184  end
   7.185 +--//--
   7.186  
   7.187 +--[[--
   7.188 +db_selector =                -- same selector returned
   7.189 +<db_selector>:add_order_by(
   7.190 +  expression                 -- expression as passed to "assemble_command"
   7.191 +)
   7.192 +
   7.193 +Adds expressions for ORDER BY clause to the selector. The selector is modified and returned.
   7.194 +
   7.195 +--]]--
   7.196  function selector_prototype:add_order_by(expression)
   7.197    add(self._order_by, expression)
   7.198    return self
   7.199  end
   7.200 +--//--
   7.201  
   7.202 +--[[--
   7.203 +db_selector =         -- same selector returned
   7.204 +<db_selector>:limit(
   7.205 +  count               -- integer used as LIMIT
   7.206 +)
   7.207 +
   7.208 +Limits the number of rows to a given number, by using LIMIT. The selector is modified and returned.
   7.209 +
   7.210 +--]]--
   7.211  function selector_prototype:limit(count)
   7.212    if type(count) ~= "number" or count % 1 ~= 0 then
   7.213      error("LIMIT must be an integer.")
   7.214 @@ -182,7 +312,17 @@
   7.215    self._limit = count
   7.216    return self
   7.217  end
   7.218 +--//--
   7.219  
   7.220 +--[[--
   7.221 +db_selector =          -- same selector returned
   7.222 +<db_selector>:offset(
   7.223 +  count                -- integer used as OFFSET
   7.224 +)
   7.225 +
   7.226 +Skips a given number of rows, by using OFFSET. The selector is modified and returned.
   7.227 +
   7.228 +--]]--
   7.229  function selector_prototype:offset(count)
   7.230    if type(count) ~= "number" or count % 1 ~= 0 then
   7.231      error("OFFSET must be an integer.")
   7.232 @@ -190,34 +330,90 @@
   7.233    self._offset = count
   7.234    return self
   7.235  end
   7.236 +--//--
   7.237  
   7.238 +--[[--
   7.239 +db_selector =              -- same selector returned
   7.240 +<db_selector>:for_share()
   7.241 +
   7.242 +Adds FOR SHARE to the statement, to share-lock all rows read. The selector is modified and returned.
   7.243 +
   7.244 +--]]--
   7.245  function selector_prototype:for_share()
   7.246    self._read_lock.all = true
   7.247    return self
   7.248  end
   7.249 +--//--
   7.250  
   7.251 +--[[--
   7.252 +db_selector =                -- same selector returned
   7.253 +<db_selector>:for_share_of(
   7.254 +  expression                 -- expression as passed to "assemble_command"
   7.255 +)
   7.256 +
   7.257 +Adds FOR SHARE OF to the statement, to share-lock all rows read by the named table(s). The selector is modified and returned.
   7.258 +
   7.259 +--]]--
   7.260  function selector_prototype:for_share_of(expression)
   7.261    add(self._read_lock, expression)
   7.262    return self
   7.263  end
   7.264 +--//--
   7.265  
   7.266 +--[[--
   7.267 +db_selector =               -- same selector returned
   7.268 +<db_selector>:for_update()
   7.269 +
   7.270 +Adds FOR UPDATE to the statement, to exclusivly lock all rows read. The selector is modified and returned.
   7.271 +
   7.272 +--]]--
   7.273  function selector_prototype:for_update()
   7.274    self._write_lock.all = true
   7.275    return self
   7.276  end
   7.277 +--//--
   7.278  
   7.279 +--[[--
   7.280 +db_selector =                 -- same selector returned
   7.281 +<db_selector>:for_update_of(
   7.282 +  expression                  -- expression as passed to "assemble_command"
   7.283 +)
   7.284 +
   7.285 +Adds FOR SHARE OF to the statement, to exclusivly lock all rows read by the named table(s). The selector is modified and returned.
   7.286 +
   7.287 +--]]--
   7.288  function selector_prototype:for_update_of(expression)
   7.289    add(self._write_lock, expression)
   7.290    return self
   7.291  end
   7.292 +--//--
   7.293  
   7.294 +--[[--
   7.295 +db_selector =                 -- same selector returned
   7.296 +<db_selector>:reset_fields()
   7.297 +
   7.298 +This method removes all fields added by method "add_field". The selector is modified and returned.
   7.299 +
   7.300 +--]]--
   7.301  function selector_prototype:reset_fields()
   7.302    for idx in ipairs(self._fields) do
   7.303      self._fields[idx] = nil
   7.304    end
   7.305    return self
   7.306  end
   7.307 +--//--
   7.308  
   7.309 +--[[--
   7.310 +db_selector =             -- same selector returned
   7.311 +<db_selector>:add_field(
   7.312 +  expression,             -- expression as passed to "assemble_command"
   7.313 +  alias,                  -- optional alias expression as passed to "assemble_command"
   7.314 +  option_list             -- optional list of options (may contain strings "distinct" or "grouped")
   7.315 +)
   7.316 +
   7.317 +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.
   7.318 +
   7.319 +--]]--
   7.320  function selector_prototype:add_field(expression, alias, options)
   7.321    if alias then
   7.322      add(self._fields, {'$ AS "$"', {expression}, {alias}})
   7.323 @@ -245,18 +441,58 @@
   7.324    end
   7.325    return self
   7.326  end
   7.327 +--//--
   7.328  
   7.329 +--[[--
   7.330 +db_selector =        -- same selector returned
   7.331 +<db_selector>:join(
   7.332 +  expression,        -- expression as passed to "assemble_command"
   7.333 +  alias,             -- optional alias expression as passed to "assemble_command"
   7.334 +  condition          -- optional condition expression as passed to "assemble_command"
   7.335 +)
   7.336 +
   7.337 +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.
   7.338 +
   7.339 +This method is identical to "add_from".
   7.340 +
   7.341 +--]]--
   7.342  function selector_prototype:join(...)  -- NOTE: alias for add_from
   7.343    return self:add_from(...)
   7.344  end
   7.345 +--//--
   7.346  
   7.347 +--[[--
   7.348 +db_selector =        -- same selector returned
   7.349 +<db_selector>:from(
   7.350 +  expression,        -- expression as passed to "assemble_command"
   7.351 +  alias,             -- optional alias expression as passed to "assemble_command"
   7.352 +  condition          -- optional condition expression as passed to "assemble_command"
   7.353 +)
   7.354 +
   7.355 +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.
   7.356 +
   7.357 +This method is identical to "add_from" or "join", except that an error is thrown, if there is already any FROM expression existent.
   7.358 +
   7.359 +--]]--
   7.360  function selector_prototype:from(expression, alias, condition)
   7.361    if #self._from > 0 then
   7.362      error("From-clause already existing (hint: try join).")
   7.363    end
   7.364    return self:join(expression, alias, condition)
   7.365  end
   7.366 +--//--
   7.367  
   7.368 +--[[--
   7.369 +db_selector =             -- same selector returned
   7.370 +<db_selector>:left_join(
   7.371 +  expression,             -- expression as passed to "assemble_command"
   7.372 +  alias,                  -- optional alias expression as passed to "assemble_command"
   7.373 +  condition               -- optional condition expression as passed to "assemble_command"
   7.374 +)
   7.375 +
   7.376 +Adds expressions for FROM clause to the selector using a LEFT OUTER JOIN. The selector is modified and returned.
   7.377 +
   7.378 +--]]--
   7.379  function selector_prototype:left_join(expression, alias, condition)
   7.380    local first = (#self._from == 0)
   7.381    if not first then
   7.382 @@ -277,42 +513,127 @@
   7.383    end
   7.384    return self
   7.385  end
   7.386 +--//--
   7.387  
   7.388 +--[[--
   7.389 +db_selector =         -- same selector returned
   7.390 +<db_selector>:union(
   7.391 +  expression          -- expression or selector without ORDER BY, LIMIT, FOR UPDATE or FOR SHARE
   7.392 +)
   7.393 +
   7.394 +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.
   7.395 +
   7.396 +--]]--
   7.397  function selector_prototype:union(expression)
   7.398    self:add_combine{"UNION $", {expression}}
   7.399    return self
   7.400  end
   7.401 +--//--
   7.402  
   7.403 +--[[--
   7.404 +db_selector =             -- same selector returned
   7.405 +<db_selector>:union_all(
   7.406 +  expression              -- expression or selector without ORDER BY, LIMIT, FOR UPDATE or FOR SHARE
   7.407 +)
   7.408 +
   7.409 +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.
   7.410 +
   7.411 +--]]--
   7.412  function selector_prototype:union_all(expression)
   7.413    self:add_combine{"UNION ALL $", {expression}}
   7.414    return self
   7.415  end
   7.416 +--//--
   7.417  
   7.418 +--[[--
   7.419 +db_selector =             -- same selector returned
   7.420 +<db_selector>:intersect(
   7.421 +  expression              -- expression or selector without ORDER BY, LIMIT, FOR UPDATE or FOR SHARE
   7.422 +)
   7.423 +
   7.424 +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.
   7.425 +
   7.426 +--]]--
   7.427  function selector_prototype:intersect(expression)
   7.428    self:add_combine{"INTERSECT $", {expression}}
   7.429    return self
   7.430  end
   7.431 +--//--
   7.432  
   7.433 +--[[--
   7.434 +db_selector =                 -- same selector returned
   7.435 +<db_selector>:intersect_all(
   7.436 +  expression                  -- expression or selector without ORDER BY, LIMIT, FOR UPDATE or FOR SHARE
   7.437 +)
   7.438 +
   7.439 +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.
   7.440 +
   7.441 +--]]--
   7.442  function selector_prototype:intersect_all(expression)
   7.443    self:add_combine{"INTERSECT ALL $", {expression}}
   7.444    return self
   7.445  end
   7.446 +--//--
   7.447  
   7.448 +--[[--
   7.449 +db_selector =          -- same selector returned
   7.450 +<db_selector>:except(
   7.451 +  expression           -- expression or selector without ORDER BY, LIMIT, FOR UPDATE or FOR SHARE
   7.452 +)
   7.453 +
   7.454 +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.
   7.455 +
   7.456 +--]]--
   7.457  function selector_prototype:except(expression)
   7.458    self:add_combine{"EXCEPT $", {expression}}
   7.459    return self
   7.460  end
   7.461 +--//--
   7.462  
   7.463 +--[[--
   7.464 +db_selector =              -- same selector returned
   7.465 +<db_selector>:except_all(
   7.466 +  expression               -- expression or selector without ORDER BY, LIMIT, FOR UPDATE or FOR SHARE
   7.467 +)
   7.468 +
   7.469 +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.
   7.470 +
   7.471 +--]]--
   7.472  function selector_prototype:except_all(expression)
   7.473    self:add_combine{"EXCEPT ALL $", {expression}}
   7.474    return self
   7.475  end
   7.476 +--//--
   7.477  
   7.478 +--[[--
   7.479 +db_selector =             -- same selector returned
   7.480 +<db_selector>:set_class(
   7.481 +  class                   -- database class (model)
   7.482 +)
   7.483 +
   7.484 +This method makes the selector to return database result lists or objects of the given database class (model). The selector is modified and returned.
   7.485 +
   7.486 +--]]--
   7.487  function selector_prototype:set_class(class)
   7.488    self._class = class
   7.489    return self
   7.490  end
   7.491 +--//--
   7.492  
   7.493 +--[[--
   7.494 +db_selector =          -- same selector returned
   7.495 +<db_selector>:attach(
   7.496 +  mode,                -- attachment type: "11" one to one, "1m" one to many, "m1" many to one
   7.497 +  data2,               -- other database result list or object, the results of this selector shall be attached with
   7.498 +  field1,              -- field name(s) in result list or object of this selector used for attaching
   7.499 +  field2,              -- field name(s) in "data2" used for attaching
   7.500 +  ref1,                -- name of reference field in the results of this selector after attaching
   7.501 +  ref2                 -- name of reference field in "data2" after attaching
   7.502 +)
   7.503 +
   7.504 +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.
   7.505 +
   7.506 +--]]--
   7.507  function selector_prototype:attach(mode, data2, field1, field2, ref1, ref2)
   7.508    self._attach = {
   7.509      mode = mode,
   7.510 @@ -324,8 +645,7 @@
   7.511    }
   7.512    return self
   7.513  end
   7.514 -
   7.515 --- TODO: many-to-many relations
   7.516 +--//--
   7.517  
   7.518  function selector_metatable:__tostring()
   7.519    local parts = {sep = " "}
   7.520 @@ -381,6 +701,14 @@
   7.521    return self._db_conn:assemble_command{"$", parts}
   7.522  end
   7.523  
   7.524 +--[[--
   7.525 +db_error,                 -- database error object, or nil in case of success
   7.526 +result =                  -- database result list or object
   7.527 +<db_selector>:try_exec()
   7.528 +
   7.529 +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.
   7.530 +
   7.531 +--]]--
   7.532  function selector_prototype:try_exec()
   7.533    if self._mode == "empty_list" then
   7.534      if self._class then
   7.535 @@ -410,7 +738,15 @@
   7.536      return nil
   7.537    end
   7.538  end
   7.539 +--//--
   7.540  
   7.541 +--[[--
   7.542 +result =              -- database result list or object
   7.543 +<db_selector>:exec()
   7.544 +
   7.545 +This method executes the selector on its database. The result list or object is returned on success, otherwise an error is thrown.
   7.546 +
   7.547 +--]]--
   7.548  function selector_prototype:exec()
   7.549    local db_error, result = self:try_exec()
   7.550    if db_error then
   7.551 @@ -419,8 +755,15 @@
   7.552      return result
   7.553    end
   7.554  end
   7.555 +--//--
   7.556  
   7.557 --- NOTE: This function caches the result!
   7.558 +--[[--
   7.559 +count =                -- number of rows returned
   7.560 +<db_selector>:count()
   7.561 +
   7.562 +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.
   7.563 +
   7.564 +--]]--
   7.565  function selector_prototype:count()
   7.566    if not self._count then
   7.567      local count_selector = self:get_db_conn():new_selector()
   7.568 @@ -431,6 +774,7 @@
   7.569    end
   7.570    return self._count
   7.571  end
   7.572 +--//--
   7.573  
   7.574  
   7.575  
   7.576 @@ -453,6 +797,20 @@
   7.577    end
   7.578  end
   7.579  
   7.580 +--[[--
   7.581 +mondelefant.attach(
   7.582 +  mode,              -- attachment type: "11" one to one, "1m" one to many, "m1" many to one
   7.583 +  data1,             -- first database result list or object
   7.584 +  data2,             -- second database result list or object
   7.585 +  key1,              -- field name(s) in first result list or object used for attaching
   7.586 +  key2,              -- field name(s) in second result list or object used for attaching
   7.587 +  ref1,              -- name of reference field to be set in first database result list or object
   7.588 +  ref2               -- name of reference field to be set in second database result list or object
   7.589 +)
   7.590 +
   7.591 +This function attaches database result lists/objects with each other. It does not need to be called directly.
   7.592 +
   7.593 +--]]--
   7.594  function attach(mode, data1, data2, key1, key2, ref1, ref2)
   7.595    local many1, many2
   7.596    if mode == "11" then
   7.597 @@ -532,6 +890,7 @@
   7.598      end
   7.599    end
   7.600  end
   7.601 +--//--
   7.602  
   7.603  
   7.604  
   7.605 @@ -539,15 +898,37 @@
   7.606  -- model system --
   7.607  ------------------
   7.608  
   7.609 +--[[--
   7.610 +<db_class>.primary_key
   7.611 +
   7.612 +Primary key of a database class (model). Defaults to "id".
   7.613 +
   7.614 +--]]--
   7.615  class_prototype.primary_key = "id"
   7.616 +--//--
   7.617  
   7.618 +--[[--
   7.619 +db_handle =               -- database connection handle used by this class
   7.620 +<db_class>:get_db_conn()
   7.621 +
   7.622 +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.
   7.623 +
   7.624 +--]]--
   7.625  function class_prototype:get_db_conn()
   7.626    error(
   7.627      "Method mondelefant class(_prototype):get_db_conn() " ..
   7.628      "has to be implemented."
   7.629    )
   7.630  end
   7.631 +--//--
   7.632  
   7.633 +--[[--
   7.634 +string =                          -- string of form '"schemaname"."tablename"' or '"tablename"'
   7.635 +<db_class>:get_qualified_table()
   7.636 +
   7.637 +This method returns a string with the (double quoted) qualified table name used to store objects of this class.
   7.638 +
   7.639 +--]]--
   7.640  function class_prototype:get_qualified_table()
   7.641    if not self.table then error "Table unknown." end
   7.642    if self.schema then
   7.643 @@ -556,7 +937,15 @@
   7.644      return '"' .. self.table .. '"'
   7.645    end
   7.646  end
   7.647 +--]]--
   7.648  
   7.649 +--[[--
   7.650 +string =                                  -- single quoted string of form "'schemaname.tablename'" or "'tablename'"
   7.651 +<db_class>:get_qualified_table_literal()
   7.652 +
   7.653 +This method returns a string with an SQL literal representing the given table. It causes ambiguities when the table name contains a dot (".") character.
   7.654 +
   7.655 +--]]--
   7.656  function class_prototype:get_qualified_table_literal()
   7.657    if not self.table then error "Table unknown." end
   7.658    if self.schema then
   7.659 @@ -565,7 +954,15 @@
   7.660      return self.table
   7.661    end
   7.662  end
   7.663 +--//--
   7.664  
   7.665 +--[[--
   7.666 +list =                             -- list of column names of primary key
   7.667 +<db_class>:get_primary_key_list()
   7.668 +
   7.669 +This method returns a list of column names of the primary key.
   7.670 +
   7.671 +--]]--
   7.672  function class_prototype:get_primary_key_list()
   7.673    local primary_key = self.primary_key
   7.674    if type(primary_key) == "string" then
   7.675 @@ -574,7 +971,15 @@
   7.676      return primary_key
   7.677    end
   7.678  end
   7.679 +--//--
   7.680  
   7.681 +--[[--
   7.682 +columns =                 -- list of columns
   7.683 +<db_class>:get_columns()
   7.684 +
   7.685 +This method returns a list of column names of the table used for the class.
   7.686 +
   7.687 +--]]--
   7.688  function class_prototype:get_columns()
   7.689    if self._columns then
   7.690      return self._columns
   7.691 @@ -600,6 +1005,15 @@
   7.692    return columns
   7.693  end
   7.694  
   7.695 +--[[--
   7.696 +selector =                -- new selector for selecting objects of this class
   7.697 +<db_class>:new_selector(
   7.698 +  db_conn                 -- optional(!) database connection handle, defaults to result of :get_db_conn()
   7.699 +)
   7.700 +
   7.701 +This method creates a new selector for selecting objects of the class.
   7.702 +
   7.703 +--]]--
   7.704  function class_prototype:new_selector(db_conn)
   7.705    local selector = (db_conn or self:get_db_conn()):new_selector()
   7.706    selector:set_class(self)
   7.707 @@ -607,20 +1021,44 @@
   7.708    selector:add_field(self:get_qualified_table() .. ".*")
   7.709    return selector
   7.710  end
   7.711 +--//--
   7.712  
   7.713 +--[[--
   7.714 +db_list =                 -- database result being an empty list
   7.715 +<db_class>:create_list()
   7.716 +
   7.717 +Creates an empty database result representing a list of objects of the given class.
   7.718 +
   7.719 +--]]--
   7.720  function class_prototype:create_list()
   7.721    local list = self:get_db_conn():create_list()
   7.722    list._class = self
   7.723    return list
   7.724  end
   7.725 +--//--
   7.726  
   7.727 +--[[--
   7.728 +db_object =       -- database object (instance of model)
   7.729 +<db_class>:new()
   7.730 +
   7.731 +Creates a new object of the given class.
   7.732 +
   7.733 +--]]--
   7.734  function class_prototype:new()
   7.735    local object = self:get_db_conn():create_object()
   7.736    object._class = self
   7.737    object._new = true
   7.738    return object
   7.739  end
   7.740 +--//--
   7.741  
   7.742 +--[[--
   7.743 +db_error =              -- database error object, or nil in case of success
   7.744 +<db_object>:try_save()
   7.745 +
   7.746 +This method saves changes to an object in the database. Returns nil on success, otherwise an error object is returned.
   7.747 +
   7.748 +--]]--
   7.749  function class_prototype.object:try_save()
   7.750    if not self._class then
   7.751      error("Cannot save object: No class information available.")
   7.752 @@ -703,7 +1141,14 @@
   7.753    end
   7.754    return nil
   7.755  end
   7.756 +--//--
   7.757  
   7.758 +--[[--
   7.759 +<db_object>:save()
   7.760 +
   7.761 +This method saves changes to an object in the database. Throws error, unless successful.
   7.762 +
   7.763 +--]]--
   7.764  function class_prototype.object:save()
   7.765    local db_error = self:try_save()
   7.766    if db_error then
   7.767 @@ -711,7 +1156,15 @@
   7.768    end
   7.769    return self
   7.770  end
   7.771 +--//--
   7.772  
   7.773 +--[[--
   7.774 +db_error =                 -- database error object, or nil in case of success
   7.775 +<db_object>:try_destroy()
   7.776 +
   7.777 +This method deletes an object in the database. Returns nil on success, otherwise an error object is returned.
   7.778 +
   7.779 +--]]--
   7.780  function class_prototype.object:try_destroy()
   7.781    if not self._class then
   7.782      error("Cannot destroy object: No class information available.")
   7.783 @@ -731,7 +1184,14 @@
   7.784      primary_key_compare
   7.785    }
   7.786  end
   7.787 +--//--
   7.788  
   7.789 +--[[--
   7.790 +<db_object>:destroy()
   7.791 +
   7.792 +This method deletes an object in the database. Throws error, unless successful.
   7.793 +
   7.794 +--]]--
   7.795  function class_prototype.object:destroy()
   7.796    local db_error = self:try_destroy()
   7.797    if db_error then
   7.798 @@ -739,7 +1199,22 @@
   7.799    end
   7.800    return self
   7.801  end
   7.802 +--//--
   7.803  
   7.804 +--[[--
   7.805 +db_selector =
   7.806 +<db_list>:get_reference_selector(
   7.807 +  ref_name,                        -- name of reference (e.g. "children")
   7.808 +  options,                         -- table options passed to the reference loader (e.g. { order = ... })
   7.809 +  ref_alias,                       -- optional alias for the reference (e.g. "ordered_children")
   7.810 +  back_ref_alias                   -- back reference name (e.g. "parent")
   7.811 +)
   7.812 +
   7.813 +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.
   7.814 +
   7.815 +This method is not only available for database result lists but also for database result objects.
   7.816 +
   7.817 +--]]--
   7.818  function class_prototype.list:get_reference_selector(
   7.819    ref_name, options, ref_alias, back_ref_alias
   7.820  )
   7.821 @@ -774,22 +1249,86 @@
   7.822    )
   7.823    return selector
   7.824  end
   7.825 +--//--
   7.826  
   7.827 +--[[--
   7.828 +db_list_or_object =
   7.829 +<db_list>:load(
   7.830 +  ref_name,          -- name of reference (e.g. "children")
   7.831 +  options,           -- table options passed to the reference loader (e.g. { order = ... })
   7.832 +  ref_alias,         -- optional alias for the reference (e.g. "ordered_children")
   7.833 +  back_ref_alias     -- back reference name (e.g. "parent")
   7.834 +)
   7.835 +
   7.836 +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.
   7.837 +
   7.838 +This method is not only available for database result lists but also for database result objects.
   7.839 +
   7.840 +--]]--
   7.841  function class_prototype.list.load(...)
   7.842    return class_prototype.list.get_reference_selector(...):exec()
   7.843  end
   7.844 +--//--
   7.845  
   7.846 +--[[--
   7.847 +db_object =
   7.848 +<db_object>:get_reference_selector(
   7.849 +  ref_name,                          -- name of reference (e.g. "children")
   7.850 +  options,                           -- table options passed to the reference loader (e.g. { order = ... })
   7.851 +  ref_alias,                         -- optional alias for the reference (e.g. "ordered_children")
   7.852 +  back_ref_alias                     -- back reference name (e.g. "parent")
   7.853 +)
   7.854 +
   7.855 +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.
   7.856 +
   7.857 +This method is not only available for database result objects but also for database result lists.
   7.858 +
   7.859 +--]]--
   7.860  function class_prototype.object:get_reference_selector(...)
   7.861    local list = self._class:create_list()
   7.862    list[1] = self
   7.863    return list:get_reference_selector(...)
   7.864  end
   7.865 +--//--
   7.866  
   7.867 +--[[--
   7.868 +db_list_or_object =
   7.869 +<db_object>:load(
   7.870 +  ref_name,          -- name of reference (e.g. "children")
   7.871 +  options,           -- table options passed to the reference loader (e.g. { order = ... })
   7.872 +  ref_alias,         -- optional alias for the reference (e.g. "ordered_children")
   7.873 +  back_ref_alias     -- back reference name (e.g. "parent")
   7.874 +)
   7.875 +
   7.876 +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.
   7.877 +
   7.878 +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.
   7.879 +
   7.880 +--]]--
   7.881  function class_prototype.object.load(...)
   7.882    return class_prototype.object.get_reference_selector(...):exec()
   7.883  end
   7.884 +--//--
   7.885  
   7.886 +--[[--
   7.887 +db_class =                                        -- same class returned
   7.888 +<db_class>:add_reference{
   7.889 +  mode                  = mode,                   -- "11", "1m", "m1", or "mm" (one/many to one/many)
   7.890 +  to                    = to,                     -- referenced class (model), optionally as string or function returning the value (avoids autoload)
   7.891 +  this_key              = this_key,               -- name of key in this class (model)
   7.892 +  that_key              = that_key,               -- name of key in the other class (model) ("to" argument)
   7.893 +  ref                   = ref,                    -- name of reference in this class, referring to the other class
   7.894 +  back_ref              = back_ref,               -- name of reference in other class, referring to this class
   7.895 +  default_order         = default_order,          -- expression as passed to "assemble_command" used for sorting
   7.896 +  selector_generator    = selector_generator,     -- alternative function used as selector generator (use only, when you know what you are doing)
   7.897 +  connected_by_table    = connected_by_table,     -- connecting table used for many to many relations
   7.898 +  connected_by_this_key = connected_by_this_key,  -- key in connecting table referring to "this_key" of this class (model)
   7.899 +  connected_by_that_key = connected_by_that_key   -- key in connecting table referring to "that_key" in other class (model) ("to" argument)
   7.900 +}
   7.901  
   7.902 +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.
   7.903 +
   7.904 +--]]--
   7.905  function class_prototype:add_reference(args)
   7.906    local selector_generator    = args.selector_generator
   7.907    local mode                  = args.mode
   7.908 @@ -885,3 +1424,5 @@
   7.909    end
   7.910    return self
   7.911  end
   7.912 +--//--
   7.913 +
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/libraries/mondelefant/mondelefant_native.autodoc.lua	Fri Jun 04 19:00:34 2010 +0200
     8.3 @@ -0,0 +1,265 @@
     8.4 +
     8.5 +--[[--
     8.6 +db_handle,                            -- database handle, or nil in case of error
     8.7 +errmsg,                               -- error message
     8.8 +errcode =                             -- error code
     8.9 +mondelefant.connect{
    8.10 +  engine          = "postgresql",     -- no other engine is supported
    8.11 +  host            = host,             -- hostname or directory with leading slash where Unix-domain socket resides
    8.12 +  hostaddr        = hostaddr,         -- IPv4, or IPv6 address if supported
    8.13 +  port            = port,             -- TCP port or socket file name extension
    8.14 +  dbname          = dbname,           -- name of database to connect with
    8.15 +  user            = user,             -- login name
    8.16 +  password        = password,         -- password
    8.17 +  connect_timeout = connect_timeout,  -- seconds to wait for connection to be established. Zero or nil means infinite
    8.18 +  ...
    8.19 +}  
    8.20 +
    8.21 +Opens a new database connection and returns a handle for that connection.
    8.22 +
    8.23 +--]]--
    8.24 +-- implemented in mondelefant_native.c as
    8.25 +-- static int mondelefant_connect(lua_State *L)
    8.26 +--//--
    8.27 +
    8.28 +
    8.29 +--[[--
    8.30 +<db_handle>:close()
    8.31 +
    8.32 +Closes the database connection. This method may be called multiple times and is called automatically when the database handle is garbage collected.
    8.33 +
    8.34 +--]]--
    8.35 +-- implemented in mondelefant_native.c as
    8.36 +-- static int mondelefant_conn_close(lua_State *L)
    8.37 +--//--
    8.38 +
    8.39 +
    8.40 +--[[--
    8.41 +status =             -- true, if database connection has no malfunction
    8.42 +<db_handle>:is_ok()
    8.43 +
    8.44 +Returns false, if the database connection has a malfunction, otherwise true.
    8.45 +
    8.46 +--]]--
    8.47 +-- implemented in mondelefant_native.c as
    8.48 +-- static int mondelefant_conn_is_ok(lua_State *L)
    8.49 +--//--
    8.50 +
    8.51 +
    8.52 +--[[--
    8.53 +status =                              -- status string
    8.54 +<db_handle>:get_transaction_status()
    8.55 +
    8.56 +Depending on the transaction status of the connection a string is returned:
    8.57 +- idle
    8.58 +- active
    8.59 +- intrans
    8.60 +- inerror
    8.61 +- unknown
    8.62 +
    8.63 +--]]--
    8.64 +-- implemented in mondelefant_native.c as
    8.65 +-- static int mondelefant_conn_get_transaction_status(lua_State *L)
    8.66 +--//--
    8.67 +
    8.68 +
    8.69 +--[[--
    8.70 +db_list =                  -- database result being an empty list
    8.71 +<db_handle>:create_list()
    8.72 +
    8.73 +Creates an empty database result representing a list. The used meta-table is "result_metatable". The attribute "_connection" is set to the database handle, and the attribute "_type" is set to "list".
    8.74 +
    8.75 +--]]--
    8.76 +-- implemented in mondelefant_native.c as
    8.77 +-- static int mondelefant_conn_create_list(lua_State *L)
    8.78 +--//--
    8.79 +
    8.80 +
    8.81 +--[[--
    8.82 +db_object =                  -- database result being an empty object (row)
    8.83 +<db_handle>:create_object()
    8.84 +
    8.85 +Creates an empty database result representing an object (row). The used meta-table is "result_metatable". The attribute "_connection" is set to the database handle, and the attribute "_type" is set to "object". Additionally the attributes "_data", "_dirty" and "_ref" are initialized with an empty table. TODO: Documentation of _data, _dirty and _ref.
    8.86 +
    8.87 +--]]--
    8.88 +-- implemented in mondelefant_native.c as
    8.89 +-- static int mondelefant_conn_create_object(lua_State *L)
    8.90 +--//--
    8.91 +
    8.92 +
    8.93 +--[[--
    8.94 +quoted_encoded_string =    -- encoded and quoted string
    8.95 +<db_handle>:quote_string(
    8.96 +  unencoded_string         -- string to encode and quote
    8.97 +)
    8.98 +
    8.99 +Prepares a string to be used safely within SQL queries. This function is database dependent (see "backslash_quote" server configuration option for PostgreSQL).
   8.100 +
   8.101 +--]]--
   8.102 +-- implemented in mondelefant_native.c as
   8.103 +-- static int mondelefant_conn_quote_string(lua_State *L)
   8.104 +--//--
   8.105 +
   8.106 +
   8.107 +--[[--
   8.108 +quoted_encoded_data =      -- encoded and quoted data (as Lua string)
   8.109 +<db_handle>:quote_string(
   8.110 +  raw_data                 -- data (as Lua string) to encode and quote
   8.111 +)
   8.112 +
   8.113 +Prepares a binary string to be used safely within SQL queries (as "bytea" type). This function is database dependent.
   8.114 +
   8.115 +--]]--
   8.116 +-- implemented in mondelefant_native.c as
   8.117 +-- static int mondelefant_conn_quote_binary(lua_State *L)
   8.118 +--//--
   8.119 +
   8.120 +
   8.121 +--[[--
   8.122 +sql_string =
   8.123 +<db_handle>:assemble_command{
   8.124 +  template,                    -- template string
   8.125 +  arg1,                        -- value to be inserted
   8.126 +  arg2,                        -- another value to be inserted
   8.127 +  key1 = named_arg3,           -- named value
   8.128 +  key2 = named_arg4,           -- another named value
   8.129 +  ...
   8.130 +}
   8.131 +
   8.132 +This method returns a SQL command by inserting the given values into the template string. Placeholders are "?" or "$", optionally followed by alphanumeric characters (including underscores). Placeholder characters can be escaped by writing them twice. A question-mark ("?") denotes a single value to be inserted, a dollar-sign ("$") denotes a list of sub-structures to be inserted. If alphanumeric characters are following the placeholder character, then these characters form a key, which is used to lookup the value to be used, otherwise values of numeric indicies are used.
   8.133 +
   8.134 +TODO: documentation of input-converters
   8.135 +
   8.136 +List of sub-structures are tables with an optional "sep" value, which is used as seperator. Each (numerically indexed) entry  of this table is passed to a recursive call of "assemble_command" and concatenated with the given seperator, or ", ", if no seperator is given.
   8.137 +
   8.138 +--]]--
   8.139 +-- implemented in mondelefant_native.c as
   8.140 +-- static int mondelefant_conn_assemble_command(lua_State *L)
   8.141 +--//--
   8.142 +
   8.143 +
   8.144 +--[[--
   8.145 +db_error,               -- error object, or nil in case of success
   8.146 +result1,                -- result of first command
   8.147 +result2,                -- result of second command
   8.148 +... =
   8.149 +<db_handle>:try_query(
   8.150 +  command1,             -- first command (to be processed by "assemble_command" method)
   8.151 +  mode1,                -- mode for first command: "list", "object" or "opt_object"
   8.152 +  command2,             -- second command (to be processed by "assemble_command" method)
   8.153 +  mode2,                -- mode for second command: "list", "object" or "opt_object"
   8.154 +  ..
   8.155 +)
   8.156 +
   8.157 +This method executes one or multiple SQL commands and returns its results. Each command is pre-processed by the "assemble_command" method of the database handle. A mode can be selected for each command: "list" for normal queries, "object" for queries which have exactly one result row, or "opt_object" which have one or zero result rows. If an error occurs, an error object is returned as first result value.
   8.158 +
   8.159 +The mode of the last command may be ommitted and default to "list".
   8.160 +
   8.161 +--]]--
   8.162 +-- implemented in mondelefant_native.c as
   8.163 +-- static int mondelefant_conn_try_query(lua_State *L)
   8.164 +--//--
   8.165 +
   8.166 +
   8.167 +--[[--
   8.168 +<db_error>:escalate()
   8.169 +
   8.170 +Causes a Lua error to be thrown. If the database connection has "error_objects" set to true, then the object is thrown itself, otherwise a string is thrown.
   8.171 +
   8.172 +--]]--
   8.173 +-- implemented in mondelefant_native.c as
   8.174 +-- static int mondelefant_errorobject_escalate(lua_State *L)
   8.175 +--//--
   8.176 +
   8.177 +
   8.178 +--[[--
   8.179 +bool =                  -- true or false
   8.180 +<db_error>:is_kind_of(
   8.181 +  error_code            -- error code as used by this library
   8.182 +)
   8.183 +
   8.184 +Checks, if a given error is of a given kind.
   8.185 +
   8.186 +Example:
   8.187 +db_error:is_kind_of("IntegrityConstraintViolation")
   8.188 +
   8.189 +--]]--
   8.190 +-- implemented in mondelefant_native.c as
   8.191 +-- static int mondelefant_errorobject_is_kind_of(lua_State *L)
   8.192 +--//--
   8.193 +
   8.194 +
   8.195 +--[[--
   8.196 +result1,            -- result of first command
   8.197 +result2,            -- result of second command
   8.198 +... =
   8.199 +<db_handle>:query(
   8.200 +  command1,         -- first command (to be processed by "assemble_command" method)
   8.201 +  mode1,            -- mode for first command: "list", "object" or "opt_object"
   8.202 +  command2,         -- second command (to be processed by "assemble_command" method)
   8.203 +  mode2,            -- mode for second command: "list", "object" or "opt_object"
   8.204 +  ..
   8.205 +)
   8.206 +
   8.207 +Same as "try_query" but raises error, when occurring.
   8.208 +
   8.209 +--]]--
   8.210 +-- implemented in mondelefant_native.c as
   8.211 +-- static int mondelefant_conn_query(lua_State *L)
   8.212 +--//--
   8.213 +
   8.214 +
   8.215 +--[[--
   8.216 +db_list_or_object =     -- first argument is returned
   8.217 +mondelefant.set_class(
   8.218 +  db_list_or_object,    -- database result list or object
   8.219 +  db_class              -- database class (model)
   8.220 +)
   8.221 +
   8.222 +This function sets a class for a given database result list or object. If a result list is given as first argument, the class is also set for all elements of the list.
   8.223 +
   8.224 +--]]--
   8.225 +-- implemented in mondelefant_native.c as
   8.226 +-- static int mondelefant_set_class(lua_State *L)
   8.227 +--//--
   8.228 +
   8.229 +
   8.230 +--[[--
   8.231 +db_class =               -- new database class (model)
   8.232 +mondelefant.new_class()
   8.233 +
   8.234 +This function creates a new class (model) used for database result lists or objects.
   8.235 +
   8.236 +--]]--
   8.237 +-- implemented in mondelefant_native.c as
   8.238 +-- static int mondelefant_new_class(lua_State *L) 
   8.239 +--//--
   8.240 +
   8.241 +
   8.242 +--[[--
   8.243 +reference_data =           -- table with reference information
   8.244 +<db_class>:get_reference(
   8.245 +  name                     -- reference name
   8.246 +)
   8.247 +
   8.248 +This function performs a lookup for the given name in the "reference" table. Prototypes are used, when lookup was unsuccessful.
   8.249 +
   8.250 +--]]--
   8.251 +-- implemented in mondelefant_native.c as
   8.252 +-- static int mondelefant_class_get_reference(lua_State *L)
   8.253 +--//--
   8.254 +
   8.255 +
   8.256 +--[[--
   8.257 +reference_name =                            -- reference name
   8.258 +<db_class>:get_foreign_key_reference_name(
   8.259 +  foreign_key                               -- foreign key
   8.260 +)
   8.261 +
   8.262 +This function performs a lookup for the given name in the "foreign_keys" table. Prototypes are used, when lookup was unsuccessful.
   8.263 +
   8.264 +--]]--
   8.265 +-- implemented in mondelefant_native.c as
   8.266 +-- static int mondelefant_class_get_reference(lua_State *L)
   8.267 +--//--
   8.268 +
     9.1 --- a/libraries/mondelefant/mondelefant_native.c	Thu Apr 22 20:46:29 2010 +0200
     9.2 +++ b/libraries/mondelefant/mondelefant_native.c	Fri Jun 04 19:00:34 2010 +0200
     9.3 @@ -5,24 +5,35 @@
     9.4  #include <catalog/pg_type.h>
     9.5  #include <stdint.h>
     9.6  
     9.7 -#define MONDELEFANT_REGKEY "e449ba8d9a53d353_mondelefant"
     9.8 +// NOTE: Comments with format "// <number>" denote the Lua stack position
     9.9 +
    9.10 +// prefix for all Lua registry entries of this library:
    9.11 +#define MONDELEFANT_REGKEY "e449ba8d9a53d353_mondelefant_"
    9.12  
    9.13 -#define MONDELEFANT_MODULE_REGKEY (MONDELEFANT_REGKEY "_module")
    9.14 -#define MONDELEFANT_CONN_MT_REGKEY (MONDELEFANT_REGKEY "_connection")
    9.15 -#define MONDELEFANT_CONN_DATA_REGKEY (MONDELEFANT_REGKEY "_connection_data")
    9.16 -#define MONDELEFANT_RESULT_MT_REGKEY (MONDELEFANT_REGKEY "_result")
    9.17 -#define MONDELEFANT_ERROROBJECT_MT_REGKEY (MONDELEFANT_REGKEY "_errorobject")
    9.18 -#define MONDELEFANT_CLASS_MT_REGKEY (MONDELEFANT_REGKEY "_class")
    9.19 -#define MONDELEFANT_CLASS_PROTO_REGKEY (MONDELEFANT_REGKEY "_class_proto")
    9.20 +// registry key of module "mondelefant_native":
    9.21 +#define MONDELEFANT_MODULE_REGKEY (MONDELEFANT_REGKEY "module")
    9.22 +// registry key of meta-table for database connections:
    9.23 +#define MONDELEFANT_CONN_MT_REGKEY (MONDELEFANT_REGKEY "connection")
    9.24 +// registry key of table storing connection specific data:
    9.25 +#define MONDELEFANT_CONN_DATA_REGKEY (MONDELEFANT_REGKEY "connection_data")
    9.26 +// registry key of meta-table for database result lists and objects:
    9.27 +#define MONDELEFANT_RESULT_MT_REGKEY (MONDELEFANT_REGKEY "result")
    9.28 +// registry key of meta-table for database error objects:
    9.29 +#define MONDELEFANT_ERROROBJECT_MT_REGKEY (MONDELEFANT_REGKEY "errorobject")
    9.30 +// registry key of meta-table for models (named classes here):
    9.31 +#define MONDELEFANT_CLASS_MT_REGKEY (MONDELEFANT_REGKEY "class")
    9.32 +// registry key of default prototype for models/classes:
    9.33 +#define MONDELEFANT_CLASS_PROTO_REGKEY (MONDELEFANT_REGKEY "class_proto")
    9.34  
    9.35 -#define MONDELEFANT_SERVER_ENCODING_ASCII 0
    9.36 -#define MONDELEFANT_SERVER_ENCODING_UTF8  1
    9.37 -
    9.38 +// C-structure for database connection userdata:
    9.39  typedef struct {
    9.40    PGconn *pgconn;
    9.41    int server_encoding;
    9.42  } mondelefant_conn_t;
    9.43 +#define MONDELEFANT_SERVER_ENCODING_ASCII 0
    9.44 +#define MONDELEFANT_SERVER_ENCODING_UTF8  1
    9.45  
    9.46 +// transform codepoint-position to byte-position for a given UTF-8 string:
    9.47  static size_t utf8_position_to_byte(const char *str, size_t utf8pos) {
    9.48    size_t bytepos;
    9.49    for (bytepos = 0; utf8pos > 0; bytepos++) {
    9.50 @@ -34,8 +45,10 @@
    9.51    return bytepos;
    9.52  }
    9.53  
    9.54 +// PostgreSQL's OID for binary data type (bytea):
    9.55  #define MONDELEFANT_POSTGRESQL_BINARY_OID ((Oid)17)
    9.56  
    9.57 +// mapping a PostgreSQL type given by its OID to a string identifier:
    9.58  static const char *mondelefant_oid_to_typestr(Oid oid) {
    9.59    switch (oid) {
    9.60      case 16: return "bool";
    9.61 @@ -79,9 +92,18 @@
    9.62    }
    9.63  }
    9.64  
    9.65 +// This library maps PostgreSQL's error codes to CamelCase string
    9.66 +// identifiers, which consist of CamelCase identifiers and are seperated
    9.67 +// by dots (".") (no leading or trailing dots).
    9.68 +// There are additional error identifiers which do not have a corresponding
    9.69 +// PostgreSQL error associated with it.
    9.70 +
    9.71 +// matching start of local variable 'pgcode' against string 'incode',
    9.72 +// returning string 'outcode' on match:
    9.73  #define mondelefant_errcode_item(incode, outcode) \
    9.74    if (!strncmp(pgcode, (incode), strlen(incode))) return outcode; else
    9.75  
    9.76 +// additional error identifiers without corresponding PostgreSQL error:
    9.77  #define MONDELEFANT_ERRCODE_UNKNOWN "unknown"
    9.78  #define MONDELEFANT_ERRCODE_CONNECTION "ConnectionException"
    9.79  #define MONDELEFANT_ERRCODE_RESULTCOUNT_LOW "WrongResultSetCount.ResultSetMissing"
    9.80 @@ -89,6 +111,7 @@
    9.81  #define MONDELEFANT_ERRCODE_QUERY1_NO_ROWS "NoData.OneRowExpected"
    9.82  #define MONDELEFANT_ERRCODE_QUERY1_MULTIPLE_ROWS "CardinalityViolation.OneRowExpected"
    9.83  
    9.84 +// mapping PostgreSQL error code to error code as returned by this library:
    9.85  static const char *mondelefant_translate_errcode(const char *pgcode) {
    9.86    if (!pgcode) abort();  // should not happen
    9.87    mondelefant_errcode_item("02", "NoData")
    9.88 @@ -136,6 +159,9 @@
    9.89    return "unknown";
    9.90  }
    9.91  
    9.92 +// C-function, checking if a given error code (as defined by this library)
    9.93 +// is belonging to a certain class of errors (strings are equal or error
    9.94 +// code begins with error class followed by a dot):
    9.95  static int mondelefant_check_error_class(
    9.96    const char *errcode, const char *errclass
    9.97  ) {
    9.98 @@ -150,6 +176,7 @@
    9.99    }
   9.100  }
   9.101  
   9.102 +// pushing first line of a string on Lua's stack (without trailing CR/LF):
   9.103  static void mondelefant_push_first_line(lua_State *L, const char *str) {
   9.104    char *str2;
   9.105    size_t i = 0;
   9.106 @@ -164,11 +191,14 @@
   9.107    free(str2);
   9.108  }
   9.109  
   9.110 +// "connect" function of library, which establishes a database connection
   9.111 +// and returns a database connection handle:
   9.112  static int mondelefant_connect(lua_State *L) {
   9.113 -  luaL_Buffer buf;
   9.114 -  const char *conninfo;
   9.115 -  PGconn *pgconn;
   9.116 -  mondelefant_conn_t *conn;
   9.117 +  luaL_Buffer buf;  // Lua string buffer to create 'conninfo' (see below)
   9.118 +  const char *conninfo;  // string for PQconnectdb function
   9.119 +  PGconn *pgconn;  // PGconn object as returned by PQconnectdb function
   9.120 +  mondelefant_conn_t *conn;  // C-structure for userdata
   9.121 +  // if engine is anything but "postgresql", then raise error:
   9.122    lua_settop(L, 1);
   9.123    lua_getfield(L, 1, "engine");  // 2
   9.124    if (!lua_toboolean(L, 2)) {
   9.125 @@ -180,6 +210,8 @@
   9.126        "Only database engine 'postgresql' is supported."
   9.127      );
   9.128    }
   9.129 +  // create 'conninfo' string for PQconnectdb function, which contains all
   9.130 +  // options except "engine" option:
   9.131    lua_settop(L, 1);
   9.132    lua_pushnil(L);  // slot for key at stack position 2
   9.133    lua_pushnil(L);  // slot for value at stack position 3
   9.134 @@ -223,7 +255,9 @@
   9.135    lua_replace(L, 2);
   9.136    lua_settop(L, 2);
   9.137    conninfo = lua_tostring(L, 2);
   9.138 +  // call PQconnectdb function of libpq:
   9.139    pgconn = PQconnectdb(conninfo);
   9.140 +  // throw errors, if neccessary:
   9.141    if (!pgconn) {
   9.142      return luaL_error(L,
   9.143        "Error in libpq while creating 'PGconn' structure."
   9.144 @@ -244,9 +278,12 @@
   9.145      PQfinish(pgconn);
   9.146      return 3;
   9.147    }
   9.148 +  // create userdata:
   9.149    lua_settop(L, 0);
   9.150    conn = lua_newuserdata(L, sizeof(*conn));  // 1
   9.151 +  // set 'pgconn' in C-struct of userdata:
   9.152    conn->pgconn = pgconn;
   9.153 +  // set 'server_encoding' in C-struct of userdata:
   9.154    {
   9.155      const char *charset;
   9.156      charset = PQparameterStatus(pgconn, "server_encoding");
   9.157 @@ -256,21 +293,25 @@
   9.158        conn->server_encoding = MONDELEFANT_SERVER_ENCODING_ASCII;
   9.159      }
   9.160    }
   9.161 -
   9.162 +  // set meta-table of userdata:
   9.163    luaL_getmetatable(L, MONDELEFANT_CONN_MT_REGKEY);  // 2
   9.164    lua_setmetatable(L, 1);
   9.165 -
   9.166 +  // create entry in table storing connection specific data and associate
   9.167 +  // created userdata with it:
   9.168    lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_CONN_DATA_REGKEY);  // 2
   9.169    lua_pushvalue(L, 1);  // 3
   9.170    lua_newtable(L);  // 4
   9.171    lua_settable(L, 2);
   9.172    lua_settop(L, 1);
   9.173 -
   9.174 +  // store key "engine" with value "postgresql" as connection specific data:
   9.175    lua_pushliteral(L, "postgresql");
   9.176    lua_setfield(L, 1, "engine");
   9.177 +  // return userdata:
   9.178    return 1;
   9.179  }
   9.180  
   9.181 +// returns pointer to libpq handle 'pgconn' of userdata at given index
   9.182 +// (or throws error, if database connection has been closed):
   9.183  static mondelefant_conn_t *mondelefant_get_conn(lua_State *L, int index) {
   9.184    mondelefant_conn_t *conn;
   9.185    conn = luaL_checkudata(L, index, MONDELEFANT_CONN_MT_REGKEY);
   9.186 @@ -281,7 +322,9 @@
   9.187    return conn;
   9.188  }
   9.189  
   9.190 +// meta-method "__index" of database handles (userdata):
   9.191  static int mondelefant_conn_index(lua_State *L) {
   9.192 +  // try table for connection specific data:
   9.193    lua_settop(L, 2);
   9.194    lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_CONN_DATA_REGKEY);  // 3
   9.195    lua_pushvalue(L, 1);  // 4
   9.196 @@ -290,6 +333,7 @@
   9.197    lua_pushvalue(L, 2);  // 4
   9.198    lua_gettable(L, 3);  // 4
   9.199    if (!lua_isnil(L, 4)) return 1;
   9.200 +  // try to use prototype stored in connection specific data:
   9.201    lua_settop(L, 3);
   9.202    lua_getfield(L, 3, "prototype");  // 4
   9.203    if (lua_toboolean(L, 4)) {
   9.204 @@ -297,6 +341,7 @@
   9.205      lua_gettable(L, 4);  // 5
   9.206      if (!lua_isnil(L, 5)) return 1;
   9.207    }
   9.208 +  // try to use "postgresql_connection_prototype" of library:
   9.209    lua_settop(L, 2);
   9.210    lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_MODULE_REGKEY);  // 3
   9.211    lua_getfield(L, 3, "postgresql_connection_prototype");  // 4
   9.212 @@ -305,6 +350,7 @@
   9.213      lua_gettable(L, 4);  // 5
   9.214      if (!lua_isnil(L, 5)) return 1;
   9.215    }
   9.216 +  // try to use "connection_prototype" of library:
   9.217    lua_settop(L, 3);
   9.218    lua_getfield(L, 3, "connection_prototype");  // 4
   9.219    if (lua_toboolean(L, 4)) {
   9.220 @@ -312,21 +358,26 @@
   9.221      lua_gettable(L, 4);  // 5
   9.222      if (!lua_isnil(L, 5)) return 1;
   9.223    }
   9.224 +  // give up and return nothing:
   9.225    return 0;
   9.226  }
   9.227  
   9.228 +// meta-method "__newindex" of database handles (userdata):
   9.229  static int mondelefant_conn_newindex(lua_State *L) {
   9.230 +  // store key-value pair in table for connection specific data:
   9.231    lua_settop(L, 3);
   9.232    lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_CONN_DATA_REGKEY);  // 4
   9.233    lua_pushvalue(L, 1);  // 5
   9.234    lua_gettable(L, 4);  // 5
   9.235 -  lua_remove(L, 4);  // connection specific data-table  at stack position 4
   9.236 +  lua_remove(L, 4);  // connection specific data-table at stack position 4
   9.237    lua_pushvalue(L, 2);
   9.238    lua_pushvalue(L, 3);
   9.239    lua_settable(L, 4);
   9.240 +  // return nothing:
   9.241    return 0;
   9.242  }
   9.243  
   9.244 +// meta-method "__gc" of database handles:
   9.245  static int mondelefant_conn_free(lua_State *L) {
   9.246    mondelefant_conn_t *conn;
   9.247    conn = luaL_checkudata(L, 1, MONDELEFANT_CONN_MT_REGKEY);
   9.248 @@ -335,6 +386,7 @@
   9.249    return 0;
   9.250  }
   9.251  
   9.252 +// method "close" of database handles:
   9.253  static int mondelefant_conn_close(lua_State *L) {
   9.254    mondelefant_conn_t *conn;
   9.255    lua_settop(L, 1);
   9.256 @@ -344,6 +396,7 @@
   9.257    return 0;
   9.258  }
   9.259  
   9.260 +// method "is_okay" of database handles:
   9.261  static int mondelefant_conn_is_ok(lua_State *L) {
   9.262    mondelefant_conn_t *conn;
   9.263    lua_settop(L, 1);
   9.264 @@ -352,6 +405,7 @@
   9.265    return 1;
   9.266  }
   9.267  
   9.268 +// method "get_transaction_status" of database handles:
   9.269  static int mondelefant_conn_get_transaction_status(lua_State *L) {
   9.270    mondelefant_conn_t *conn;
   9.271    lua_settop(L, 1);
   9.272 @@ -375,71 +429,97 @@
   9.273    return 1;
   9.274  }
   9.275  
   9.276 +// method "create_list" of database handles:
   9.277  static int mondelefant_conn_create_list(lua_State *L) {
   9.278 +  // ensure that first argument is a database connection:
   9.279    lua_settop(L, 2);
   9.280    luaL_checkudata(L, 1, MONDELEFANT_CONN_MT_REGKEY);
   9.281 +  // if no second argument is given, use an empty table:
   9.282    if (!lua_toboolean(L, 2)) {
   9.283      lua_newtable(L);
   9.284      lua_replace(L, 2);  // new result at stack position 2
   9.285    }
   9.286 +  // set meta-table for database result lists/objects:
   9.287    lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_RESULT_MT_REGKEY);  // 3
   9.288    lua_setmetatable(L, 2);
   9.289 +  // set "_connection" attribute to self:
   9.290    lua_pushvalue(L, 1);  // 3
   9.291    lua_setfield(L, 2, "_connection");
   9.292 +  // set "_type" attribute to string "list":
   9.293    lua_pushliteral(L, "list");  // 3
   9.294    lua_setfield(L, 2, "_type");
   9.295 +  // return created database result list:
   9.296    return 1;
   9.297  }
   9.298  
   9.299 +// method "create_object" of database handles:
   9.300  static int mondelefant_conn_create_object(lua_State *L) {
   9.301 +  // ensure that first argument is a database connection:
   9.302    lua_settop(L, 2);
   9.303    luaL_checkudata(L, 1, MONDELEFANT_CONN_MT_REGKEY);
   9.304 +  // if no second argument is given, use an empty table:
   9.305    if (!lua_toboolean(L, 2)) {
   9.306      lua_newtable(L);
   9.307      lua_replace(L, 2);  // new result at stack position 2
   9.308    }
   9.309 +  //   set meta-table for database result lists/objects:
   9.310    lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_RESULT_MT_REGKEY);  // 3
   9.311    lua_setmetatable(L, 2);
   9.312 +  // set "_connection" attribute to self:
   9.313    lua_pushvalue(L, 1);  // 3
   9.314    lua_setfield(L, 2, "_connection");
   9.315 +  // set "_type" attribute to string "object":
   9.316    lua_pushliteral(L, "object");  // 3
   9.317    lua_setfield(L, 2, "_type");  // "object" or "list"
   9.318 +  // create empty tables for "_data", "_dirty" and "_ref" attributes:
   9.319    lua_newtable(L);  // 3
   9.320    lua_setfield(L, 2, "_data");
   9.321    lua_newtable(L);  // 3
   9.322    lua_setfield(L, 2, "_dirty");
   9.323    lua_newtable(L);  // 3
   9.324    lua_setfield(L, 2, "_ref");  // nil=no info, false=nil, else table
   9.325 +  // return created database result object:
   9.326    return 1;
   9.327  }
   9.328  
   9.329 +// method "quote_string" of database handles:
   9.330  static int mondelefant_conn_quote_string(lua_State *L) {
   9.331    mondelefant_conn_t *conn;
   9.332    const char *input;
   9.333    size_t input_len;
   9.334    char *output;
   9.335    size_t output_len;
   9.336 +  // get 'conn' attribute of C-struct of database connection:
   9.337    lua_settop(L, 2);
   9.338    conn = mondelefant_get_conn(L, 1);
   9.339 +  // get second argument, which must be a string:
   9.340    input = luaL_checklstring(L, 2, &input_len);
   9.341 +  // throw error, if string is too long:
   9.342    if (input_len > (SIZE_MAX / sizeof(char) - 3) / 2) {
   9.343      return luaL_error(L, "String to be escaped is too long.");
   9.344    }
   9.345 +  // allocate memory for quoted string:
   9.346    output = malloc((2 * input_len + 3) * sizeof(char));
   9.347    if (!output) {
   9.348      return luaL_error(L, "Could not allocate memory for string quoting.");
   9.349    }
   9.350 +  // do escaping by calling PQescapeStringConn and enclosing result with
   9.351 +  // single quotes:
   9.352    output[0] = '\'';
   9.353    output_len = PQescapeStringConn(
   9.354      conn->pgconn, output + 1, input, input_len, NULL
   9.355    );
   9.356    output[output_len + 1] = '\'';
   9.357    output[output_len + 2] = 0;
   9.358 +  // create Lua string:
   9.359    lua_pushlstring(L, output, output_len + 2);
   9.360 +  // free allocated memory:
   9.361    free(output);
   9.362 +  // return Lua string:
   9.363    return 1;
   9.364  }
   9.365  
   9.366 +// method "quote_binary" of database handles:
   9.367  static int mondelefant_conn_quote_binary(lua_State *L) {
   9.368    mondelefant_conn_t *conn;
   9.369    const char *input;
   9.370 @@ -447,39 +527,52 @@
   9.371    char *output;
   9.372    size_t output_len;
   9.373    luaL_Buffer buf;
   9.374 +  // get 'conn' attribute of C-struct of database connection:
   9.375    lua_settop(L, 2);
   9.376    conn = mondelefant_get_conn(L, 1);
   9.377 +  // get second argument, which must be a string:
   9.378    input = luaL_checklstring(L, 2, &input_len);
   9.379 +  // call PQescapeByteaConn, which allocates memory itself:
   9.380    output = (char *)PQescapeByteaConn(
   9.381      conn->pgconn, (const unsigned char *)input, input_len, &output_len
   9.382    );
   9.383 +  // if PQescapeByteaConn returned NULL, then throw error:
   9.384    if (!output) {
   9.385      return luaL_error(L, "Could not allocate memory for binary quoting.");
   9.386    }
   9.387 +  // create Lua string enclosed by single quotes:
   9.388    luaL_buffinit(L, &buf);
   9.389    luaL_addchar(&buf, '\'');
   9.390    luaL_addlstring(&buf, output, output_len - 1);
   9.391    luaL_addchar(&buf, '\'');
   9.392    luaL_pushresult(&buf);
   9.393 +  // free memory allocated by PQescapeByteaConn:
   9.394    PQfreemem(output);
   9.395 +  // return Lua string:
   9.396    return 1;
   9.397  }
   9.398  
   9.399 +// method "assemble_command" of database handles:
   9.400  static int mondelefant_conn_assemble_command(lua_State *L) {
   9.401    mondelefant_conn_t *conn;
   9.402    int paramidx = 2;
   9.403    const char *template;
   9.404    size_t template_pos = 0;
   9.405    luaL_Buffer buf;
   9.406 +  // get 'conn' attribute of C-struct of database connection:
   9.407    lua_settop(L, 2);
   9.408    conn = mondelefant_get_conn(L, 1);
   9.409 +  // if second argument is a string, return this string:
   9.410    if (lua_isstring(L, 2)) {
   9.411      lua_tostring(L, 2);
   9.412      return 1;
   9.413    }
   9.414 -  // extra feature for objects with __tostring meta-method:
   9.415 +  // if second argument has __tostring meta-method,
   9.416 +  // then use this method and return its result:
   9.417    if (luaL_callmeta(L, 2, "__tostring")) return 1;
   9.418 +  // otherwise, require that second argument is a table:
   9.419    luaL_checktype(L, 2, LUA_TTABLE);
   9.420 +  // get first element of table, which must be a string:
   9.421    lua_rawgeti(L, 2, 1);  // 3
   9.422    luaL_argcheck(L,
   9.423      lua_isstring(L, 3),
   9.424 @@ -487,23 +580,37 @@
   9.425      "First entry of SQL command structure is not a string."
   9.426    );
   9.427    template = lua_tostring(L, 3);
   9.428 +  // get value of "input_converter" attribute of database connection:
   9.429    lua_pushliteral(L, "input_converter");  // 4
   9.430    lua_gettable(L, 1);  // input_converter at stack position 4
   9.431 +  // reserve space on Lua stack:
   9.432    lua_pushnil(L);  // free space at stack position 5
   9.433    lua_pushnil(L);  // free space at stack position 6
   9.434 +  // initialize Lua buffer for result string:
   9.435    luaL_buffinit(L, &buf);
   9.436 +  // fill buffer in loop:
   9.437    while (1) {
   9.438 +    // variable declaration:
   9.439      char c;
   9.440 +    // get next character:
   9.441      c = template[template_pos++];
   9.442 +    // break, when character is NULL byte:
   9.443      if (!c) break;
   9.444 -    if (c == '?' || c == '$') {
   9.445 -      if (template[template_pos] == c) {
   9.446 +    // question-mark and dollar-sign are special characters:
   9.447 +    if (c == '?' || c == '$') {  // special character found
   9.448 +      // check, if same character follows:
   9.449 +      if (template[template_pos] == c) {  // special character is escaped
   9.450 +        // consume two characters of input and add one character to buffer:
   9.451          template_pos++;
   9.452          luaL_addchar(&buf, c);
   9.453 -      } else {
   9.454 +      } else {  // special character is not escaped
   9.455          luaL_Buffer keybuf;
   9.456          int subcmd;
   9.457 +        // set 'subcmd' = true, if special character was a dollar-sign,
   9.458 +        // set 'subcmd' = false, if special character was a question-mark:
   9.459          subcmd = (c == '$');
   9.460 +        // read any number of alpha numeric chars or underscores
   9.461 +        // and store them on Lua stack:
   9.462          luaL_buffinit(L, &keybuf);
   9.463          while (1) {
   9.464            c = template[template_pos];
   9.465 @@ -517,21 +624,30 @@
   9.466            template_pos++;
   9.467          }
   9.468          luaL_pushresult(&keybuf);
   9.469 +        // check, if any characters matched:
   9.470          if (lua_objlen(L, -1)) {
   9.471 +          // if any alpha numeric chars or underscores were found,
   9.472 +          // push them on stack as a Lua string and use them to lookup
   9.473 +          // value from second argument:
   9.474            lua_pushvalue(L, -1);           // save key on stack
   9.475 -          lua_gettable(L, 2);             // fetch value
   9.476 +          lua_gettable(L, 2);             // fetch value (raw-value)
   9.477          } else {
   9.478 +          // otherwise push nil and use numeric lookup based on 'paramidx':
   9.479            lua_pop(L, 1);
   9.480            lua_pushnil(L);                 // put nil on key position
   9.481 -          lua_rawgeti(L, 2, paramidx++);  // fetch value
   9.482 +          lua_rawgeti(L, 2, paramidx++);  // fetch value (raw-value)
   9.483          }
   9.484 -        // stack: ..., <buffer>, key, pre-value
   9.485 -        if (subcmd) {
   9.486 +        // Lua stack contains: ..., <buffer>, key, raw-value
   9.487 +        // branch according to type of special character ("?" or "$"):
   9.488 +        if (subcmd) {  // dollar-sign
   9.489            size_t i;
   9.490            size_t count;
   9.491 -          lua_replace(L, 5);  // sub-structure at stack position 5
   9.492 -          lua_pop(L, 1);      // drop stored key
   9.493 -          // stack: ..., <buffer>
   9.494 +          // store fetched value (which is supposed to be sub-structure)
   9.495 +          // on Lua stack position 5 and drop key:
   9.496 +          lua_replace(L, 5);
   9.497 +          lua_pop(L, 1);
   9.498 +          // Lua stack contains: ..., <buffer>
   9.499 +          // check, if fetched value is really a sub-structure:
   9.500            luaL_argcheck(L,
   9.501              !lua_isnil(L, 5),
   9.502              2,
   9.503 @@ -542,9 +658,13 @@
   9.504              2,
   9.505              "SQL sub-structure must be a table."
   9.506            );
   9.507 -          // stack: ..., <buffer>
   9.508 +          // Lua stack contains: ..., <buffer>
   9.509 +          // get value of "sep" attribute of sub-structure,
   9.510 +          // and place it on Lua stack position 6:
   9.511            lua_getfield(L, 5, "sep");
   9.512 -          lua_replace(L, 6);  // seperator at stack position 6
   9.513 +          lua_replace(L, 6);
   9.514 +          // if seperator is nil, then use ", " as default,
   9.515 +          // if seperator is neither nil nor a string, then throw error:
   9.516            if (lua_isnil(L, 6)) {
   9.517              lua_pushstring(L, ", ");
   9.518              lua_replace(L, 6);
   9.519 @@ -555,21 +675,26 @@
   9.520                "Seperator of SQL sub-structure has to be a string."
   9.521              );
   9.522            }
   9.523 +          // iterate over items of sub-structure:
   9.524            count = lua_objlen(L, 5);
   9.525            for (i = 0; i < count; i++) {
   9.526 +            // add seperator, unless this is the first run:
   9.527              if (i) {
   9.528                lua_pushvalue(L, 6);
   9.529                luaL_addvalue(&buf);
   9.530              }
   9.531 +            // recursivly apply assemble function and add results to buffer:
   9.532              lua_pushcfunction(L, mondelefant_conn_assemble_command);
   9.533              lua_pushvalue(L, 1);
   9.534              lua_rawgeti(L, 5, i+1);
   9.535              lua_call(L, 2, 1);
   9.536              luaL_addvalue(&buf);
   9.537            }
   9.538 -        } else {
   9.539 +        } else {  // question-mark
   9.540            if (lua_toboolean(L, 4)) {
   9.541 -            // call input_converter with connection handle, value and info
   9.542 +            // call input_converter with connection handle, raw-value and
   9.543 +            // an info-table which contains a "field_name" entry with the
   9.544 +            // used key:
   9.545              lua_pushvalue(L, 4);
   9.546              lua_pushvalue(L, 1);
   9.547              lua_pushvalue(L, -3);
   9.548 @@ -577,54 +702,71 @@
   9.549              lua_pushvalue(L, -6);
   9.550              lua_setfield(L, -2, "field_name");
   9.551              lua_call(L, 3, 1);
   9.552 -            // stack: ..., <buffer>, key, pre-value, final-value
   9.553 +            // Lua stack contains: ..., <buffer>, key, raw-value, final-value
   9.554 +            // remove key and raw-value:
   9.555              lua_remove(L, -2);
   9.556              lua_remove(L, -2);
   9.557 -            // stack: ..., <buffer>, final-value
   9.558 +            // Lua stack contains: ..., <buffer>, final-value
   9.559 +            // throw error, if final-value is not a string:
   9.560              if (!lua_isstring(L, -1)) {
   9.561                return luaL_error(L, "input_converter returned non-string.");
   9.562              }
   9.563            } else {
   9.564 +            // remove key from stack:
   9.565              lua_remove(L, -2);
   9.566 -            // stack: ..., <buffer>, pre-value
   9.567 -            if (lua_isnil(L, -1)) {
   9.568 +            // Lua stack contains: ..., <buffer>, raw-value
   9.569 +            // branch according to type of value:
   9.570 +            if (lua_isnil(L, -1)) {  // value is nil
   9.571 +              // push string "NULL" to stack:
   9.572                lua_pushliteral(L, "NULL");
   9.573 -            } else if (lua_type(L, -1) == LUA_TBOOLEAN) {
   9.574 +            } else if (lua_type(L, -1) == LUA_TBOOLEAN) {  // value is boolean
   9.575 +              // push strings "TRUE" or "FALSE" to stack:
   9.576                lua_pushstring(L, lua_toboolean(L, -1) ? "TRUE" : "FALSE");
   9.577 -            } else if (lua_isstring(L, -1)) {
   9.578 +            } else if (lua_isstring(L, -1)) {  // value is string or number
   9.579                // NOTE: In this version of lua a number will be converted
   9.580 +              // push output of "quote_string" method of database
   9.581 +              // connection to stack:
   9.582                lua_tostring(L, -1);
   9.583                lua_pushcfunction(L, mondelefant_conn_quote_string);
   9.584                lua_pushvalue(L, 1);
   9.585                lua_pushvalue(L, -3);
   9.586                lua_call(L, 2, 1);
   9.587 -            } else {
   9.588 +            } else {  // value is of other type
   9.589 +              // throw error:
   9.590                return luaL_error(L,
   9.591                  "Unable to convert SQL value due to unknown type "
   9.592                  "or missing input_converter."
   9.593                );
   9.594              }
   9.595 -            // stack: ..., <buffer>, pre-value, final-value
   9.596 +            // Lua stack contains: ..., <buffer>, raw-value, final-value
   9.597 +            // remove raw-value:
   9.598              lua_remove(L, -2);
   9.599 -            // stack: ..., <buffer>, final-value
   9.600 +            // Lua stack contains: ..., <buffer>, final-value
   9.601            }
   9.602 +          // append final-value to buffer:
   9.603            luaL_addvalue(&buf);
   9.604          }
   9.605        }
   9.606 -    } else {
   9.607 +    } else {  // character is not special
   9.608 +      // just copy character:
   9.609        luaL_addchar(&buf, c);
   9.610      }
   9.611    }
   9.612 +  // return string in buffer:
   9.613    luaL_pushresult(&buf);
   9.614    return 1;
   9.615  }
   9.616  
   9.617 +// max number of SQL statements executed by one "query" method call:
   9.618  #define MONDELEFANT_MAX_COMMAND_COUNT 64
   9.619 +// max number of columns in a database result:
   9.620  #define MONDELEFANT_MAX_COLUMN_COUNT 1024
   9.621 +// enum values for 'modes' array in C-function below:
   9.622  #define MONDELEFANT_QUERY_MODE_LIST 1
   9.623  #define MONDELEFANT_QUERY_MODE_OBJECT 2
   9.624  #define MONDELEFANT_QUERY_MODE_OPT_OBJECT 3
   9.625  
   9.626 +// method "try_query" of database handles:
   9.627  static int mondelefant_conn_try_query(lua_State *L) {
   9.628    mondelefant_conn_t *conn;
   9.629    int command_count;
   9.630 @@ -634,12 +776,17 @@
   9.631    int sent_success;
   9.632    PGresult *res;
   9.633    int rows, cols, row, col;
   9.634 +  // get 'conn' attribute of C-struct of database connection:
   9.635    conn = mondelefant_get_conn(L, 1);
   9.636 +  // calculate number of commands (2 arguments for one command):
   9.637    command_count = lua_gettop(L) / 2;
   9.638 -  lua_pushnil(L);  // needed, if last mode was omitted
   9.639 +  // push nil on stack, which is needed, if last mode was ommitted:
   9.640 +  lua_pushnil(L);
   9.641 +  // throw error, if number of commands is too high:
   9.642    if (command_count > MONDELEFANT_MAX_COMMAND_COUNT) {
   9.643      return luaL_error(L, "Exceeded maximum command count in one query.");
   9.644    }
   9.645 +  // create SQL string, store query modes and push SQL string on stack:
   9.646    luaL_buffinit(L, &buf);
   9.647    for (command_idx = 0; command_idx < command_count; command_idx++) {
   9.648      int mode;
   9.649 @@ -671,6 +818,7 @@
   9.650    }
   9.651    luaL_pushresult(&buf);  // stack position unknown
   9.652    lua_replace(L, 2);  // SQL command string to stack position 2
   9.653 +  // call sql_tracer, if set:
   9.654    lua_settop(L, 2);
   9.655    lua_getfield(L, 1, "sql_tracer");  // tracer at stack position 3
   9.656    if (lua_toboolean(L, 3)) {
   9.657 @@ -678,14 +826,23 @@
   9.658      lua_pushvalue(L, 2);  // 5
   9.659      lua_call(L, 2, 1);  // trace callback at stack position 3
   9.660    }
   9.661 +  // NOTE: If no tracer was found, then nil or false is stored at stack
   9.662 +  // position 3.
   9.663 +  // call PQsendQuery function and store result in 'sent_success' variable:
   9.664    sent_success = PQsendQuery(conn->pgconn, lua_tostring(L, 2));
   9.665 +  // create preliminary result table:
   9.666    lua_newtable(L);  // results in table at stack position 4
   9.667 +  // iterate over results using function PQgetResult to fill result table:
   9.668    for (command_idx = 0; ; command_idx++) {
   9.669      int mode;
   9.670      char binary[MONDELEFANT_MAX_COLUMN_COUNT];
   9.671      ExecStatusType pgstatus;
   9.672 +    // fetch mode which was given for the command:
   9.673      mode = modes[command_idx];
   9.674 +    // if PQsendQuery call was successful, then fetch result data:
   9.675      if (sent_success) {
   9.676 +      // NOTE: PQgetResult called one extra time. Break only, if all
   9.677 +      // queries have been processed and PQgetResult returned NULL.
   9.678        res = PQgetResult(conn->pgconn);
   9.679        if (command_idx >= command_count && !res) break;
   9.680        if (res) {
   9.681 @@ -694,6 +851,7 @@
   9.682          cols = PQnfields(res);
   9.683        }
   9.684      }
   9.685 +    // handle errors:
   9.686      if (
   9.687        !sent_success || command_idx >= command_count || !res ||
   9.688        (pgstatus != PGRES_TUPLES_OK && pgstatus != PGRES_COMMAND_OK) ||
   9.689 @@ -822,8 +980,8 @@
   9.690        }
   9.691        return 1;
   9.692      }
   9.693 -    rows = PQntuples(res);
   9.694 -    cols = PQnfields(res);
   9.695 +    // call "create_list" or "create_object" method of database handle,
   9.696 +    // result will be at stack position 5:
   9.697      if (modes[command_idx] == MONDELEFANT_QUERY_MODE_LIST) {
   9.698        lua_pushcfunction(L, mondelefant_conn_create_list);  // 5
   9.699        lua_pushvalue(L, 1);  // 6
   9.700 @@ -833,7 +991,8 @@
   9.701        lua_pushvalue(L, 1);  // 6
   9.702        lua_call(L, 1, 1);  // 5
   9.703      }
   9.704 -    lua_newtable(L);  // column_info at atack position 6
   9.705 +    // set "_column_info":
   9.706 +    lua_newtable(L);  // 6
   9.707      for (col = 0; col < cols; col++) {
   9.708        lua_newtable(L);  // 7
   9.709        lua_pushstring(L, PQfname(res, col));
   9.710 @@ -870,7 +1029,8 @@
   9.711        }
   9.712        lua_rawseti(L, 6, col+1);
   9.713      }
   9.714 -    lua_setfield(L, 5, "_column_info");  // stack at position 5 with result
   9.715 +    lua_setfield(L, 5, "_column_info");
   9.716 +    // set "_rows_affected":
   9.717      {
   9.718        char *tmp;
   9.719        tmp = PQcmdTuples(res);
   9.720 @@ -879,6 +1039,7 @@
   9.721          lua_setfield(L, 5, "_rows_affected");
   9.722        }
   9.723      }
   9.724 +    // set "_oid":
   9.725      {
   9.726        Oid tmp;
   9.727        tmp = PQoidValue(res);
   9.728 @@ -887,6 +1048,8 @@
   9.729          lua_setfield(L, 5, "_oid");
   9.730        }
   9.731      }
   9.732 +    // copy data as strings or nil, while performing binary unescaping
   9.733 +    // automatically:
   9.734      if (modes[command_idx] == MONDELEFANT_QUERY_MODE_LIST) {
   9.735        for (row = 0; row < rows; row++) {
   9.736          lua_pushcfunction(L, mondelefant_conn_create_object);  // 6
   9.737 @@ -942,18 +1105,25 @@
   9.738        lua_pop(L, 1);
   9.739        lua_pushnil(L);
   9.740      }
   9.741 +    // save result in result list:
   9.742      lua_rawseti(L, 4, command_idx+1);
   9.743 +    // extra assertion:
   9.744      if (lua_gettop(L) != 4) abort();  // should not happen
   9.745 +    // free memory acquired by libpq:
   9.746      PQclear(res);
   9.747    }
   9.748    // trace callback at stack position 3
   9.749    // result at stack position 4 (top of stack)
   9.750 +  // if a trace callback is existent, then call:
   9.751    if (lua_toboolean(L, 3)) {
   9.752      lua_pushvalue(L, 3);
   9.753      lua_call(L, 0, 0);
   9.754    }
   9.755 -  lua_replace(L, 3);  // result at stack position 3
   9.756 -  lua_getfield(L, 1, "output_converter");  // output converter at stack position 4
   9.757 +  // put result at stack position 3:
   9.758 +  lua_replace(L, 3);
   9.759 +  // get output converter to stack position 4:
   9.760 +  lua_getfield(L, 1, "output_converter");
   9.761 +  // apply output converters and fill "_data" table according to column names:
   9.762    for (command_idx = 0; command_idx < command_count; command_idx++) {
   9.763      int mode;
   9.764      mode = modes[command_idx];
   9.765 @@ -1008,6 +1178,7 @@
   9.766      }
   9.767      lua_settop(L, 4);
   9.768    }
   9.769 +  // return nil as first result value, followed by result lists/objects:
   9.770    lua_settop(L, 3);
   9.771    lua_pushnil(L);
   9.772    for (command_idx = 0; command_idx < command_count; command_idx++) {
   9.773 @@ -1016,14 +1187,18 @@
   9.774    return command_count+1;
   9.775  }
   9.776  
   9.777 +// method "escalate" of error objects:
   9.778  static int mondelefant_errorobject_escalate(lua_State *L) {
   9.779 +  // check, if we may throw an error object instead of an error string:
   9.780    lua_settop(L, 1);
   9.781    lua_getfield(L, 1, "connection");  // 2
   9.782    lua_getfield(L, 2, "error_objects");  // 3
   9.783    if (lua_toboolean(L, 3)) {
   9.784 +    // throw error object:
   9.785      lua_settop(L, 1);
   9.786      return lua_error(L);
   9.787    } else {
   9.788 +    // throw error string:
   9.789      lua_getfield(L, 1, "message");  // 4
   9.790      if (lua_isnil(L, 4)) {
   9.791        return luaL_error(L, "No error message given for escalation.");
   9.792 @@ -1032,6 +1207,7 @@
   9.793    }
   9.794  }
   9.795  
   9.796 +// method "is_kind_of" of error objects:
   9.797  static int mondelefant_errorobject_is_kind_of(lua_State *L) {
   9.798    lua_settop(L, 2);
   9.799    lua_getfield(L, 1, "code");  // 3
   9.800 @@ -1048,40 +1224,51 @@
   9.801    return 1;
   9.802  }
   9.803  
   9.804 +// method "query" of database handles:
   9.805  static int mondelefant_conn_query(lua_State *L) {
   9.806    int argc;
   9.807 +  // count number of arguments:
   9.808    argc = lua_gettop(L);
   9.809 -  lua_pushvalue(L, 1);
   9.810 -  lua_insert(L, 1);
   9.811 +  // insert "try_query" function/method at stack position 1:
   9.812    lua_pushcfunction(L, mondelefant_conn_try_query);
   9.813 -  lua_insert(L, 2);
   9.814 -  lua_call(L, argc, LUA_MULTRET);  // results (with error) starting at index 2
   9.815 -  if (lua_toboolean(L, 2)) {
   9.816 +  lua_insert(L, 1);
   9.817 +  // call "try_query" method:
   9.818 +  lua_call(L, argc, LUA_MULTRET);  // results (with error) starting at index 1
   9.819 +  // check, if error occurred:
   9.820 +  if (lua_toboolean(L, 1)) {
   9.821 +    // invoke escalate method of error object:
   9.822      lua_pushcfunction(L, mondelefant_errorobject_escalate);
   9.823 -    lua_pushvalue(L, 2);
   9.824 +    lua_pushvalue(L, 1);
   9.825      lua_call(L, 1, 0);  // will raise an error
   9.826      return 0;  // should not be executed
   9.827    } else {
   9.828 -    return lua_gettop(L) - 2;
   9.829 +    // return everything but nil error object:
   9.830 +    return lua_gettop(L) - 1;
   9.831    }
   9.832  }
   9.833  
   9.834 +// library function "set_class":
   9.835  static int mondelefant_set_class(lua_State *L) {
   9.836 +  // ensure that first argument is a database result list/object:
   9.837    lua_settop(L, 2);
   9.838    lua_getmetatable(L, 1);  // 3
   9.839    lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_RESULT_MT_REGKEY);  // 4
   9.840    luaL_argcheck(L, lua_equal(L, 3, 4), 1, "not a database result");
   9.841 +  // ensure that second argument is a database class (model):
   9.842    lua_settop(L, 2);
   9.843    lua_getmetatable(L, 2);  // 3
   9.844    lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_CLASS_MT_REGKEY);  // 4
   9.845    luaL_argcheck(L, lua_equal(L, 3, 4), 2, "not a database class");
   9.846 +  // set attribute "_class" of result list/object to given class:
   9.847    lua_settop(L, 2);
   9.848    lua_pushvalue(L, 2);  // 3
   9.849    lua_setfield(L, 1, "_class");
   9.850 +  // test, if database result is a list (and not a single object):
   9.851    lua_getfield(L, 1, "_type");  // 3
   9.852    lua_pushliteral(L, "list");  // 4
   9.853    if (lua_rawequal(L, 3, 4)) {
   9.854      int i;
   9.855 +    // set attribute "_class" of all elements to given class:
   9.856      for (i=0; i < lua_objlen(L, 1); i++) {
   9.857        lua_settop(L, 2);
   9.858        lua_rawgeti(L, 1, i+1);  // 3
   9.859 @@ -1089,21 +1276,27 @@
   9.860        lua_setfield(L, 3, "_class");
   9.861      }
   9.862    }
   9.863 +  // return first argument:
   9.864    lua_settop(L, 1);
   9.865    return 1;
   9.866  }
   9.867  
   9.868 +// library function "new_class":
   9.869  static int mondelefant_new_class(lua_State *L) {
   9.870 +  // use first argument as template or create new table:
   9.871    lua_settop(L, 1);
   9.872    if (!lua_toboolean(L, 1)) {
   9.873      lua_settop(L, 0);
   9.874      lua_newtable(L);  // 1
   9.875    }
   9.876 +  // set meta-table for database classes (models):
   9.877    lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_CLASS_MT_REGKEY);  // 2
   9.878    lua_setmetatable(L, 1);
   9.879 +  // check, if "prototype" attribute is not set:
   9.880    lua_pushliteral(L, "prototype");  // 2
   9.881    lua_rawget(L, 1);  // 2
   9.882    if (!lua_toboolean(L, 2)) {
   9.883 +    // set "prototype" attribute to default prototype:
   9.884      lua_pushliteral(L, "prototype");  // 3
   9.885      lua_getfield(L,
   9.886        LUA_REGISTRYINDEX,
   9.887 @@ -1111,6 +1304,7 @@
   9.888      );  // 4
   9.889      lua_rawset(L, 1);
   9.890    }
   9.891 +  // set "object" attribute to empty table, unless it is already set:
   9.892    lua_settop(L, 1);
   9.893    lua_pushliteral(L, "object");  // 2
   9.894    lua_rawget(L, 1);  // 2
   9.895 @@ -1119,6 +1313,7 @@
   9.896      lua_newtable(L);  // 4
   9.897      lua_rawset(L, 1);
   9.898    }
   9.899 +  // set "object_get" attribute to empty table, unless it is already set:
   9.900    lua_settop(L, 1);
   9.901    lua_pushliteral(L, "object_get");  // 2
   9.902    lua_rawget(L, 1);  // 2
   9.903 @@ -1127,6 +1322,7 @@
   9.904      lua_newtable(L);  // 4
   9.905      lua_rawset(L, 1);
   9.906    }
   9.907 +  // set "object_set" attribute to empty table, unless it is already set:
   9.908    lua_settop(L, 1);
   9.909    lua_pushliteral(L, "object_set");  // 2
   9.910    lua_rawget(L, 1);  // 2
   9.911 @@ -1135,6 +1331,7 @@
   9.912      lua_newtable(L);  // 4
   9.913      lua_rawset(L, 1);
   9.914    }
   9.915 +  // set "list" attribute to empty table, unless it is already set:
   9.916    lua_settop(L, 1);
   9.917    lua_pushliteral(L, "list");  // 2
   9.918    lua_rawget(L, 1);  // 2
   9.919 @@ -1143,6 +1340,7 @@
   9.920      lua_newtable(L);  // 4
   9.921      lua_rawset(L, 1);
   9.922    }
   9.923 +  // set "references" attribute to empty table, unless it is already set:
   9.924    lua_settop(L, 1);
   9.925    lua_pushliteral(L, "references");  // 2
   9.926    lua_rawget(L, 1);  // 2
   9.927 @@ -1151,6 +1349,7 @@
   9.928      lua_newtable(L);  // 4
   9.929      lua_rawset(L, 1);
   9.930    }
   9.931 +  // set "foreign_keys" attribute to empty table, unless it is already set:
   9.932    lua_settop(L, 1);
   9.933    lua_pushliteral(L, "foreign_keys");  // 2
   9.934    lua_rawget(L, 1);  // 2
   9.935 @@ -1159,51 +1358,67 @@
   9.936      lua_newtable(L);  // 4
   9.937      lua_rawset(L, 1);
   9.938    }
   9.939 +  // return table:
   9.940    lua_settop(L, 1);
   9.941    return 1;
   9.942  }
   9.943  
   9.944 +// method "get_reference" of classes (models):
   9.945  static int mondelefant_class_get_reference(lua_State *L) {
   9.946    lua_settop(L, 2);
   9.947    while (lua_toboolean(L, 1)) {
   9.948 +    // get "references" table:
   9.949      lua_getfield(L, 1, "references");  // 3
   9.950 +    // perform lookup:
   9.951      lua_pushvalue(L, 2);  // 4
   9.952      lua_gettable(L, 3);  // 4
   9.953 +    // return result, if lookup was successful:
   9.954      if (!lua_isnil(L, 4)) return 1;
   9.955 +    // replace current table by its prototype:
   9.956      lua_settop(L, 2);
   9.957      lua_pushliteral(L, "prototype");  // 3
   9.958      lua_rawget(L, 1);  // 3
   9.959      lua_replace(L, 1);
   9.960    }
   9.961 +  // return nothing:
   9.962    return 0;
   9.963  }
   9.964  
   9.965 +// method "iterate_over_references" of classes (models):
   9.966  static int mondelefant_class_iterate_over_references(lua_State *L) {
   9.967    return luaL_error(L, "Reference iterator not implemented yet.");  // TODO
   9.968  }
   9.969  
   9.970 +// method "get_foreign_key_reference_name" of classes (models):
   9.971  static int mondelefant_class_get_foreign_key_reference_name(lua_State *L) {
   9.972    lua_settop(L, 2);
   9.973    while (lua_toboolean(L, 1)) {
   9.974 +    // get "foreign_keys" table:
   9.975      lua_getfield(L, 1, "foreign_keys");  // 3
   9.976 +    // perform lookup:
   9.977      lua_pushvalue(L, 2);  // 4
   9.978      lua_gettable(L, 3);  // 4
   9.979 +    // return result, if lookup was successful:
   9.980      if (!lua_isnil(L, 4)) return 1;
   9.981 +    // replace current table by its prototype:
   9.982      lua_settop(L, 2);
   9.983      lua_pushliteral(L, "prototype");  // 3
   9.984      lua_rawget(L, 1);  // 3
   9.985      lua_replace(L, 1);
   9.986    }
   9.987 +  // return nothing:
   9.988    return 0;
   9.989  }
   9.990  
   9.991 +// meta-method "__index" of database result lists and objects:
   9.992  static int mondelefant_result_index(lua_State *L) {
   9.993    const char *result_type;
   9.994 -  lua_settop(L, 2);
   9.995 +  // only lookup, when key is a string not beginning with an underscore:
   9.996    if (lua_type(L, 2) != LUA_TSTRING || lua_tostring(L, 2)[0] == '_') {
   9.997 -    lua_rawget(L, 1);
   9.998 -    return 1;
   9.999 +    return 0;
  9.1000    }
  9.1001 +  // get value of "_class" attribute, or default class, when unset:
  9.1002 +  lua_settop(L, 2);
  9.1003    lua_getfield(L, 1, "_class");  // 3
  9.1004    if (!lua_toboolean(L, 3)) {
  9.1005      lua_settop(L, 2);
  9.1006 @@ -1212,9 +1427,11 @@
  9.1007        MONDELEFANT_CLASS_PROTO_REGKEY
  9.1008      );  // 3
  9.1009    }
  9.1010 +  // get value of "_type" attribute:
  9.1011    lua_getfield(L, 1, "_type");  // 4
  9.1012    result_type = lua_tostring(L, 4);
  9.1013 -  if (result_type && !strcmp(result_type, "object")) {
  9.1014 +  // different lookup for lists and objects:
  9.1015 +  if (result_type && !strcmp(result_type, "object")) {  // object
  9.1016      lua_settop(L, 3);
  9.1017      // try inherited attributes, methods or getter functions:
  9.1018      while (lua_toboolean(L, 3)) {
  9.1019 @@ -1303,7 +1520,7 @@
  9.1020        return 1;
  9.1021      }
  9.1022      return 0;
  9.1023 -  } else if (result_type && !strcmp(result_type, "list")) {
  9.1024 +  } else if (result_type && !strcmp(result_type, "list")) {  // list
  9.1025      lua_settop(L, 3);
  9.1026      // try inherited list attributes or methods:
  9.1027      while (lua_toboolean(L, 3)) {
  9.1028 @@ -1317,16 +1534,20 @@
  9.1029        lua_replace(L, 3);
  9.1030      }
  9.1031    }
  9.1032 +  // return nothing:
  9.1033    return 0;
  9.1034  }
  9.1035  
  9.1036 +// meta-method "__newindex" of database result lists and objects:
  9.1037  static int mondelefant_result_newindex(lua_State *L) {
  9.1038    const char *result_type;
  9.1039 +  // perform rawset, unless key is a string not starting with underscore:
  9.1040    lua_settop(L, 3);
  9.1041    if (lua_type(L, 2) != LUA_TSTRING || lua_tostring(L, 2)[0] == '_') {
  9.1042      lua_rawset(L, 1);
  9.1043      return 1;
  9.1044    }
  9.1045 +  // get value of "_class" attribute, or default class, when unset:
  9.1046    lua_getfield(L, 1, "_class");  // 4
  9.1047    if (!lua_toboolean(L, 4)) {
  9.1048      lua_settop(L, 3);
  9.1049 @@ -1335,9 +1556,11 @@
  9.1050        MONDELEFANT_CLASS_PROTO_REGKEY
  9.1051      );  // 4
  9.1052    }
  9.1053 +  // get value of "_type" attribute:
  9.1054    lua_getfield(L, 1, "_type");  // 5
  9.1055    result_type = lua_tostring(L, 5);
  9.1056 -  if (result_type && !strcmp(result_type, "object")) {
  9.1057 +  // distinguish between lists and objects:
  9.1058 +  if (result_type && !strcmp(result_type, "object")) {  // objects
  9.1059      lua_settop(L, 4);
  9.1060      // try object setter functions:
  9.1061      while (lua_toboolean(L, 4)) {
  9.1062 @@ -1412,15 +1635,17 @@
  9.1063        lua_settable(L, 5);
  9.1064      }
  9.1065      return 0;
  9.1066 -  } else {
  9.1067 +  } else {  // non-objects (i.e. lists)
  9.1068 +    // perform rawset:
  9.1069      lua_settop(L, 3);
  9.1070      lua_rawset(L, 1);
  9.1071      return 0;
  9.1072    }
  9.1073 -  return 0;
  9.1074  }
  9.1075  
  9.1076 +// meta-method "__index" of classes (models):
  9.1077  static int mondelefant_class_index(lua_State *L) {
  9.1078 +  // perform lookup in prototype:
  9.1079    lua_settop(L, 2);
  9.1080    lua_pushliteral(L, "prototype");  // 3
  9.1081    lua_rawget(L, 1);  // 3
  9.1082 @@ -1429,6 +1654,7 @@
  9.1083    return 1;
  9.1084  }
  9.1085  
  9.1086 +// registration information for functions of library:
  9.1087  static const struct luaL_Reg mondelefant_module_functions[] = {
  9.1088    {"connect", mondelefant_connect},
  9.1089    {"set_class", mondelefant_set_class},
  9.1090 @@ -1436,6 +1662,7 @@
  9.1091    {NULL, NULL}
  9.1092  };
  9.1093  
  9.1094 +// registration information for meta-methods of database connections:
  9.1095  static const struct luaL_Reg mondelefant_conn_mt_functions[] = {
  9.1096    {"__gc", mondelefant_conn_free},
  9.1097    {"__index", mondelefant_conn_index},
  9.1098 @@ -1443,6 +1670,7 @@
  9.1099    {NULL, NULL}
  9.1100  };
  9.1101  
  9.1102 +// registration information for methods of database connections:
  9.1103  static const struct luaL_Reg mondelefant_conn_methods[] = {
  9.1104    {"close", mondelefant_conn_close},
  9.1105    {"is_ok", mondelefant_conn_is_ok},
  9.1106 @@ -1457,27 +1685,32 @@
  9.1107    {NULL, NULL}
  9.1108  };
  9.1109  
  9.1110 +// registration information for meta-methods of error objects:
  9.1111  static const struct luaL_Reg mondelefant_errorobject_mt_functions[] = {
  9.1112    {NULL, NULL}
  9.1113  };
  9.1114  
  9.1115 +// registration information for methods of error objects:
  9.1116  static const struct luaL_Reg mondelefant_errorobject_methods[] = {
  9.1117    {"escalate", mondelefant_errorobject_escalate},
  9.1118    {"is_kind_of", mondelefant_errorobject_is_kind_of},
  9.1119    {NULL, NULL}
  9.1120  };
  9.1121  
  9.1122 +// registration information for meta-methods of database result lists/objects:
  9.1123  static const struct luaL_Reg mondelefant_result_mt_functions[] = {
  9.1124    {"__index", mondelefant_result_index},
  9.1125    {"__newindex", mondelefant_result_newindex},
  9.1126    {NULL, NULL}
  9.1127  };
  9.1128  
  9.1129 +// registration information for methods of database result lists/objects:
  9.1130  static const struct luaL_Reg mondelefant_class_mt_functions[] = {
  9.1131    {"__index", mondelefant_class_index},
  9.1132    {NULL, NULL}
  9.1133  };
  9.1134  
  9.1135 +// registration information for methods of classes (models):
  9.1136  static const struct luaL_Reg mondelefant_class_methods[] = {
  9.1137    {"get_reference", mondelefant_class_get_reference},
  9.1138    {"iterate_over_references", mondelefant_class_iterate_over_references},
  9.1139 @@ -1486,14 +1719,17 @@
  9.1140    {NULL, NULL}
  9.1141  };
  9.1142  
  9.1143 +// registration information for methods of database result objects (not lists!):
  9.1144  static const struct luaL_Reg mondelefant_object_methods[] = {
  9.1145    {NULL, NULL}
  9.1146  };
  9.1147  
  9.1148 +// registration information for methods of database result lists (not single objects!):
  9.1149  static const struct luaL_Reg mondelefant_list_methods[] = {
  9.1150    {NULL, NULL}
  9.1151  };
  9.1152  
  9.1153 +// luaopen function to initialize/register library:
  9.1154  int luaopen_mondelefant_native(lua_State *L) {
  9.1155    lua_settop(L, 0);
  9.1156    lua_newtable(L);  // module at stack position 1

Impressum / About Us