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