webmcp
diff libraries/mondelefant/mondelefant_native.c @ 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 | 9fdfb27f8e67 |
children | ed00b972f40e |
line diff
1.1 --- a/libraries/mondelefant/mondelefant_native.c Thu Apr 22 20:46:29 2010 +0200 1.2 +++ b/libraries/mondelefant/mondelefant_native.c Fri Jun 04 19:00:34 2010 +0200 1.3 @@ -5,24 +5,35 @@ 1.4 #include <catalog/pg_type.h> 1.5 #include <stdint.h> 1.6 1.7 -#define MONDELEFANT_REGKEY "e449ba8d9a53d353_mondelefant" 1.8 +// NOTE: Comments with format "// <number>" denote the Lua stack position 1.9 + 1.10 +// prefix for all Lua registry entries of this library: 1.11 +#define MONDELEFANT_REGKEY "e449ba8d9a53d353_mondelefant_" 1.12 1.13 -#define MONDELEFANT_MODULE_REGKEY (MONDELEFANT_REGKEY "_module") 1.14 -#define MONDELEFANT_CONN_MT_REGKEY (MONDELEFANT_REGKEY "_connection") 1.15 -#define MONDELEFANT_CONN_DATA_REGKEY (MONDELEFANT_REGKEY "_connection_data") 1.16 -#define MONDELEFANT_RESULT_MT_REGKEY (MONDELEFANT_REGKEY "_result") 1.17 -#define MONDELEFANT_ERROROBJECT_MT_REGKEY (MONDELEFANT_REGKEY "_errorobject") 1.18 -#define MONDELEFANT_CLASS_MT_REGKEY (MONDELEFANT_REGKEY "_class") 1.19 -#define MONDELEFANT_CLASS_PROTO_REGKEY (MONDELEFANT_REGKEY "_class_proto") 1.20 +// registry key of module "mondelefant_native": 1.21 +#define MONDELEFANT_MODULE_REGKEY (MONDELEFANT_REGKEY "module") 1.22 +// registry key of meta-table for database connections: 1.23 +#define MONDELEFANT_CONN_MT_REGKEY (MONDELEFANT_REGKEY "connection") 1.24 +// registry key of table storing connection specific data: 1.25 +#define MONDELEFANT_CONN_DATA_REGKEY (MONDELEFANT_REGKEY "connection_data") 1.26 +// registry key of meta-table for database result lists and objects: 1.27 +#define MONDELEFANT_RESULT_MT_REGKEY (MONDELEFANT_REGKEY "result") 1.28 +// registry key of meta-table for database error objects: 1.29 +#define MONDELEFANT_ERROROBJECT_MT_REGKEY (MONDELEFANT_REGKEY "errorobject") 1.30 +// registry key of meta-table for models (named classes here): 1.31 +#define MONDELEFANT_CLASS_MT_REGKEY (MONDELEFANT_REGKEY "class") 1.32 +// registry key of default prototype for models/classes: 1.33 +#define MONDELEFANT_CLASS_PROTO_REGKEY (MONDELEFANT_REGKEY "class_proto") 1.34 1.35 -#define MONDELEFANT_SERVER_ENCODING_ASCII 0 1.36 -#define MONDELEFANT_SERVER_ENCODING_UTF8 1 1.37 - 1.38 +// C-structure for database connection userdata: 1.39 typedef struct { 1.40 PGconn *pgconn; 1.41 int server_encoding; 1.42 } mondelefant_conn_t; 1.43 +#define MONDELEFANT_SERVER_ENCODING_ASCII 0 1.44 +#define MONDELEFANT_SERVER_ENCODING_UTF8 1 1.45 1.46 +// transform codepoint-position to byte-position for a given UTF-8 string: 1.47 static size_t utf8_position_to_byte(const char *str, size_t utf8pos) { 1.48 size_t bytepos; 1.49 for (bytepos = 0; utf8pos > 0; bytepos++) { 1.50 @@ -34,8 +45,10 @@ 1.51 return bytepos; 1.52 } 1.53 1.54 +// PostgreSQL's OID for binary data type (bytea): 1.55 #define MONDELEFANT_POSTGRESQL_BINARY_OID ((Oid)17) 1.56 1.57 +// mapping a PostgreSQL type given by its OID to a string identifier: 1.58 static const char *mondelefant_oid_to_typestr(Oid oid) { 1.59 switch (oid) { 1.60 case 16: return "bool"; 1.61 @@ -79,9 +92,18 @@ 1.62 } 1.63 } 1.64 1.65 +// This library maps PostgreSQL's error codes to CamelCase string 1.66 +// identifiers, which consist of CamelCase identifiers and are seperated 1.67 +// by dots (".") (no leading or trailing dots). 1.68 +// There are additional error identifiers which do not have a corresponding 1.69 +// PostgreSQL error associated with it. 1.70 + 1.71 +// matching start of local variable 'pgcode' against string 'incode', 1.72 +// returning string 'outcode' on match: 1.73 #define mondelefant_errcode_item(incode, outcode) \ 1.74 if (!strncmp(pgcode, (incode), strlen(incode))) return outcode; else 1.75 1.76 +// additional error identifiers without corresponding PostgreSQL error: 1.77 #define MONDELEFANT_ERRCODE_UNKNOWN "unknown" 1.78 #define MONDELEFANT_ERRCODE_CONNECTION "ConnectionException" 1.79 #define MONDELEFANT_ERRCODE_RESULTCOUNT_LOW "WrongResultSetCount.ResultSetMissing" 1.80 @@ -89,6 +111,7 @@ 1.81 #define MONDELEFANT_ERRCODE_QUERY1_NO_ROWS "NoData.OneRowExpected" 1.82 #define MONDELEFANT_ERRCODE_QUERY1_MULTIPLE_ROWS "CardinalityViolation.OneRowExpected" 1.83 1.84 +// mapping PostgreSQL error code to error code as returned by this library: 1.85 static const char *mondelefant_translate_errcode(const char *pgcode) { 1.86 if (!pgcode) abort(); // should not happen 1.87 mondelefant_errcode_item("02", "NoData") 1.88 @@ -136,6 +159,9 @@ 1.89 return "unknown"; 1.90 } 1.91 1.92 +// C-function, checking if a given error code (as defined by this library) 1.93 +// is belonging to a certain class of errors (strings are equal or error 1.94 +// code begins with error class followed by a dot): 1.95 static int mondelefant_check_error_class( 1.96 const char *errcode, const char *errclass 1.97 ) { 1.98 @@ -150,6 +176,7 @@ 1.99 } 1.100 } 1.101 1.102 +// pushing first line of a string on Lua's stack (without trailing CR/LF): 1.103 static void mondelefant_push_first_line(lua_State *L, const char *str) { 1.104 char *str2; 1.105 size_t i = 0; 1.106 @@ -164,11 +191,14 @@ 1.107 free(str2); 1.108 } 1.109 1.110 +// "connect" function of library, which establishes a database connection 1.111 +// and returns a database connection handle: 1.112 static int mondelefant_connect(lua_State *L) { 1.113 - luaL_Buffer buf; 1.114 - const char *conninfo; 1.115 - PGconn *pgconn; 1.116 - mondelefant_conn_t *conn; 1.117 + luaL_Buffer buf; // Lua string buffer to create 'conninfo' (see below) 1.118 + const char *conninfo; // string for PQconnectdb function 1.119 + PGconn *pgconn; // PGconn object as returned by PQconnectdb function 1.120 + mondelefant_conn_t *conn; // C-structure for userdata 1.121 + // if engine is anything but "postgresql", then raise error: 1.122 lua_settop(L, 1); 1.123 lua_getfield(L, 1, "engine"); // 2 1.124 if (!lua_toboolean(L, 2)) { 1.125 @@ -180,6 +210,8 @@ 1.126 "Only database engine 'postgresql' is supported." 1.127 ); 1.128 } 1.129 + // create 'conninfo' string for PQconnectdb function, which contains all 1.130 + // options except "engine" option: 1.131 lua_settop(L, 1); 1.132 lua_pushnil(L); // slot for key at stack position 2 1.133 lua_pushnil(L); // slot for value at stack position 3 1.134 @@ -223,7 +255,9 @@ 1.135 lua_replace(L, 2); 1.136 lua_settop(L, 2); 1.137 conninfo = lua_tostring(L, 2); 1.138 + // call PQconnectdb function of libpq: 1.139 pgconn = PQconnectdb(conninfo); 1.140 + // throw errors, if neccessary: 1.141 if (!pgconn) { 1.142 return luaL_error(L, 1.143 "Error in libpq while creating 'PGconn' structure." 1.144 @@ -244,9 +278,12 @@ 1.145 PQfinish(pgconn); 1.146 return 3; 1.147 } 1.148 + // create userdata: 1.149 lua_settop(L, 0); 1.150 conn = lua_newuserdata(L, sizeof(*conn)); // 1 1.151 + // set 'pgconn' in C-struct of userdata: 1.152 conn->pgconn = pgconn; 1.153 + // set 'server_encoding' in C-struct of userdata: 1.154 { 1.155 const char *charset; 1.156 charset = PQparameterStatus(pgconn, "server_encoding"); 1.157 @@ -256,21 +293,25 @@ 1.158 conn->server_encoding = MONDELEFANT_SERVER_ENCODING_ASCII; 1.159 } 1.160 } 1.161 - 1.162 + // set meta-table of userdata: 1.163 luaL_getmetatable(L, MONDELEFANT_CONN_MT_REGKEY); // 2 1.164 lua_setmetatable(L, 1); 1.165 - 1.166 + // create entry in table storing connection specific data and associate 1.167 + // created userdata with it: 1.168 lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_CONN_DATA_REGKEY); // 2 1.169 lua_pushvalue(L, 1); // 3 1.170 lua_newtable(L); // 4 1.171 lua_settable(L, 2); 1.172 lua_settop(L, 1); 1.173 - 1.174 + // store key "engine" with value "postgresql" as connection specific data: 1.175 lua_pushliteral(L, "postgresql"); 1.176 lua_setfield(L, 1, "engine"); 1.177 + // return userdata: 1.178 return 1; 1.179 } 1.180 1.181 +// returns pointer to libpq handle 'pgconn' of userdata at given index 1.182 +// (or throws error, if database connection has been closed): 1.183 static mondelefant_conn_t *mondelefant_get_conn(lua_State *L, int index) { 1.184 mondelefant_conn_t *conn; 1.185 conn = luaL_checkudata(L, index, MONDELEFANT_CONN_MT_REGKEY); 1.186 @@ -281,7 +322,9 @@ 1.187 return conn; 1.188 } 1.189 1.190 +// meta-method "__index" of database handles (userdata): 1.191 static int mondelefant_conn_index(lua_State *L) { 1.192 + // try table for connection specific data: 1.193 lua_settop(L, 2); 1.194 lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_CONN_DATA_REGKEY); // 3 1.195 lua_pushvalue(L, 1); // 4 1.196 @@ -290,6 +333,7 @@ 1.197 lua_pushvalue(L, 2); // 4 1.198 lua_gettable(L, 3); // 4 1.199 if (!lua_isnil(L, 4)) return 1; 1.200 + // try to use prototype stored in connection specific data: 1.201 lua_settop(L, 3); 1.202 lua_getfield(L, 3, "prototype"); // 4 1.203 if (lua_toboolean(L, 4)) { 1.204 @@ -297,6 +341,7 @@ 1.205 lua_gettable(L, 4); // 5 1.206 if (!lua_isnil(L, 5)) return 1; 1.207 } 1.208 + // try to use "postgresql_connection_prototype" of library: 1.209 lua_settop(L, 2); 1.210 lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_MODULE_REGKEY); // 3 1.211 lua_getfield(L, 3, "postgresql_connection_prototype"); // 4 1.212 @@ -305,6 +350,7 @@ 1.213 lua_gettable(L, 4); // 5 1.214 if (!lua_isnil(L, 5)) return 1; 1.215 } 1.216 + // try to use "connection_prototype" of library: 1.217 lua_settop(L, 3); 1.218 lua_getfield(L, 3, "connection_prototype"); // 4 1.219 if (lua_toboolean(L, 4)) { 1.220 @@ -312,21 +358,26 @@ 1.221 lua_gettable(L, 4); // 5 1.222 if (!lua_isnil(L, 5)) return 1; 1.223 } 1.224 + // give up and return nothing: 1.225 return 0; 1.226 } 1.227 1.228 +// meta-method "__newindex" of database handles (userdata): 1.229 static int mondelefant_conn_newindex(lua_State *L) { 1.230 + // store key-value pair in table for connection specific data: 1.231 lua_settop(L, 3); 1.232 lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_CONN_DATA_REGKEY); // 4 1.233 lua_pushvalue(L, 1); // 5 1.234 lua_gettable(L, 4); // 5 1.235 - lua_remove(L, 4); // connection specific data-table at stack position 4 1.236 + lua_remove(L, 4); // connection specific data-table at stack position 4 1.237 lua_pushvalue(L, 2); 1.238 lua_pushvalue(L, 3); 1.239 lua_settable(L, 4); 1.240 + // return nothing: 1.241 return 0; 1.242 } 1.243 1.244 +// meta-method "__gc" of database handles: 1.245 static int mondelefant_conn_free(lua_State *L) { 1.246 mondelefant_conn_t *conn; 1.247 conn = luaL_checkudata(L, 1, MONDELEFANT_CONN_MT_REGKEY); 1.248 @@ -335,6 +386,7 @@ 1.249 return 0; 1.250 } 1.251 1.252 +// method "close" of database handles: 1.253 static int mondelefant_conn_close(lua_State *L) { 1.254 mondelefant_conn_t *conn; 1.255 lua_settop(L, 1); 1.256 @@ -344,6 +396,7 @@ 1.257 return 0; 1.258 } 1.259 1.260 +// method "is_okay" of database handles: 1.261 static int mondelefant_conn_is_ok(lua_State *L) { 1.262 mondelefant_conn_t *conn; 1.263 lua_settop(L, 1); 1.264 @@ -352,6 +405,7 @@ 1.265 return 1; 1.266 } 1.267 1.268 +// method "get_transaction_status" of database handles: 1.269 static int mondelefant_conn_get_transaction_status(lua_State *L) { 1.270 mondelefant_conn_t *conn; 1.271 lua_settop(L, 1); 1.272 @@ -375,71 +429,97 @@ 1.273 return 1; 1.274 } 1.275 1.276 +// method "create_list" of database handles: 1.277 static int mondelefant_conn_create_list(lua_State *L) { 1.278 + // ensure that first argument is a database connection: 1.279 lua_settop(L, 2); 1.280 luaL_checkudata(L, 1, MONDELEFANT_CONN_MT_REGKEY); 1.281 + // if no second argument is given, use an empty table: 1.282 if (!lua_toboolean(L, 2)) { 1.283 lua_newtable(L); 1.284 lua_replace(L, 2); // new result at stack position 2 1.285 } 1.286 + // set meta-table for database result lists/objects: 1.287 lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_RESULT_MT_REGKEY); // 3 1.288 lua_setmetatable(L, 2); 1.289 + // set "_connection" attribute to self: 1.290 lua_pushvalue(L, 1); // 3 1.291 lua_setfield(L, 2, "_connection"); 1.292 + // set "_type" attribute to string "list": 1.293 lua_pushliteral(L, "list"); // 3 1.294 lua_setfield(L, 2, "_type"); 1.295 + // return created database result list: 1.296 return 1; 1.297 } 1.298 1.299 +// method "create_object" of database handles: 1.300 static int mondelefant_conn_create_object(lua_State *L) { 1.301 + // ensure that first argument is a database connection: 1.302 lua_settop(L, 2); 1.303 luaL_checkudata(L, 1, MONDELEFANT_CONN_MT_REGKEY); 1.304 + // if no second argument is given, use an empty table: 1.305 if (!lua_toboolean(L, 2)) { 1.306 lua_newtable(L); 1.307 lua_replace(L, 2); // new result at stack position 2 1.308 } 1.309 + // set meta-table for database result lists/objects: 1.310 lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_RESULT_MT_REGKEY); // 3 1.311 lua_setmetatable(L, 2); 1.312 + // set "_connection" attribute to self: 1.313 lua_pushvalue(L, 1); // 3 1.314 lua_setfield(L, 2, "_connection"); 1.315 + // set "_type" attribute to string "object": 1.316 lua_pushliteral(L, "object"); // 3 1.317 lua_setfield(L, 2, "_type"); // "object" or "list" 1.318 + // create empty tables for "_data", "_dirty" and "_ref" attributes: 1.319 lua_newtable(L); // 3 1.320 lua_setfield(L, 2, "_data"); 1.321 lua_newtable(L); // 3 1.322 lua_setfield(L, 2, "_dirty"); 1.323 lua_newtable(L); // 3 1.324 lua_setfield(L, 2, "_ref"); // nil=no info, false=nil, else table 1.325 + // return created database result object: 1.326 return 1; 1.327 } 1.328 1.329 +// method "quote_string" of database handles: 1.330 static int mondelefant_conn_quote_string(lua_State *L) { 1.331 mondelefant_conn_t *conn; 1.332 const char *input; 1.333 size_t input_len; 1.334 char *output; 1.335 size_t output_len; 1.336 + // get 'conn' attribute of C-struct of database connection: 1.337 lua_settop(L, 2); 1.338 conn = mondelefant_get_conn(L, 1); 1.339 + // get second argument, which must be a string: 1.340 input = luaL_checklstring(L, 2, &input_len); 1.341 + // throw error, if string is too long: 1.342 if (input_len > (SIZE_MAX / sizeof(char) - 3) / 2) { 1.343 return luaL_error(L, "String to be escaped is too long."); 1.344 } 1.345 + // allocate memory for quoted string: 1.346 output = malloc((2 * input_len + 3) * sizeof(char)); 1.347 if (!output) { 1.348 return luaL_error(L, "Could not allocate memory for string quoting."); 1.349 } 1.350 + // do escaping by calling PQescapeStringConn and enclosing result with 1.351 + // single quotes: 1.352 output[0] = '\''; 1.353 output_len = PQescapeStringConn( 1.354 conn->pgconn, output + 1, input, input_len, NULL 1.355 ); 1.356 output[output_len + 1] = '\''; 1.357 output[output_len + 2] = 0; 1.358 + // create Lua string: 1.359 lua_pushlstring(L, output, output_len + 2); 1.360 + // free allocated memory: 1.361 free(output); 1.362 + // return Lua string: 1.363 return 1; 1.364 } 1.365 1.366 +// method "quote_binary" of database handles: 1.367 static int mondelefant_conn_quote_binary(lua_State *L) { 1.368 mondelefant_conn_t *conn; 1.369 const char *input; 1.370 @@ -447,39 +527,52 @@ 1.371 char *output; 1.372 size_t output_len; 1.373 luaL_Buffer buf; 1.374 + // get 'conn' attribute of C-struct of database connection: 1.375 lua_settop(L, 2); 1.376 conn = mondelefant_get_conn(L, 1); 1.377 + // get second argument, which must be a string: 1.378 input = luaL_checklstring(L, 2, &input_len); 1.379 + // call PQescapeByteaConn, which allocates memory itself: 1.380 output = (char *)PQescapeByteaConn( 1.381 conn->pgconn, (const unsigned char *)input, input_len, &output_len 1.382 ); 1.383 + // if PQescapeByteaConn returned NULL, then throw error: 1.384 if (!output) { 1.385 return luaL_error(L, "Could not allocate memory for binary quoting."); 1.386 } 1.387 + // create Lua string enclosed by single quotes: 1.388 luaL_buffinit(L, &buf); 1.389 luaL_addchar(&buf, '\''); 1.390 luaL_addlstring(&buf, output, output_len - 1); 1.391 luaL_addchar(&buf, '\''); 1.392 luaL_pushresult(&buf); 1.393 + // free memory allocated by PQescapeByteaConn: 1.394 PQfreemem(output); 1.395 + // return Lua string: 1.396 return 1; 1.397 } 1.398 1.399 +// method "assemble_command" of database handles: 1.400 static int mondelefant_conn_assemble_command(lua_State *L) { 1.401 mondelefant_conn_t *conn; 1.402 int paramidx = 2; 1.403 const char *template; 1.404 size_t template_pos = 0; 1.405 luaL_Buffer buf; 1.406 + // get 'conn' attribute of C-struct of database connection: 1.407 lua_settop(L, 2); 1.408 conn = mondelefant_get_conn(L, 1); 1.409 + // if second argument is a string, return this string: 1.410 if (lua_isstring(L, 2)) { 1.411 lua_tostring(L, 2); 1.412 return 1; 1.413 } 1.414 - // extra feature for objects with __tostring meta-method: 1.415 + // if second argument has __tostring meta-method, 1.416 + // then use this method and return its result: 1.417 if (luaL_callmeta(L, 2, "__tostring")) return 1; 1.418 + // otherwise, require that second argument is a table: 1.419 luaL_checktype(L, 2, LUA_TTABLE); 1.420 + // get first element of table, which must be a string: 1.421 lua_rawgeti(L, 2, 1); // 3 1.422 luaL_argcheck(L, 1.423 lua_isstring(L, 3), 1.424 @@ -487,23 +580,37 @@ 1.425 "First entry of SQL command structure is not a string." 1.426 ); 1.427 template = lua_tostring(L, 3); 1.428 + // get value of "input_converter" attribute of database connection: 1.429 lua_pushliteral(L, "input_converter"); // 4 1.430 lua_gettable(L, 1); // input_converter at stack position 4 1.431 + // reserve space on Lua stack: 1.432 lua_pushnil(L); // free space at stack position 5 1.433 lua_pushnil(L); // free space at stack position 6 1.434 + // initialize Lua buffer for result string: 1.435 luaL_buffinit(L, &buf); 1.436 + // fill buffer in loop: 1.437 while (1) { 1.438 + // variable declaration: 1.439 char c; 1.440 + // get next character: 1.441 c = template[template_pos++]; 1.442 + // break, when character is NULL byte: 1.443 if (!c) break; 1.444 - if (c == '?' || c == '$') { 1.445 - if (template[template_pos] == c) { 1.446 + // question-mark and dollar-sign are special characters: 1.447 + if (c == '?' || c == '$') { // special character found 1.448 + // check, if same character follows: 1.449 + if (template[template_pos] == c) { // special character is escaped 1.450 + // consume two characters of input and add one character to buffer: 1.451 template_pos++; 1.452 luaL_addchar(&buf, c); 1.453 - } else { 1.454 + } else { // special character is not escaped 1.455 luaL_Buffer keybuf; 1.456 int subcmd; 1.457 + // set 'subcmd' = true, if special character was a dollar-sign, 1.458 + // set 'subcmd' = false, if special character was a question-mark: 1.459 subcmd = (c == '$'); 1.460 + // read any number of alpha numeric chars or underscores 1.461 + // and store them on Lua stack: 1.462 luaL_buffinit(L, &keybuf); 1.463 while (1) { 1.464 c = template[template_pos]; 1.465 @@ -517,21 +624,30 @@ 1.466 template_pos++; 1.467 } 1.468 luaL_pushresult(&keybuf); 1.469 + // check, if any characters matched: 1.470 if (lua_objlen(L, -1)) { 1.471 + // if any alpha numeric chars or underscores were found, 1.472 + // push them on stack as a Lua string and use them to lookup 1.473 + // value from second argument: 1.474 lua_pushvalue(L, -1); // save key on stack 1.475 - lua_gettable(L, 2); // fetch value 1.476 + lua_gettable(L, 2); // fetch value (raw-value) 1.477 } else { 1.478 + // otherwise push nil and use numeric lookup based on 'paramidx': 1.479 lua_pop(L, 1); 1.480 lua_pushnil(L); // put nil on key position 1.481 - lua_rawgeti(L, 2, paramidx++); // fetch value 1.482 + lua_rawgeti(L, 2, paramidx++); // fetch value (raw-value) 1.483 } 1.484 - // stack: ..., <buffer>, key, pre-value 1.485 - if (subcmd) { 1.486 + // Lua stack contains: ..., <buffer>, key, raw-value 1.487 + // branch according to type of special character ("?" or "$"): 1.488 + if (subcmd) { // dollar-sign 1.489 size_t i; 1.490 size_t count; 1.491 - lua_replace(L, 5); // sub-structure at stack position 5 1.492 - lua_pop(L, 1); // drop stored key 1.493 - // stack: ..., <buffer> 1.494 + // store fetched value (which is supposed to be sub-structure) 1.495 + // on Lua stack position 5 and drop key: 1.496 + lua_replace(L, 5); 1.497 + lua_pop(L, 1); 1.498 + // Lua stack contains: ..., <buffer> 1.499 + // check, if fetched value is really a sub-structure: 1.500 luaL_argcheck(L, 1.501 !lua_isnil(L, 5), 1.502 2, 1.503 @@ -542,9 +658,13 @@ 1.504 2, 1.505 "SQL sub-structure must be a table." 1.506 ); 1.507 - // stack: ..., <buffer> 1.508 + // Lua stack contains: ..., <buffer> 1.509 + // get value of "sep" attribute of sub-structure, 1.510 + // and place it on Lua stack position 6: 1.511 lua_getfield(L, 5, "sep"); 1.512 - lua_replace(L, 6); // seperator at stack position 6 1.513 + lua_replace(L, 6); 1.514 + // if seperator is nil, then use ", " as default, 1.515 + // if seperator is neither nil nor a string, then throw error: 1.516 if (lua_isnil(L, 6)) { 1.517 lua_pushstring(L, ", "); 1.518 lua_replace(L, 6); 1.519 @@ -555,21 +675,26 @@ 1.520 "Seperator of SQL sub-structure has to be a string." 1.521 ); 1.522 } 1.523 + // iterate over items of sub-structure: 1.524 count = lua_objlen(L, 5); 1.525 for (i = 0; i < count; i++) { 1.526 + // add seperator, unless this is the first run: 1.527 if (i) { 1.528 lua_pushvalue(L, 6); 1.529 luaL_addvalue(&buf); 1.530 } 1.531 + // recursivly apply assemble function and add results to buffer: 1.532 lua_pushcfunction(L, mondelefant_conn_assemble_command); 1.533 lua_pushvalue(L, 1); 1.534 lua_rawgeti(L, 5, i+1); 1.535 lua_call(L, 2, 1); 1.536 luaL_addvalue(&buf); 1.537 } 1.538 - } else { 1.539 + } else { // question-mark 1.540 if (lua_toboolean(L, 4)) { 1.541 - // call input_converter with connection handle, value and info 1.542 + // call input_converter with connection handle, raw-value and 1.543 + // an info-table which contains a "field_name" entry with the 1.544 + // used key: 1.545 lua_pushvalue(L, 4); 1.546 lua_pushvalue(L, 1); 1.547 lua_pushvalue(L, -3); 1.548 @@ -577,54 +702,71 @@ 1.549 lua_pushvalue(L, -6); 1.550 lua_setfield(L, -2, "field_name"); 1.551 lua_call(L, 3, 1); 1.552 - // stack: ..., <buffer>, key, pre-value, final-value 1.553 + // Lua stack contains: ..., <buffer>, key, raw-value, final-value 1.554 + // remove key and raw-value: 1.555 lua_remove(L, -2); 1.556 lua_remove(L, -2); 1.557 - // stack: ..., <buffer>, final-value 1.558 + // Lua stack contains: ..., <buffer>, final-value 1.559 + // throw error, if final-value is not a string: 1.560 if (!lua_isstring(L, -1)) { 1.561 return luaL_error(L, "input_converter returned non-string."); 1.562 } 1.563 } else { 1.564 + // remove key from stack: 1.565 lua_remove(L, -2); 1.566 - // stack: ..., <buffer>, pre-value 1.567 - if (lua_isnil(L, -1)) { 1.568 + // Lua stack contains: ..., <buffer>, raw-value 1.569 + // branch according to type of value: 1.570 + if (lua_isnil(L, -1)) { // value is nil 1.571 + // push string "NULL" to stack: 1.572 lua_pushliteral(L, "NULL"); 1.573 - } else if (lua_type(L, -1) == LUA_TBOOLEAN) { 1.574 + } else if (lua_type(L, -1) == LUA_TBOOLEAN) { // value is boolean 1.575 + // push strings "TRUE" or "FALSE" to stack: 1.576 lua_pushstring(L, lua_toboolean(L, -1) ? "TRUE" : "FALSE"); 1.577 - } else if (lua_isstring(L, -1)) { 1.578 + } else if (lua_isstring(L, -1)) { // value is string or number 1.579 // NOTE: In this version of lua a number will be converted 1.580 + // push output of "quote_string" method of database 1.581 + // connection to stack: 1.582 lua_tostring(L, -1); 1.583 lua_pushcfunction(L, mondelefant_conn_quote_string); 1.584 lua_pushvalue(L, 1); 1.585 lua_pushvalue(L, -3); 1.586 lua_call(L, 2, 1); 1.587 - } else { 1.588 + } else { // value is of other type 1.589 + // throw error: 1.590 return luaL_error(L, 1.591 "Unable to convert SQL value due to unknown type " 1.592 "or missing input_converter." 1.593 ); 1.594 } 1.595 - // stack: ..., <buffer>, pre-value, final-value 1.596 + // Lua stack contains: ..., <buffer>, raw-value, final-value 1.597 + // remove raw-value: 1.598 lua_remove(L, -2); 1.599 - // stack: ..., <buffer>, final-value 1.600 + // Lua stack contains: ..., <buffer>, final-value 1.601 } 1.602 + // append final-value to buffer: 1.603 luaL_addvalue(&buf); 1.604 } 1.605 } 1.606 - } else { 1.607 + } else { // character is not special 1.608 + // just copy character: 1.609 luaL_addchar(&buf, c); 1.610 } 1.611 } 1.612 + // return string in buffer: 1.613 luaL_pushresult(&buf); 1.614 return 1; 1.615 } 1.616 1.617 +// max number of SQL statements executed by one "query" method call: 1.618 #define MONDELEFANT_MAX_COMMAND_COUNT 64 1.619 +// max number of columns in a database result: 1.620 #define MONDELEFANT_MAX_COLUMN_COUNT 1024 1.621 +// enum values for 'modes' array in C-function below: 1.622 #define MONDELEFANT_QUERY_MODE_LIST 1 1.623 #define MONDELEFANT_QUERY_MODE_OBJECT 2 1.624 #define MONDELEFANT_QUERY_MODE_OPT_OBJECT 3 1.625 1.626 +// method "try_query" of database handles: 1.627 static int mondelefant_conn_try_query(lua_State *L) { 1.628 mondelefant_conn_t *conn; 1.629 int command_count; 1.630 @@ -634,12 +776,17 @@ 1.631 int sent_success; 1.632 PGresult *res; 1.633 int rows, cols, row, col; 1.634 + // get 'conn' attribute of C-struct of database connection: 1.635 conn = mondelefant_get_conn(L, 1); 1.636 + // calculate number of commands (2 arguments for one command): 1.637 command_count = lua_gettop(L) / 2; 1.638 - lua_pushnil(L); // needed, if last mode was omitted 1.639 + // push nil on stack, which is needed, if last mode was ommitted: 1.640 + lua_pushnil(L); 1.641 + // throw error, if number of commands is too high: 1.642 if (command_count > MONDELEFANT_MAX_COMMAND_COUNT) { 1.643 return luaL_error(L, "Exceeded maximum command count in one query."); 1.644 } 1.645 + // create SQL string, store query modes and push SQL string on stack: 1.646 luaL_buffinit(L, &buf); 1.647 for (command_idx = 0; command_idx < command_count; command_idx++) { 1.648 int mode; 1.649 @@ -671,6 +818,7 @@ 1.650 } 1.651 luaL_pushresult(&buf); // stack position unknown 1.652 lua_replace(L, 2); // SQL command string to stack position 2 1.653 + // call sql_tracer, if set: 1.654 lua_settop(L, 2); 1.655 lua_getfield(L, 1, "sql_tracer"); // tracer at stack position 3 1.656 if (lua_toboolean(L, 3)) { 1.657 @@ -678,14 +826,23 @@ 1.658 lua_pushvalue(L, 2); // 5 1.659 lua_call(L, 2, 1); // trace callback at stack position 3 1.660 } 1.661 + // NOTE: If no tracer was found, then nil or false is stored at stack 1.662 + // position 3. 1.663 + // call PQsendQuery function and store result in 'sent_success' variable: 1.664 sent_success = PQsendQuery(conn->pgconn, lua_tostring(L, 2)); 1.665 + // create preliminary result table: 1.666 lua_newtable(L); // results in table at stack position 4 1.667 + // iterate over results using function PQgetResult to fill result table: 1.668 for (command_idx = 0; ; command_idx++) { 1.669 int mode; 1.670 char binary[MONDELEFANT_MAX_COLUMN_COUNT]; 1.671 ExecStatusType pgstatus; 1.672 + // fetch mode which was given for the command: 1.673 mode = modes[command_idx]; 1.674 + // if PQsendQuery call was successful, then fetch result data: 1.675 if (sent_success) { 1.676 + // NOTE: PQgetResult called one extra time. Break only, if all 1.677 + // queries have been processed and PQgetResult returned NULL. 1.678 res = PQgetResult(conn->pgconn); 1.679 if (command_idx >= command_count && !res) break; 1.680 if (res) { 1.681 @@ -694,6 +851,7 @@ 1.682 cols = PQnfields(res); 1.683 } 1.684 } 1.685 + // handle errors: 1.686 if ( 1.687 !sent_success || command_idx >= command_count || !res || 1.688 (pgstatus != PGRES_TUPLES_OK && pgstatus != PGRES_COMMAND_OK) || 1.689 @@ -822,8 +980,8 @@ 1.690 } 1.691 return 1; 1.692 } 1.693 - rows = PQntuples(res); 1.694 - cols = PQnfields(res); 1.695 + // call "create_list" or "create_object" method of database handle, 1.696 + // result will be at stack position 5: 1.697 if (modes[command_idx] == MONDELEFANT_QUERY_MODE_LIST) { 1.698 lua_pushcfunction(L, mondelefant_conn_create_list); // 5 1.699 lua_pushvalue(L, 1); // 6 1.700 @@ -833,7 +991,8 @@ 1.701 lua_pushvalue(L, 1); // 6 1.702 lua_call(L, 1, 1); // 5 1.703 } 1.704 - lua_newtable(L); // column_info at atack position 6 1.705 + // set "_column_info": 1.706 + lua_newtable(L); // 6 1.707 for (col = 0; col < cols; col++) { 1.708 lua_newtable(L); // 7 1.709 lua_pushstring(L, PQfname(res, col)); 1.710 @@ -870,7 +1029,8 @@ 1.711 } 1.712 lua_rawseti(L, 6, col+1); 1.713 } 1.714 - lua_setfield(L, 5, "_column_info"); // stack at position 5 with result 1.715 + lua_setfield(L, 5, "_column_info"); 1.716 + // set "_rows_affected": 1.717 { 1.718 char *tmp; 1.719 tmp = PQcmdTuples(res); 1.720 @@ -879,6 +1039,7 @@ 1.721 lua_setfield(L, 5, "_rows_affected"); 1.722 } 1.723 } 1.724 + // set "_oid": 1.725 { 1.726 Oid tmp; 1.727 tmp = PQoidValue(res); 1.728 @@ -887,6 +1048,8 @@ 1.729 lua_setfield(L, 5, "_oid"); 1.730 } 1.731 } 1.732 + // copy data as strings or nil, while performing binary unescaping 1.733 + // automatically: 1.734 if (modes[command_idx] == MONDELEFANT_QUERY_MODE_LIST) { 1.735 for (row = 0; row < rows; row++) { 1.736 lua_pushcfunction(L, mondelefant_conn_create_object); // 6 1.737 @@ -942,18 +1105,25 @@ 1.738 lua_pop(L, 1); 1.739 lua_pushnil(L); 1.740 } 1.741 + // save result in result list: 1.742 lua_rawseti(L, 4, command_idx+1); 1.743 + // extra assertion: 1.744 if (lua_gettop(L) != 4) abort(); // should not happen 1.745 + // free memory acquired by libpq: 1.746 PQclear(res); 1.747 } 1.748 // trace callback at stack position 3 1.749 // result at stack position 4 (top of stack) 1.750 + // if a trace callback is existent, then call: 1.751 if (lua_toboolean(L, 3)) { 1.752 lua_pushvalue(L, 3); 1.753 lua_call(L, 0, 0); 1.754 } 1.755 - lua_replace(L, 3); // result at stack position 3 1.756 - lua_getfield(L, 1, "output_converter"); // output converter at stack position 4 1.757 + // put result at stack position 3: 1.758 + lua_replace(L, 3); 1.759 + // get output converter to stack position 4: 1.760 + lua_getfield(L, 1, "output_converter"); 1.761 + // apply output converters and fill "_data" table according to column names: 1.762 for (command_idx = 0; command_idx < command_count; command_idx++) { 1.763 int mode; 1.764 mode = modes[command_idx]; 1.765 @@ -1008,6 +1178,7 @@ 1.766 } 1.767 lua_settop(L, 4); 1.768 } 1.769 + // return nil as first result value, followed by result lists/objects: 1.770 lua_settop(L, 3); 1.771 lua_pushnil(L); 1.772 for (command_idx = 0; command_idx < command_count; command_idx++) { 1.773 @@ -1016,14 +1187,18 @@ 1.774 return command_count+1; 1.775 } 1.776 1.777 +// method "escalate" of error objects: 1.778 static int mondelefant_errorobject_escalate(lua_State *L) { 1.779 + // check, if we may throw an error object instead of an error string: 1.780 lua_settop(L, 1); 1.781 lua_getfield(L, 1, "connection"); // 2 1.782 lua_getfield(L, 2, "error_objects"); // 3 1.783 if (lua_toboolean(L, 3)) { 1.784 + // throw error object: 1.785 lua_settop(L, 1); 1.786 return lua_error(L); 1.787 } else { 1.788 + // throw error string: 1.789 lua_getfield(L, 1, "message"); // 4 1.790 if (lua_isnil(L, 4)) { 1.791 return luaL_error(L, "No error message given for escalation."); 1.792 @@ -1032,6 +1207,7 @@ 1.793 } 1.794 } 1.795 1.796 +// method "is_kind_of" of error objects: 1.797 static int mondelefant_errorobject_is_kind_of(lua_State *L) { 1.798 lua_settop(L, 2); 1.799 lua_getfield(L, 1, "code"); // 3 1.800 @@ -1048,40 +1224,51 @@ 1.801 return 1; 1.802 } 1.803 1.804 +// method "query" of database handles: 1.805 static int mondelefant_conn_query(lua_State *L) { 1.806 int argc; 1.807 + // count number of arguments: 1.808 argc = lua_gettop(L); 1.809 - lua_pushvalue(L, 1); 1.810 - lua_insert(L, 1); 1.811 + // insert "try_query" function/method at stack position 1: 1.812 lua_pushcfunction(L, mondelefant_conn_try_query); 1.813 - lua_insert(L, 2); 1.814 - lua_call(L, argc, LUA_MULTRET); // results (with error) starting at index 2 1.815 - if (lua_toboolean(L, 2)) { 1.816 + lua_insert(L, 1); 1.817 + // call "try_query" method: 1.818 + lua_call(L, argc, LUA_MULTRET); // results (with error) starting at index 1 1.819 + // check, if error occurred: 1.820 + if (lua_toboolean(L, 1)) { 1.821 + // invoke escalate method of error object: 1.822 lua_pushcfunction(L, mondelefant_errorobject_escalate); 1.823 - lua_pushvalue(L, 2); 1.824 + lua_pushvalue(L, 1); 1.825 lua_call(L, 1, 0); // will raise an error 1.826 return 0; // should not be executed 1.827 } else { 1.828 - return lua_gettop(L) - 2; 1.829 + // return everything but nil error object: 1.830 + return lua_gettop(L) - 1; 1.831 } 1.832 } 1.833 1.834 +// library function "set_class": 1.835 static int mondelefant_set_class(lua_State *L) { 1.836 + // ensure that first argument is a database result list/object: 1.837 lua_settop(L, 2); 1.838 lua_getmetatable(L, 1); // 3 1.839 lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_RESULT_MT_REGKEY); // 4 1.840 luaL_argcheck(L, lua_equal(L, 3, 4), 1, "not a database result"); 1.841 + // ensure that second argument is a database class (model): 1.842 lua_settop(L, 2); 1.843 lua_getmetatable(L, 2); // 3 1.844 lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_CLASS_MT_REGKEY); // 4 1.845 luaL_argcheck(L, lua_equal(L, 3, 4), 2, "not a database class"); 1.846 + // set attribute "_class" of result list/object to given class: 1.847 lua_settop(L, 2); 1.848 lua_pushvalue(L, 2); // 3 1.849 lua_setfield(L, 1, "_class"); 1.850 + // test, if database result is a list (and not a single object): 1.851 lua_getfield(L, 1, "_type"); // 3 1.852 lua_pushliteral(L, "list"); // 4 1.853 if (lua_rawequal(L, 3, 4)) { 1.854 int i; 1.855 + // set attribute "_class" of all elements to given class: 1.856 for (i=0; i < lua_objlen(L, 1); i++) { 1.857 lua_settop(L, 2); 1.858 lua_rawgeti(L, 1, i+1); // 3 1.859 @@ -1089,21 +1276,27 @@ 1.860 lua_setfield(L, 3, "_class"); 1.861 } 1.862 } 1.863 + // return first argument: 1.864 lua_settop(L, 1); 1.865 return 1; 1.866 } 1.867 1.868 +// library function "new_class": 1.869 static int mondelefant_new_class(lua_State *L) { 1.870 + // use first argument as template or create new table: 1.871 lua_settop(L, 1); 1.872 if (!lua_toboolean(L, 1)) { 1.873 lua_settop(L, 0); 1.874 lua_newtable(L); // 1 1.875 } 1.876 + // set meta-table for database classes (models): 1.877 lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_CLASS_MT_REGKEY); // 2 1.878 lua_setmetatable(L, 1); 1.879 + // check, if "prototype" attribute is not set: 1.880 lua_pushliteral(L, "prototype"); // 2 1.881 lua_rawget(L, 1); // 2 1.882 if (!lua_toboolean(L, 2)) { 1.883 + // set "prototype" attribute to default prototype: 1.884 lua_pushliteral(L, "prototype"); // 3 1.885 lua_getfield(L, 1.886 LUA_REGISTRYINDEX, 1.887 @@ -1111,6 +1304,7 @@ 1.888 ); // 4 1.889 lua_rawset(L, 1); 1.890 } 1.891 + // set "object" attribute to empty table, unless it is already set: 1.892 lua_settop(L, 1); 1.893 lua_pushliteral(L, "object"); // 2 1.894 lua_rawget(L, 1); // 2 1.895 @@ -1119,6 +1313,7 @@ 1.896 lua_newtable(L); // 4 1.897 lua_rawset(L, 1); 1.898 } 1.899 + // set "object_get" attribute to empty table, unless it is already set: 1.900 lua_settop(L, 1); 1.901 lua_pushliteral(L, "object_get"); // 2 1.902 lua_rawget(L, 1); // 2 1.903 @@ -1127,6 +1322,7 @@ 1.904 lua_newtable(L); // 4 1.905 lua_rawset(L, 1); 1.906 } 1.907 + // set "object_set" attribute to empty table, unless it is already set: 1.908 lua_settop(L, 1); 1.909 lua_pushliteral(L, "object_set"); // 2 1.910 lua_rawget(L, 1); // 2 1.911 @@ -1135,6 +1331,7 @@ 1.912 lua_newtable(L); // 4 1.913 lua_rawset(L, 1); 1.914 } 1.915 + // set "list" attribute to empty table, unless it is already set: 1.916 lua_settop(L, 1); 1.917 lua_pushliteral(L, "list"); // 2 1.918 lua_rawget(L, 1); // 2 1.919 @@ -1143,6 +1340,7 @@ 1.920 lua_newtable(L); // 4 1.921 lua_rawset(L, 1); 1.922 } 1.923 + // set "references" attribute to empty table, unless it is already set: 1.924 lua_settop(L, 1); 1.925 lua_pushliteral(L, "references"); // 2 1.926 lua_rawget(L, 1); // 2 1.927 @@ -1151,6 +1349,7 @@ 1.928 lua_newtable(L); // 4 1.929 lua_rawset(L, 1); 1.930 } 1.931 + // set "foreign_keys" attribute to empty table, unless it is already set: 1.932 lua_settop(L, 1); 1.933 lua_pushliteral(L, "foreign_keys"); // 2 1.934 lua_rawget(L, 1); // 2 1.935 @@ -1159,51 +1358,67 @@ 1.936 lua_newtable(L); // 4 1.937 lua_rawset(L, 1); 1.938 } 1.939 + // return table: 1.940 lua_settop(L, 1); 1.941 return 1; 1.942 } 1.943 1.944 +// method "get_reference" of classes (models): 1.945 static int mondelefant_class_get_reference(lua_State *L) { 1.946 lua_settop(L, 2); 1.947 while (lua_toboolean(L, 1)) { 1.948 + // get "references" table: 1.949 lua_getfield(L, 1, "references"); // 3 1.950 + // perform lookup: 1.951 lua_pushvalue(L, 2); // 4 1.952 lua_gettable(L, 3); // 4 1.953 + // return result, if lookup was successful: 1.954 if (!lua_isnil(L, 4)) return 1; 1.955 + // replace current table by its prototype: 1.956 lua_settop(L, 2); 1.957 lua_pushliteral(L, "prototype"); // 3 1.958 lua_rawget(L, 1); // 3 1.959 lua_replace(L, 1); 1.960 } 1.961 + // return nothing: 1.962 return 0; 1.963 } 1.964 1.965 +// method "iterate_over_references" of classes (models): 1.966 static int mondelefant_class_iterate_over_references(lua_State *L) { 1.967 return luaL_error(L, "Reference iterator not implemented yet."); // TODO 1.968 } 1.969 1.970 +// method "get_foreign_key_reference_name" of classes (models): 1.971 static int mondelefant_class_get_foreign_key_reference_name(lua_State *L) { 1.972 lua_settop(L, 2); 1.973 while (lua_toboolean(L, 1)) { 1.974 + // get "foreign_keys" table: 1.975 lua_getfield(L, 1, "foreign_keys"); // 3 1.976 + // perform lookup: 1.977 lua_pushvalue(L, 2); // 4 1.978 lua_gettable(L, 3); // 4 1.979 + // return result, if lookup was successful: 1.980 if (!lua_isnil(L, 4)) return 1; 1.981 + // replace current table by its prototype: 1.982 lua_settop(L, 2); 1.983 lua_pushliteral(L, "prototype"); // 3 1.984 lua_rawget(L, 1); // 3 1.985 lua_replace(L, 1); 1.986 } 1.987 + // return nothing: 1.988 return 0; 1.989 } 1.990 1.991 +// meta-method "__index" of database result lists and objects: 1.992 static int mondelefant_result_index(lua_State *L) { 1.993 const char *result_type; 1.994 - lua_settop(L, 2); 1.995 + // only lookup, when key is a string not beginning with an underscore: 1.996 if (lua_type(L, 2) != LUA_TSTRING || lua_tostring(L, 2)[0] == '_') { 1.997 - lua_rawget(L, 1); 1.998 - return 1; 1.999 + return 0; 1.1000 } 1.1001 + // get value of "_class" attribute, or default class, when unset: 1.1002 + lua_settop(L, 2); 1.1003 lua_getfield(L, 1, "_class"); // 3 1.1004 if (!lua_toboolean(L, 3)) { 1.1005 lua_settop(L, 2); 1.1006 @@ -1212,9 +1427,11 @@ 1.1007 MONDELEFANT_CLASS_PROTO_REGKEY 1.1008 ); // 3 1.1009 } 1.1010 + // get value of "_type" attribute: 1.1011 lua_getfield(L, 1, "_type"); // 4 1.1012 result_type = lua_tostring(L, 4); 1.1013 - if (result_type && !strcmp(result_type, "object")) { 1.1014 + // different lookup for lists and objects: 1.1015 + if (result_type && !strcmp(result_type, "object")) { // object 1.1016 lua_settop(L, 3); 1.1017 // try inherited attributes, methods or getter functions: 1.1018 while (lua_toboolean(L, 3)) { 1.1019 @@ -1303,7 +1520,7 @@ 1.1020 return 1; 1.1021 } 1.1022 return 0; 1.1023 - } else if (result_type && !strcmp(result_type, "list")) { 1.1024 + } else if (result_type && !strcmp(result_type, "list")) { // list 1.1025 lua_settop(L, 3); 1.1026 // try inherited list attributes or methods: 1.1027 while (lua_toboolean(L, 3)) { 1.1028 @@ -1317,16 +1534,20 @@ 1.1029 lua_replace(L, 3); 1.1030 } 1.1031 } 1.1032 + // return nothing: 1.1033 return 0; 1.1034 } 1.1035 1.1036 +// meta-method "__newindex" of database result lists and objects: 1.1037 static int mondelefant_result_newindex(lua_State *L) { 1.1038 const char *result_type; 1.1039 + // perform rawset, unless key is a string not starting with underscore: 1.1040 lua_settop(L, 3); 1.1041 if (lua_type(L, 2) != LUA_TSTRING || lua_tostring(L, 2)[0] == '_') { 1.1042 lua_rawset(L, 1); 1.1043 return 1; 1.1044 } 1.1045 + // get value of "_class" attribute, or default class, when unset: 1.1046 lua_getfield(L, 1, "_class"); // 4 1.1047 if (!lua_toboolean(L, 4)) { 1.1048 lua_settop(L, 3); 1.1049 @@ -1335,9 +1556,11 @@ 1.1050 MONDELEFANT_CLASS_PROTO_REGKEY 1.1051 ); // 4 1.1052 } 1.1053 + // get value of "_type" attribute: 1.1054 lua_getfield(L, 1, "_type"); // 5 1.1055 result_type = lua_tostring(L, 5); 1.1056 - if (result_type && !strcmp(result_type, "object")) { 1.1057 + // distinguish between lists and objects: 1.1058 + if (result_type && !strcmp(result_type, "object")) { // objects 1.1059 lua_settop(L, 4); 1.1060 // try object setter functions: 1.1061 while (lua_toboolean(L, 4)) { 1.1062 @@ -1412,15 +1635,17 @@ 1.1063 lua_settable(L, 5); 1.1064 } 1.1065 return 0; 1.1066 - } else { 1.1067 + } else { // non-objects (i.e. lists) 1.1068 + // perform rawset: 1.1069 lua_settop(L, 3); 1.1070 lua_rawset(L, 1); 1.1071 return 0; 1.1072 } 1.1073 - return 0; 1.1074 } 1.1075 1.1076 +// meta-method "__index" of classes (models): 1.1077 static int mondelefant_class_index(lua_State *L) { 1.1078 + // perform lookup in prototype: 1.1079 lua_settop(L, 2); 1.1080 lua_pushliteral(L, "prototype"); // 3 1.1081 lua_rawget(L, 1); // 3 1.1082 @@ -1429,6 +1654,7 @@ 1.1083 return 1; 1.1084 } 1.1085 1.1086 +// registration information for functions of library: 1.1087 static const struct luaL_Reg mondelefant_module_functions[] = { 1.1088 {"connect", mondelefant_connect}, 1.1089 {"set_class", mondelefant_set_class}, 1.1090 @@ -1436,6 +1662,7 @@ 1.1091 {NULL, NULL} 1.1092 }; 1.1093 1.1094 +// registration information for meta-methods of database connections: 1.1095 static const struct luaL_Reg mondelefant_conn_mt_functions[] = { 1.1096 {"__gc", mondelefant_conn_free}, 1.1097 {"__index", mondelefant_conn_index}, 1.1098 @@ -1443,6 +1670,7 @@ 1.1099 {NULL, NULL} 1.1100 }; 1.1101 1.1102 +// registration information for methods of database connections: 1.1103 static const struct luaL_Reg mondelefant_conn_methods[] = { 1.1104 {"close", mondelefant_conn_close}, 1.1105 {"is_ok", mondelefant_conn_is_ok}, 1.1106 @@ -1457,27 +1685,32 @@ 1.1107 {NULL, NULL} 1.1108 }; 1.1109 1.1110 +// registration information for meta-methods of error objects: 1.1111 static const struct luaL_Reg mondelefant_errorobject_mt_functions[] = { 1.1112 {NULL, NULL} 1.1113 }; 1.1114 1.1115 +// registration information for methods of error objects: 1.1116 static const struct luaL_Reg mondelefant_errorobject_methods[] = { 1.1117 {"escalate", mondelefant_errorobject_escalate}, 1.1118 {"is_kind_of", mondelefant_errorobject_is_kind_of}, 1.1119 {NULL, NULL} 1.1120 }; 1.1121 1.1122 +// registration information for meta-methods of database result lists/objects: 1.1123 static const struct luaL_Reg mondelefant_result_mt_functions[] = { 1.1124 {"__index", mondelefant_result_index}, 1.1125 {"__newindex", mondelefant_result_newindex}, 1.1126 {NULL, NULL} 1.1127 }; 1.1128 1.1129 +// registration information for methods of database result lists/objects: 1.1130 static const struct luaL_Reg mondelefant_class_mt_functions[] = { 1.1131 {"__index", mondelefant_class_index}, 1.1132 {NULL, NULL} 1.1133 }; 1.1134 1.1135 +// registration information for methods of classes (models): 1.1136 static const struct luaL_Reg mondelefant_class_methods[] = { 1.1137 {"get_reference", mondelefant_class_get_reference}, 1.1138 {"iterate_over_references", mondelefant_class_iterate_over_references}, 1.1139 @@ -1486,14 +1719,17 @@ 1.1140 {NULL, NULL} 1.1141 }; 1.1142 1.1143 +// registration information for methods of database result objects (not lists!): 1.1144 static const struct luaL_Reg mondelefant_object_methods[] = { 1.1145 {NULL, NULL} 1.1146 }; 1.1147 1.1148 +// registration information for methods of database result lists (not single objects!): 1.1149 static const struct luaL_Reg mondelefant_list_methods[] = { 1.1150 {NULL, NULL} 1.1151 }; 1.1152 1.1153 +// luaopen function to initialize/register library: 1.1154 int luaopen_mondelefant_native(lua_State *L) { 1.1155 lua_settop(L, 0); 1.1156 lua_newtable(L); // module at stack position 1