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

Impressum / About Us