webmcp
view libraries/mondelefant/mondelefant_native.c @ 29:0b7e87f2dc91
add trace.debug_traceback() function
It inserts a traceback into the trace debug log. Usefull on development of unknown code.
It inserts a traceback into the trace debug log. Usefull on development of unknown code.
| author | Daniel Poelzleithner <poelzi@poelzi.org> | 
|---|---|
| date | Mon Sep 20 15:29:23 2010 +0200 (2010-09-20) | 
| parents | 3a6fe8663b26 | 
| children | ed00b972f40e | 
 line source
     1 #include <lua.h>
     2 #include <lauxlib.h>
     3 #include <libpq-fe.h>
     4 #include <postgres.h>
     5 #include <catalog/pg_type.h>
     6 #include <stdint.h>
     8 // NOTE: Comments with format "// <number>" denote the Lua stack position
    10 // prefix for all Lua registry entries of this library:
    11 #define MONDELEFANT_REGKEY "e449ba8d9a53d353_mondelefant_"
    13 // registry key of module "mondelefant_native":
    14 #define MONDELEFANT_MODULE_REGKEY (MONDELEFANT_REGKEY "module")
    15 // registry key of meta-table for database connections:
    16 #define MONDELEFANT_CONN_MT_REGKEY (MONDELEFANT_REGKEY "connection")
    17 // registry key of table storing connection specific data:
    18 #define MONDELEFANT_CONN_DATA_REGKEY (MONDELEFANT_REGKEY "connection_data")
    19 // registry key of meta-table for database result lists and objects:
    20 #define MONDELEFANT_RESULT_MT_REGKEY (MONDELEFANT_REGKEY "result")
    21 // registry key of meta-table for database error objects:
    22 #define MONDELEFANT_ERROROBJECT_MT_REGKEY (MONDELEFANT_REGKEY "errorobject")
    23 // registry key of meta-table for models (named classes here):
    24 #define MONDELEFANT_CLASS_MT_REGKEY (MONDELEFANT_REGKEY "class")
    25 // registry key of default prototype for models/classes:
    26 #define MONDELEFANT_CLASS_PROTO_REGKEY (MONDELEFANT_REGKEY "class_proto")
    28 // C-structure for database connection userdata:
    29 typedef struct {
    30   PGconn *pgconn;
    31   int server_encoding;
    32 } mondelefant_conn_t;
    33 #define MONDELEFANT_SERVER_ENCODING_ASCII 0
    34 #define MONDELEFANT_SERVER_ENCODING_UTF8  1
    36 // transform codepoint-position to byte-position for a given UTF-8 string:
    37 static size_t utf8_position_to_byte(const char *str, size_t utf8pos) {
    38   size_t bytepos;
    39   for (bytepos = 0; utf8pos > 0; bytepos++) {
    40     uint8_t c;
    41     c = ((const uint8_t *)str)[bytepos];
    42     if (!c) break;
    43     if (c <= 0x7f || c >= 0xc0) utf8pos--;
    44   }
    45   return bytepos;
    46 }
    48 // PostgreSQL's OID for binary data type (bytea):
    49 #define MONDELEFANT_POSTGRESQL_BINARY_OID ((Oid)17)
    51 // mapping a PostgreSQL type given by its OID to a string identifier:
    52 static const char *mondelefant_oid_to_typestr(Oid oid) {
    53   switch (oid) {
    54     case 16: return "bool";
    55     case 17: return "bytea";
    56     case 18: return "char";
    57     case 19: return "name";
    58     case 20: return "int8";
    59     case 21: return "int2";
    60     case 23: return "int4";
    61     case 25: return "text";
    62     case 26: return "oid";
    63     case 27: return "tid";
    64     case 28: return "xid";
    65     case 29: return "cid";
    66     case 600: return "point";
    67     case 601: return "lseg";
    68     case 602: return "path";
    69     case 603: return "box";
    70     case 604: return "polygon";
    71     case 628: return "line";
    72     case 700: return "float4";
    73     case 701: return "float8";
    74     case 705: return "unknown";
    75     case 718: return "circle";
    76     case 790: return "money";
    77     case 829: return "macaddr";
    78     case 869: return "inet";
    79     case 650: return "cidr";
    80     case 1042: return "bpchar";
    81     case 1043: return "varchar";
    82     case 1082: return "date";
    83     case 1083: return "time";
    84     case 1114: return "timestamp";
    85     case 1184: return "timestamptz";
    86     case 1186: return "interval";
    87     case 1266: return "timetz";
    88     case 1560: return "bit";
    89     case 1562: return "varbit";
    90     case 1700: return "numeric";
    91     default: return NULL;
    92   }
    93 }
    95 // This library maps PostgreSQL's error codes to CamelCase string
    96 // identifiers, which consist of CamelCase identifiers and are seperated
    97 // by dots (".") (no leading or trailing dots).
    98 // There are additional error identifiers which do not have a corresponding
    99 // PostgreSQL error associated with it.
   101 // matching start of local variable 'pgcode' against string 'incode',
   102 // returning string 'outcode' on match:
   103 #define mondelefant_errcode_item(incode, outcode) \
   104   if (!strncmp(pgcode, (incode), strlen(incode))) return outcode; else
   106 // additional error identifiers without corresponding PostgreSQL error:
   107 #define MONDELEFANT_ERRCODE_UNKNOWN "unknown"
   108 #define MONDELEFANT_ERRCODE_CONNECTION "ConnectionException"
   109 #define MONDELEFANT_ERRCODE_RESULTCOUNT_LOW "WrongResultSetCount.ResultSetMissing"
   110 #define MONDELEFANT_ERRCODE_RESULTCOUNT_HIGH "WrongResultSetCount.TooManyResults"
   111 #define MONDELEFANT_ERRCODE_QUERY1_NO_ROWS "NoData.OneRowExpected"
   112 #define MONDELEFANT_ERRCODE_QUERY1_MULTIPLE_ROWS "CardinalityViolation.OneRowExpected"
   114 // mapping PostgreSQL error code to error code as returned by this library:
   115 static const char *mondelefant_translate_errcode(const char *pgcode) {
   116   if (!pgcode) abort();  // should not happen
   117   mondelefant_errcode_item("02", "NoData")
   118   mondelefant_errcode_item("03", "SqlStatementNotYetComplete")
   119   mondelefant_errcode_item("08", "ConnectionException")
   120   mondelefant_errcode_item("09", "TriggeredActionException")
   121   mondelefant_errcode_item("0A", "FeatureNotSupported")
   122   mondelefant_errcode_item("0B", "InvalidTransactionInitiation")
   123   mondelefant_errcode_item("0F", "LocatorException")
   124   mondelefant_errcode_item("0L", "InvalidGrantor")
   125   mondelefant_errcode_item("0P", "InvalidRoleSpecification")
   126   mondelefant_errcode_item("21", "CardinalityViolation")
   127   mondelefant_errcode_item("22", "DataException")
   128   mondelefant_errcode_item("23001", "IntegrityConstraintViolation.RestrictViolation")
   129   mondelefant_errcode_item("23502", "IntegrityConstraintViolation.NotNullViolation")
   130   mondelefant_errcode_item("23503", "IntegrityConstraintViolation.ForeignKeyViolation")
   131   mondelefant_errcode_item("23505", "IntegrityConstraintViolation.UniqueViolation")
   132   mondelefant_errcode_item("23514", "IntegrityConstraintViolation.CheckViolation")
   133   mondelefant_errcode_item("23",    "IntegrityConstraintViolation")
   134   mondelefant_errcode_item("24", "InvalidCursorState")
   135   mondelefant_errcode_item("25", "InvalidTransactionState")
   136   mondelefant_errcode_item("26", "InvalidSqlStatementName")
   137   mondelefant_errcode_item("27", "TriggeredDataChangeViolation")
   138   mondelefant_errcode_item("28", "InvalidAuthorizationSpecification")
   139   mondelefant_errcode_item("2B", "DependentPrivilegeDescriptorsStillExist")
   140   mondelefant_errcode_item("2D", "InvalidTransactionTermination")
   141   mondelefant_errcode_item("2F", "SqlRoutineException")
   142   mondelefant_errcode_item("34", "InvalidCursorName")
   143   mondelefant_errcode_item("38", "ExternalRoutineException")
   144   mondelefant_errcode_item("39", "ExternalRoutineInvocationException")
   145   mondelefant_errcode_item("3B", "SavepointException")
   146   mondelefant_errcode_item("3D", "InvalidCatalogName")
   147   mondelefant_errcode_item("3F", "InvalidSchemaName")
   148   mondelefant_errcode_item("40", "TransactionRollback")
   149   mondelefant_errcode_item("42", "SyntaxErrorOrAccessRuleViolation")
   150   mondelefant_errcode_item("44", "WithCheckOptionViolation")
   151   mondelefant_errcode_item("53", "InsufficientResources")
   152   mondelefant_errcode_item("54", "ProgramLimitExceeded")
   153   mondelefant_errcode_item("55", "ObjectNotInPrerequisiteState")
   154   mondelefant_errcode_item("57", "OperatorIntervention")
   155   mondelefant_errcode_item("58", "SystemError")
   156   mondelefant_errcode_item("F0", "ConfigurationFileError")
   157   mondelefant_errcode_item("P0", "PlpgsqlError")
   158   mondelefant_errcode_item("XX", "InternalError")
   159   return "unknown";
   160 }
   162 // C-function, checking if a given error code (as defined by this library)
   163 // is belonging to a certain class of errors (strings are equal or error
   164 // code begins with error class followed by a dot):
   165 static int mondelefant_check_error_class(
   166   const char *errcode, const char *errclass
   167 ) {
   168   size_t i = 0;
   169   while (1) {
   170     if (errclass[i] == 0) {
   171       if (errcode[i] == 0 || errcode[i] == '.') return 1;
   172       else return 0;
   173     }
   174     if (errcode[i] != errclass[i]) return 0;
   175     i++;
   176   }
   177 }
   179 // pushing first line of a string on Lua's stack (without trailing CR/LF):
   180 static void mondelefant_push_first_line(lua_State *L, const char *str) {
   181   char *str2;
   182   size_t i = 0;
   183   if (!str) abort();  // should not happen
   184   str2 = strdup(str);
   185   while (1) {
   186     char c = str2[i];
   187     if (c == '\n' || c == '\r' || c == 0) { str2[i] = 0; break; }
   188     i++;
   189   };
   190   lua_pushstring(L, str2);
   191   free(str2);
   192 }
   194 // "connect" function of library, which establishes a database connection
   195 // and returns a database connection handle:
   196 static int mondelefant_connect(lua_State *L) {
   197   luaL_Buffer buf;  // Lua string buffer to create 'conninfo' (see below)
   198   const char *conninfo;  // string for PQconnectdb function
   199   PGconn *pgconn;  // PGconn object as returned by PQconnectdb function
   200   mondelefant_conn_t *conn;  // C-structure for userdata
   201   // if engine is anything but "postgresql", then raise error:
   202   lua_settop(L, 1);
   203   lua_getfield(L, 1, "engine");  // 2
   204   if (!lua_toboolean(L, 2)) {
   205     return luaL_error(L, "No database engine selected.");
   206   }
   207   lua_pushliteral(L, "postgresql");  // 3
   208   if (!lua_rawequal(L, 2, 3)) {
   209     return luaL_error(L,
   210       "Only database engine 'postgresql' is supported."
   211     );
   212   }
   213   // create 'conninfo' string for PQconnectdb function, which contains all
   214   // options except "engine" option:
   215   lua_settop(L, 1);
   216   lua_pushnil(L);  // slot for key at stack position 2
   217   lua_pushnil(L);  // slot for value at stack position 3
   218   luaL_buffinit(L, &buf);
   219   {
   220     int need_seperator = 0;
   221     while (lua_pushvalue(L, 2), lua_next(L, 1)) {
   222       lua_replace(L, 3);
   223       lua_replace(L, 2);
   224       // NOTE: numbers will be converted to strings automatically here,
   225       // but perhaps this will change in future versions of lua
   226       luaL_argcheck(L,
   227         lua_isstring(L, 2) && lua_isstring(L, 3), 1, "non-string contained"
   228       );
   229       lua_pushvalue(L, 2);
   230       lua_pushliteral(L, "engine");
   231       if (!lua_rawequal(L, -2, -1)) {
   232         const char *value;
   233         size_t value_len;
   234         size_t value_pos = 0;
   235         lua_pop(L, 1);
   236         if (need_seperator) luaL_addchar(&buf, ' ');
   237         luaL_addvalue(&buf);
   238         luaL_addchar(&buf, '=');
   239         luaL_addchar(&buf, '\'');
   240         value = lua_tolstring(L, 3, &value_len);
   241         do {
   242           char c;
   243           c = value[value_pos++];
   244           if (c == '\'') luaL_addchar(&buf, '\\');
   245           luaL_addchar(&buf, c);
   246         } while (value_pos < value_len);
   247         luaL_addchar(&buf, '\'');
   248         need_seperator = 1;
   249       } else {
   250         lua_pop(L, 1);
   251       }
   252     }
   253   }
   254   luaL_pushresult(&buf);
   255   lua_replace(L, 2);
   256   lua_settop(L, 2);
   257   conninfo = lua_tostring(L, 2);
   258   // call PQconnectdb function of libpq:
   259   pgconn = PQconnectdb(conninfo);
   260   // throw errors, if neccessary:
   261   if (!pgconn) {
   262     return luaL_error(L,
   263       "Error in libpq while creating 'PGconn' structure."
   264     );
   265   }
   266   if (PQstatus(pgconn) != CONNECTION_OK) {
   267     const char *errmsg;
   268     lua_pushnil(L);
   269     errmsg = PQerrorMessage(pgconn);
   270     if (errmsg) {
   271       mondelefant_push_first_line(L, errmsg);
   272     } else {
   273       lua_pushliteral(L,
   274         "Error while connecting to database, but no error message given."
   275       );
   276     }
   277     lua_pushliteral(L, MONDELEFANT_ERRCODE_CONNECTION);
   278     PQfinish(pgconn);
   279     return 3;
   280   }
   281   // create userdata:
   282   lua_settop(L, 0);
   283   conn = lua_newuserdata(L, sizeof(*conn));  // 1
   284   // set 'pgconn' in C-struct of userdata:
   285   conn->pgconn = pgconn;
   286   // set 'server_encoding' in C-struct of userdata:
   287   {
   288     const char *charset;
   289     charset = PQparameterStatus(pgconn, "server_encoding");
   290     if (charset && !strcmp(charset, "UTF8")) {
   291       conn->server_encoding = MONDELEFANT_SERVER_ENCODING_UTF8;
   292     } else {
   293       conn->server_encoding = MONDELEFANT_SERVER_ENCODING_ASCII;
   294     }
   295   }
   296   // set meta-table of userdata:
   297   luaL_getmetatable(L, MONDELEFANT_CONN_MT_REGKEY);  // 2
   298   lua_setmetatable(L, 1);
   299   // create entry in table storing connection specific data and associate
   300   // created userdata with it:
   301   lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_CONN_DATA_REGKEY);  // 2
   302   lua_pushvalue(L, 1);  // 3
   303   lua_newtable(L);  // 4
   304   lua_settable(L, 2);
   305   lua_settop(L, 1);
   306   // store key "engine" with value "postgresql" as connection specific data:
   307   lua_pushliteral(L, "postgresql");
   308   lua_setfield(L, 1, "engine");
   309   // return userdata:
   310   return 1;
   311 }
   313 // returns pointer to libpq handle 'pgconn' of userdata at given index
   314 // (or throws error, if database connection has been closed):
   315 static mondelefant_conn_t *mondelefant_get_conn(lua_State *L, int index) {
   316   mondelefant_conn_t *conn;
   317   conn = luaL_checkudata(L, index, MONDELEFANT_CONN_MT_REGKEY);
   318   if (!conn->pgconn) {
   319     luaL_error(L, "PostgreSQL connection has been closed.");
   320     return NULL;
   321   }
   322   return conn;
   323 }
   325 // meta-method "__index" of database handles (userdata):
   326 static int mondelefant_conn_index(lua_State *L) {
   327   // try table for connection specific data:
   328   lua_settop(L, 2);
   329   lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_CONN_DATA_REGKEY);  // 3
   330   lua_pushvalue(L, 1);  // 4
   331   lua_gettable(L, 3);  // 4
   332   lua_remove(L, 3);  // connection specific data-table at stack position 3
   333   lua_pushvalue(L, 2);  // 4
   334   lua_gettable(L, 3);  // 4
   335   if (!lua_isnil(L, 4)) return 1;
   336   // try to use prototype stored in connection specific data:
   337   lua_settop(L, 3);
   338   lua_getfield(L, 3, "prototype");  // 4
   339   if (lua_toboolean(L, 4)) {
   340     lua_pushvalue(L, 2);  // 5
   341     lua_gettable(L, 4);  // 5
   342     if (!lua_isnil(L, 5)) return 1;
   343   }
   344   // try to use "postgresql_connection_prototype" of library:
   345   lua_settop(L, 2);
   346   lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_MODULE_REGKEY);  // 3
   347   lua_getfield(L, 3, "postgresql_connection_prototype");  // 4
   348   if (lua_toboolean(L, 4)) {
   349     lua_pushvalue(L, 2);  // 5
   350     lua_gettable(L, 4);  // 5
   351     if (!lua_isnil(L, 5)) return 1;
   352   }
   353   // try to use "connection_prototype" of library:
   354   lua_settop(L, 3);
   355   lua_getfield(L, 3, "connection_prototype");  // 4
   356   if (lua_toboolean(L, 4)) {
   357     lua_pushvalue(L, 2);  // 5
   358     lua_gettable(L, 4);  // 5
   359     if (!lua_isnil(L, 5)) return 1;
   360   }
   361   // give up and return nothing:
   362   return 0;
   363 }
   365 // meta-method "__newindex" of database handles (userdata):
   366 static int mondelefant_conn_newindex(lua_State *L) {
   367   // store key-value pair in table for connection specific data:
   368   lua_settop(L, 3);
   369   lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_CONN_DATA_REGKEY);  // 4
   370   lua_pushvalue(L, 1);  // 5
   371   lua_gettable(L, 4);  // 5
   372   lua_remove(L, 4);  // connection specific data-table at stack position 4
   373   lua_pushvalue(L, 2);
   374   lua_pushvalue(L, 3);
   375   lua_settable(L, 4);
   376   // return nothing:
   377   return 0;
   378 }
   380 // meta-method "__gc" of database handles:
   381 static int mondelefant_conn_free(lua_State *L) {
   382   mondelefant_conn_t *conn;
   383   conn = luaL_checkudata(L, 1, MONDELEFANT_CONN_MT_REGKEY);
   384   if (conn->pgconn) PQfinish(conn->pgconn);
   385   conn->pgconn = NULL;
   386   return 0;
   387 }
   389 // method "close" of database handles:
   390 static int mondelefant_conn_close(lua_State *L) {
   391   mondelefant_conn_t *conn;
   392   lua_settop(L, 1);
   393   conn = mondelefant_get_conn(L, 1);
   394   PQfinish(conn->pgconn);
   395   conn->pgconn = NULL;
   396   return 0;
   397 }
   399 // method "is_okay" of database handles:
   400 static int mondelefant_conn_is_ok(lua_State *L) {
   401   mondelefant_conn_t *conn;
   402   lua_settop(L, 1);
   403   conn = mondelefant_get_conn(L, 1);
   404   lua_pushboolean(L, PQstatus(conn->pgconn) == CONNECTION_OK);
   405   return 1;
   406 }
   408 // method "get_transaction_status" of database handles:
   409 static int mondelefant_conn_get_transaction_status(lua_State *L) {
   410   mondelefant_conn_t *conn;
   411   lua_settop(L, 1);
   412   conn = mondelefant_get_conn(L, 1);
   413   switch (PQtransactionStatus(conn->pgconn)) {
   414   case PQTRANS_IDLE:
   415     lua_pushliteral(L, "idle");
   416     break;
   417   case PQTRANS_ACTIVE:
   418     lua_pushliteral(L, "active");
   419     break;
   420   case PQTRANS_INTRANS:
   421     lua_pushliteral(L, "intrans");
   422     break;
   423   case PQTRANS_INERROR:
   424     lua_pushliteral(L, "inerror");
   425     break;
   426   default:
   427     lua_pushliteral(L, "unknown");
   428   }
   429   return 1;
   430 }
   432 // method "create_list" of database handles:
   433 static int mondelefant_conn_create_list(lua_State *L) {
   434   // ensure that first argument is a database connection:
   435   lua_settop(L, 2);
   436   luaL_checkudata(L, 1, MONDELEFANT_CONN_MT_REGKEY);
   437   // if no second argument is given, use an empty table:
   438   if (!lua_toboolean(L, 2)) {
   439     lua_newtable(L);
   440     lua_replace(L, 2);  // new result at stack position 2
   441   }
   442   // set meta-table for database result lists/objects:
   443   lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_RESULT_MT_REGKEY);  // 3
   444   lua_setmetatable(L, 2);
   445   // set "_connection" attribute to self:
   446   lua_pushvalue(L, 1);  // 3
   447   lua_setfield(L, 2, "_connection");
   448   // set "_type" attribute to string "list":
   449   lua_pushliteral(L, "list");  // 3
   450   lua_setfield(L, 2, "_type");
   451   // return created database result list:
   452   return 1;
   453 }
   455 // method "create_object" of database handles:
   456 static int mondelefant_conn_create_object(lua_State *L) {
   457   // ensure that first argument is a database connection:
   458   lua_settop(L, 2);
   459   luaL_checkudata(L, 1, MONDELEFANT_CONN_MT_REGKEY);
   460   // if no second argument is given, use an empty table:
   461   if (!lua_toboolean(L, 2)) {
   462     lua_newtable(L);
   463     lua_replace(L, 2);  // new result at stack position 2
   464   }
   465   //   set meta-table for database result lists/objects:
   466   lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_RESULT_MT_REGKEY);  // 3
   467   lua_setmetatable(L, 2);
   468   // set "_connection" attribute to self:
   469   lua_pushvalue(L, 1);  // 3
   470   lua_setfield(L, 2, "_connection");
   471   // set "_type" attribute to string "object":
   472   lua_pushliteral(L, "object");  // 3
   473   lua_setfield(L, 2, "_type");  // "object" or "list"
   474   // create empty tables for "_data", "_dirty" and "_ref" attributes:
   475   lua_newtable(L);  // 3
   476   lua_setfield(L, 2, "_data");
   477   lua_newtable(L);  // 3
   478   lua_setfield(L, 2, "_dirty");
   479   lua_newtable(L);  // 3
   480   lua_setfield(L, 2, "_ref");  // nil=no info, false=nil, else table
   481   // return created database result object:
   482   return 1;
   483 }
   485 // method "quote_string" of database handles:
   486 static int mondelefant_conn_quote_string(lua_State *L) {
   487   mondelefant_conn_t *conn;
   488   const char *input;
   489   size_t input_len;
   490   char *output;
   491   size_t output_len;
   492   // get 'conn' attribute of C-struct of database connection:
   493   lua_settop(L, 2);
   494   conn = mondelefant_get_conn(L, 1);
   495   // get second argument, which must be a string:
   496   input = luaL_checklstring(L, 2, &input_len);
   497   // throw error, if string is too long:
   498   if (input_len > (SIZE_MAX / sizeof(char) - 3) / 2) {
   499     return luaL_error(L, "String to be escaped is too long.");
   500   }
   501   // allocate memory for quoted string:
   502   output = malloc((2 * input_len + 3) * sizeof(char));
   503   if (!output) {
   504     return luaL_error(L, "Could not allocate memory for string quoting.");
   505   }
   506   // do escaping by calling PQescapeStringConn and enclosing result with
   507   // single quotes:
   508   output[0] = '\'';
   509   output_len = PQescapeStringConn(
   510     conn->pgconn, output + 1, input, input_len, NULL
   511   );
   512   output[output_len + 1] = '\'';
   513   output[output_len + 2] = 0;
   514   // create Lua string:
   515   lua_pushlstring(L, output, output_len + 2);
   516   // free allocated memory:
   517   free(output);
   518   // return Lua string:
   519   return 1;
   520 }
   522 // method "quote_binary" of database handles:
   523 static int mondelefant_conn_quote_binary(lua_State *L) {
   524   mondelefant_conn_t *conn;
   525   const char *input;
   526   size_t input_len;
   527   char *output;
   528   size_t output_len;
   529   luaL_Buffer buf;
   530   // get 'conn' attribute of C-struct of database connection:
   531   lua_settop(L, 2);
   532   conn = mondelefant_get_conn(L, 1);
   533   // get second argument, which must be a string:
   534   input = luaL_checklstring(L, 2, &input_len);
   535   // call PQescapeByteaConn, which allocates memory itself:
   536   output = (char *)PQescapeByteaConn(
   537     conn->pgconn, (const unsigned char *)input, input_len, &output_len
   538   );
   539   // if PQescapeByteaConn returned NULL, then throw error:
   540   if (!output) {
   541     return luaL_error(L, "Could not allocate memory for binary quoting.");
   542   }
   543   // create Lua string enclosed by single quotes:
   544   luaL_buffinit(L, &buf);
   545   luaL_addchar(&buf, '\'');
   546   luaL_addlstring(&buf, output, output_len - 1);
   547   luaL_addchar(&buf, '\'');
   548   luaL_pushresult(&buf);
   549   // free memory allocated by PQescapeByteaConn:
   550   PQfreemem(output);
   551   // return Lua string:
   552   return 1;
   553 }
   555 // method "assemble_command" of database handles:
   556 static int mondelefant_conn_assemble_command(lua_State *L) {
   557   mondelefant_conn_t *conn;
   558   int paramidx = 2;
   559   const char *template;
   560   size_t template_pos = 0;
   561   luaL_Buffer buf;
   562   // get 'conn' attribute of C-struct of database connection:
   563   lua_settop(L, 2);
   564   conn = mondelefant_get_conn(L, 1);
   565   // if second argument is a string, return this string:
   566   if (lua_isstring(L, 2)) {
   567     lua_tostring(L, 2);
   568     return 1;
   569   }
   570   // if second argument has __tostring meta-method,
   571   // then use this method and return its result:
   572   if (luaL_callmeta(L, 2, "__tostring")) return 1;
   573   // otherwise, require that second argument is a table:
   574   luaL_checktype(L, 2, LUA_TTABLE);
   575   // get first element of table, which must be a string:
   576   lua_rawgeti(L, 2, 1);  // 3
   577   luaL_argcheck(L,
   578     lua_isstring(L, 3),
   579     2,
   580     "First entry of SQL command structure is not a string."
   581   );
   582   template = lua_tostring(L, 3);
   583   // get value of "input_converter" attribute of database connection:
   584   lua_pushliteral(L, "input_converter");  // 4
   585   lua_gettable(L, 1);  // input_converter at stack position 4
   586   // reserve space on Lua stack:
   587   lua_pushnil(L);  // free space at stack position 5
   588   lua_pushnil(L);  // free space at stack position 6
   589   // initialize Lua buffer for result string:
   590   luaL_buffinit(L, &buf);
   591   // fill buffer in loop:
   592   while (1) {
   593     // variable declaration:
   594     char c;
   595     // get next character:
   596     c = template[template_pos++];
   597     // break, when character is NULL byte:
   598     if (!c) break;
   599     // question-mark and dollar-sign are special characters:
   600     if (c == '?' || c == '$') {  // special character found
   601       // check, if same character follows:
   602       if (template[template_pos] == c) {  // special character is escaped
   603         // consume two characters of input and add one character to buffer:
   604         template_pos++;
   605         luaL_addchar(&buf, c);
   606       } else {  // special character is not escaped
   607         luaL_Buffer keybuf;
   608         int subcmd;
   609         // set 'subcmd' = true, if special character was a dollar-sign,
   610         // set 'subcmd' = false, if special character was a question-mark:
   611         subcmd = (c == '$');
   612         // read any number of alpha numeric chars or underscores
   613         // and store them on Lua stack:
   614         luaL_buffinit(L, &keybuf);
   615         while (1) {
   616           c = template[template_pos];
   617           if (
   618             (c < 'A' || c > 'Z') &&
   619             (c < 'a' || c > 'z') &&
   620             (c < '0' || c > '9') &&
   621             (c != '_')
   622           ) break;
   623           luaL_addchar(&keybuf, c);
   624           template_pos++;
   625         }
   626         luaL_pushresult(&keybuf);
   627         // check, if any characters matched:
   628         if (lua_objlen(L, -1)) {
   629           // if any alpha numeric chars or underscores were found,
   630           // push them on stack as a Lua string and use them to lookup
   631           // value from second argument:
   632           lua_pushvalue(L, -1);           // save key on stack
   633           lua_gettable(L, 2);             // fetch value (raw-value)
   634         } else {
   635           // otherwise push nil and use numeric lookup based on 'paramidx':
   636           lua_pop(L, 1);
   637           lua_pushnil(L);                 // put nil on key position
   638           lua_rawgeti(L, 2, paramidx++);  // fetch value (raw-value)
   639         }
   640         // Lua stack contains: ..., <buffer>, key, raw-value
   641         // branch according to type of special character ("?" or "$"):
   642         if (subcmd) {  // dollar-sign
   643           size_t i;
   644           size_t count;
   645           // store fetched value (which is supposed to be sub-structure)
   646           // on Lua stack position 5 and drop key:
   647           lua_replace(L, 5);
   648           lua_pop(L, 1);
   649           // Lua stack contains: ..., <buffer>
   650           // check, if fetched value is really a sub-structure:
   651           luaL_argcheck(L,
   652             !lua_isnil(L, 5),
   653             2,
   654             "SQL sub-structure not found."
   655           );
   656           luaL_argcheck(L,
   657             lua_type(L, 5) == LUA_TTABLE,
   658             2,
   659             "SQL sub-structure must be a table."
   660           );
   661           // Lua stack contains: ..., <buffer>
   662           // get value of "sep" attribute of sub-structure,
   663           // and place it on Lua stack position 6:
   664           lua_getfield(L, 5, "sep");
   665           lua_replace(L, 6);
   666           // if seperator is nil, then use ", " as default,
   667           // if seperator is neither nil nor a string, then throw error:
   668           if (lua_isnil(L, 6)) {
   669             lua_pushstring(L, ", ");
   670             lua_replace(L, 6);
   671           } else {
   672             luaL_argcheck(L,
   673               lua_isstring(L, 6),
   674               2,
   675               "Seperator of SQL sub-structure has to be a string."
   676             );
   677           }
   678           // iterate over items of sub-structure:
   679           count = lua_objlen(L, 5);
   680           for (i = 0; i < count; i++) {
   681             // add seperator, unless this is the first run:
   682             if (i) {
   683               lua_pushvalue(L, 6);
   684               luaL_addvalue(&buf);
   685             }
   686             // recursivly apply assemble function and add results to buffer:
   687             lua_pushcfunction(L, mondelefant_conn_assemble_command);
   688             lua_pushvalue(L, 1);
   689             lua_rawgeti(L, 5, i+1);
   690             lua_call(L, 2, 1);
   691             luaL_addvalue(&buf);
   692           }
   693         } else {  // question-mark
   694           if (lua_toboolean(L, 4)) {
   695             // call input_converter with connection handle, raw-value and
   696             // an info-table which contains a "field_name" entry with the
   697             // used key:
   698             lua_pushvalue(L, 4);
   699             lua_pushvalue(L, 1);
   700             lua_pushvalue(L, -3);
   701             lua_newtable(L);
   702             lua_pushvalue(L, -6);
   703             lua_setfield(L, -2, "field_name");
   704             lua_call(L, 3, 1);
   705             // Lua stack contains: ..., <buffer>, key, raw-value, final-value
   706             // remove key and raw-value:
   707             lua_remove(L, -2);
   708             lua_remove(L, -2);
   709             // Lua stack contains: ..., <buffer>, final-value
   710             // throw error, if final-value is not a string:
   711             if (!lua_isstring(L, -1)) {
   712               return luaL_error(L, "input_converter returned non-string.");
   713             }
   714           } else {
   715             // remove key from stack:
   716             lua_remove(L, -2);
   717             // Lua stack contains: ..., <buffer>, raw-value
   718             // branch according to type of value:
   719             if (lua_isnil(L, -1)) {  // value is nil
   720               // push string "NULL" to stack:
   721               lua_pushliteral(L, "NULL");
   722             } else if (lua_type(L, -1) == LUA_TBOOLEAN) {  // value is boolean
   723               // push strings "TRUE" or "FALSE" to stack:
   724               lua_pushstring(L, lua_toboolean(L, -1) ? "TRUE" : "FALSE");
   725             } else if (lua_isstring(L, -1)) {  // value is string or number
   726               // NOTE: In this version of lua a number will be converted
   727               // push output of "quote_string" method of database
   728               // connection to stack:
   729               lua_tostring(L, -1);
   730               lua_pushcfunction(L, mondelefant_conn_quote_string);
   731               lua_pushvalue(L, 1);
   732               lua_pushvalue(L, -3);
   733               lua_call(L, 2, 1);
   734             } else {  // value is of other type
   735               // throw error:
   736               return luaL_error(L,
   737                 "Unable to convert SQL value due to unknown type "
   738                 "or missing input_converter."
   739               );
   740             }
   741             // Lua stack contains: ..., <buffer>, raw-value, final-value
   742             // remove raw-value:
   743             lua_remove(L, -2);
   744             // Lua stack contains: ..., <buffer>, final-value
   745           }
   746           // append final-value to buffer:
   747           luaL_addvalue(&buf);
   748         }
   749       }
   750     } else {  // character is not special
   751       // just copy character:
   752       luaL_addchar(&buf, c);
   753     }
   754   }
   755   // return string in buffer:
   756   luaL_pushresult(&buf);
   757   return 1;
   758 }
   760 // max number of SQL statements executed by one "query" method call:
   761 #define MONDELEFANT_MAX_COMMAND_COUNT 64
   762 // max number of columns in a database result:
   763 #define MONDELEFANT_MAX_COLUMN_COUNT 1024
   764 // enum values for 'modes' array in C-function below:
   765 #define MONDELEFANT_QUERY_MODE_LIST 1
   766 #define MONDELEFANT_QUERY_MODE_OBJECT 2
   767 #define MONDELEFANT_QUERY_MODE_OPT_OBJECT 3
   769 // method "try_query" of database handles:
   770 static int mondelefant_conn_try_query(lua_State *L) {
   771   mondelefant_conn_t *conn;
   772   int command_count;
   773   int command_idx;
   774   int modes[MONDELEFANT_MAX_COMMAND_COUNT];
   775   luaL_Buffer buf;
   776   int sent_success;
   777   PGresult *res;
   778   int rows, cols, row, col;
   779   // get 'conn' attribute of C-struct of database connection:
   780   conn = mondelefant_get_conn(L, 1);
   781   // calculate number of commands (2 arguments for one command):
   782   command_count = lua_gettop(L) / 2;
   783   // push nil on stack, which is needed, if last mode was ommitted:
   784   lua_pushnil(L);
   785   // throw error, if number of commands is too high:
   786   if (command_count > MONDELEFANT_MAX_COMMAND_COUNT) {
   787     return luaL_error(L, "Exceeded maximum command count in one query.");
   788   }
   789   // create SQL string, store query modes and push SQL string on stack:
   790   luaL_buffinit(L, &buf);
   791   for (command_idx = 0; command_idx < command_count; command_idx++) {
   792     int mode;
   793     int mode_idx;  // stack index of mode string
   794     if (command_idx) luaL_addchar(&buf, ' ');
   795     lua_pushcfunction(L, mondelefant_conn_assemble_command);
   796     lua_pushvalue(L, 1);
   797     lua_pushvalue(L, 2 + 2 * command_idx);
   798     lua_call(L, 2, 1);
   799     luaL_addvalue(&buf);
   800     luaL_addchar(&buf, ';');
   801     mode_idx = 3 + 2 * command_idx;
   802     if (lua_isnil(L, mode_idx)) {
   803       mode = MONDELEFANT_QUERY_MODE_LIST;
   804     } else {
   805       const char *modestr;
   806       modestr = luaL_checkstring(L, mode_idx);
   807       if (!strcmp(modestr, "list")) {
   808         mode = MONDELEFANT_QUERY_MODE_LIST;
   809       } else if (!strcmp(modestr, "object")) {
   810         mode = MONDELEFANT_QUERY_MODE_OBJECT;
   811       } else if (!strcmp(modestr, "opt_object")) {
   812         mode = MONDELEFANT_QUERY_MODE_OPT_OBJECT;
   813       } else {
   814         return luaL_error(L, "Unknown query mode specified.");
   815       }
   816     }
   817     modes[command_idx] = mode;
   818   }
   819   luaL_pushresult(&buf);  // stack position unknown
   820   lua_replace(L, 2);  // SQL command string to stack position 2
   821   // call sql_tracer, if set:
   822   lua_settop(L, 2);
   823   lua_getfield(L, 1, "sql_tracer");  // tracer at stack position 3
   824   if (lua_toboolean(L, 3)) {
   825     lua_pushvalue(L, 1);  // 4
   826     lua_pushvalue(L, 2);  // 5
   827     lua_call(L, 2, 1);  // trace callback at stack position 3
   828   }
   829   // NOTE: If no tracer was found, then nil or false is stored at stack
   830   // position 3.
   831   // call PQsendQuery function and store result in 'sent_success' variable:
   832   sent_success = PQsendQuery(conn->pgconn, lua_tostring(L, 2));
   833   // create preliminary result table:
   834   lua_newtable(L);  // results in table at stack position 4
   835   // iterate over results using function PQgetResult to fill result table:
   836   for (command_idx = 0; ; command_idx++) {
   837     int mode;
   838     char binary[MONDELEFANT_MAX_COLUMN_COUNT];
   839     ExecStatusType pgstatus;
   840     // fetch mode which was given for the command:
   841     mode = modes[command_idx];
   842     // if PQsendQuery call was successful, then fetch result data:
   843     if (sent_success) {
   844       // NOTE: PQgetResult called one extra time. Break only, if all
   845       // queries have been processed and PQgetResult returned NULL.
   846       res = PQgetResult(conn->pgconn);
   847       if (command_idx >= command_count && !res) break;
   848       if (res) {
   849         pgstatus = PQresultStatus(res);
   850         rows = PQntuples(res);
   851         cols = PQnfields(res);
   852       }
   853     }
   854     // handle errors:
   855     if (
   856       !sent_success || command_idx >= command_count || !res ||
   857       (pgstatus != PGRES_TUPLES_OK && pgstatus != PGRES_COMMAND_OK) ||
   858       (rows < 1 && mode == MONDELEFANT_QUERY_MODE_OBJECT) ||
   859       (rows > 1 && mode != MONDELEFANT_QUERY_MODE_LIST)
   860     ) {
   861       const char *command;
   862       command = lua_tostring(L, 2);
   863       lua_newtable(L);  // 5
   864       lua_getfield(L,
   865         LUA_REGISTRYINDEX,
   866         MONDELEFANT_ERROROBJECT_MT_REGKEY
   867       );
   868       lua_setmetatable(L, 5);
   869       lua_pushvalue(L, 1);
   870       lua_setfield(L, 5, "connection");
   871       lua_pushinteger(L, command_idx + 1);
   872       lua_setfield(L, 5, "command_number");
   873       lua_pushvalue(L, 2);
   874       lua_setfield(L, 5, "sql_command");
   875       if (!res) {
   876         lua_pushliteral(L, MONDELEFANT_ERRCODE_RESULTCOUNT_LOW);
   877         lua_setfield(L, 5, "code");
   878         lua_pushliteral(L, "Received too few database result sets.");
   879         lua_setfield(L, 5, "message");
   880       } else if (command_idx >= command_count) {
   881         lua_pushliteral(L, MONDELEFANT_ERRCODE_RESULTCOUNT_HIGH);
   882         lua_setfield(L, 5, "code");
   883         lua_pushliteral(L, "Received too many database result sets.");
   884         lua_setfield(L, 5, "message");
   885       } else if (
   886         pgstatus != PGRES_TUPLES_OK && pgstatus != PGRES_COMMAND_OK
   887       ) {
   888         const char *sqlstate;
   889         const char *errmsg;
   890         lua_pushstring(L, PQresultErrorField(res, PG_DIAG_SEVERITY));
   891         lua_setfield(L, 5, "pg_severity");
   892         sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE);
   893         if (sqlstate) {
   894           lua_pushstring(L, sqlstate);
   895           lua_setfield(L, 5, "pg_sqlstate");
   896           lua_pushstring(L, mondelefant_translate_errcode(sqlstate));
   897           lua_setfield(L, 5, "code");
   898         } else {
   899           lua_pushliteral(L, MONDELEFANT_ERRCODE_UNKNOWN);
   900           lua_setfield(L, 5, "code");
   901         }
   902         errmsg = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY);
   903         if (errmsg) {
   904           mondelefant_push_first_line(L, errmsg);
   905           lua_setfield(L, 5, "message");
   906           lua_pushstring(L, errmsg);
   907           lua_setfield(L, 5, "pg_message_primary");
   908         } else {
   909           lua_pushliteral(L,
   910             "Error while fetching result, but no error message given."
   911           );
   912           lua_setfield(L, 5, "message");
   913         }
   914         lua_pushstring(L, PQresultErrorField(res, PG_DIAG_MESSAGE_DETAIL));
   915         lua_setfield(L, 5, "pg_message_detail");
   916         lua_pushstring(L, PQresultErrorField(res, PG_DIAG_MESSAGE_HINT));
   917         lua_setfield(L, 5, "pg_message_hint");
   918         // NOTE: "position" and "pg_internal_position" are recalculated to
   919         // byte offsets, as Lua 5.1 is not Unicode aware.
   920         {
   921           char *tmp;
   922           tmp = PQresultErrorField(res, PG_DIAG_STATEMENT_POSITION);
   923           if (tmp) {
   924             int pos;
   925             pos = atoi(tmp) - 1;
   926             if (conn->server_encoding == MONDELEFANT_SERVER_ENCODING_UTF8) {
   927               pos = utf8_position_to_byte(command, pos);
   928             }
   929             lua_pushinteger(L, pos + 1);
   930             lua_setfield(L, 5, "position");
   931           }
   932         }
   933         {
   934           const char *internal_query;
   935           internal_query = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY);
   936           lua_pushstring(L, internal_query);
   937           lua_setfield(L, 5, "pg_internal_query");
   938           char *tmp;
   939           tmp = PQresultErrorField(res, PG_DIAG_INTERNAL_POSITION);
   940           if (tmp) {
   941             int pos;
   942             pos = atoi(tmp) - 1;
   943             if (conn->server_encoding == MONDELEFANT_SERVER_ENCODING_UTF8) {
   944               pos = utf8_position_to_byte(internal_query, pos);
   945             }
   946             lua_pushinteger(L, pos + 1);
   947             lua_setfield(L, 5, "pg_internal_position");
   948           }
   949         }
   950         lua_pushstring(L, PQresultErrorField(res, PG_DIAG_CONTEXT));
   951         lua_setfield(L, 5, "pg_context");
   952         lua_pushstring(L, PQresultErrorField(res, PG_DIAG_SOURCE_FILE));
   953         lua_setfield(L, 5, "pg_source_file");
   954         lua_pushstring(L, PQresultErrorField(res, PG_DIAG_SOURCE_LINE));
   955         lua_setfield(L, 5, "pg_source_line");
   956         lua_pushstring(L, PQresultErrorField(res, PG_DIAG_SOURCE_FUNCTION));
   957         lua_setfield(L, 5, "pg_source_function");
   958       } else if (rows < 1 && mode == MONDELEFANT_QUERY_MODE_OBJECT) {
   959         lua_pushliteral(L, MONDELEFANT_ERRCODE_QUERY1_NO_ROWS);
   960         lua_setfield(L, 5, "code");
   961         lua_pushliteral(L, "Expected one row, but got empty set.");
   962         lua_setfield(L, 5, "message");
   963       } else if (rows > 1 && mode != MONDELEFANT_QUERY_MODE_LIST) {
   964         lua_pushliteral(L, MONDELEFANT_ERRCODE_QUERY1_MULTIPLE_ROWS);
   965         lua_setfield(L, 5, "code");
   966         lua_pushliteral(L, "Got more than one result row.");
   967         lua_setfield(L, 5, "message");
   968       } else {
   969         // should not happen
   970         abort();
   971       }
   972       if (res) {
   973         PQclear(res);
   974         while ((res = PQgetResult(conn->pgconn))) PQclear(res);
   975       }
   976       if (lua_toboolean(L, 3)) {
   977         lua_pushvalue(L, 3);
   978         lua_pushvalue(L, 5);
   979         lua_call(L, 1, 0);
   980       }
   981       return 1;
   982     }
   983     // call "create_list" or "create_object" method of database handle,
   984     // result will be at stack position 5:
   985     if (modes[command_idx] == MONDELEFANT_QUERY_MODE_LIST) {
   986       lua_pushcfunction(L, mondelefant_conn_create_list);  // 5
   987       lua_pushvalue(L, 1);  // 6
   988       lua_call(L, 1, 1);  // 5
   989     } else {
   990       lua_pushcfunction(L, mondelefant_conn_create_object);  // 5
   991       lua_pushvalue(L, 1);  // 6
   992       lua_call(L, 1, 1);  // 5
   993     }
   994     // set "_column_info":
   995     lua_newtable(L);  // 6
   996     for (col = 0; col < cols; col++) {
   997       lua_newtable(L);  // 7
   998       lua_pushstring(L, PQfname(res, col));
   999       lua_setfield(L, 7, "field_name");
  1000       {
  1001         Oid tmp;
  1002         tmp = PQftable(res, col);
  1003         if (tmp == InvalidOid) lua_pushnil(L);
  1004         else lua_pushinteger(L, tmp);
  1005         lua_setfield(L, 7, "table_oid");
  1006       }
  1007       {
  1008         int tmp;
  1009         tmp = PQftablecol(res, col);
  1010         if (tmp == 0) lua_pushnil(L);
  1011         else lua_pushinteger(L, tmp);
  1012         lua_setfield(L, 7, "table_column_number");
  1013       }
  1014       {
  1015         Oid tmp;
  1016         tmp = PQftype(res, col);
  1017         binary[col] = (tmp == MONDELEFANT_POSTGRESQL_BINARY_OID);
  1018         lua_pushinteger(L, tmp);
  1019         lua_setfield(L, 7, "type_oid");
  1020         lua_pushstring(L, mondelefant_oid_to_typestr(tmp));
  1021         lua_setfield(L, 7, "type");
  1022       }
  1023       {
  1024         int tmp;
  1025         tmp = PQfmod(res, col);
  1026         if (tmp == -1) lua_pushnil(L);
  1027         else lua_pushinteger(L, tmp);
  1028         lua_setfield(L, 7, "type_modifier");
  1029       }
  1030       lua_rawseti(L, 6, col+1);
  1031     }
  1032     lua_setfield(L, 5, "_column_info");
  1033     // set "_rows_affected":
  1034     {
  1035       char *tmp;
  1036       tmp = PQcmdTuples(res);
  1037       if (tmp[0]) {
  1038         lua_pushinteger(L, atoi(tmp));
  1039         lua_setfield(L, 5, "_rows_affected");
  1040       }
  1041     }
  1042     // set "_oid":
  1043     {
  1044       Oid tmp;
  1045       tmp = PQoidValue(res);
  1046       if (tmp != InvalidOid) {
  1047         lua_pushinteger(L, tmp);
  1048         lua_setfield(L, 5, "_oid");
  1049       }
  1050     }
  1051     // copy data as strings or nil, while performing binary unescaping
  1052     // automatically:
  1053     if (modes[command_idx] == MONDELEFANT_QUERY_MODE_LIST) {
  1054       for (row = 0; row < rows; row++) {
  1055         lua_pushcfunction(L, mondelefant_conn_create_object);  // 6
  1056         lua_pushvalue(L, 1);  // 7
  1057         lua_call(L, 1, 1);  // 6
  1058         for (col = 0; col < cols; col++) {
  1059           if (PQgetisnull(res, row, col)) {
  1060             lua_pushnil(L);
  1061           } else if (binary[col]) {
  1062             size_t binlen;
  1063             char *binval;
  1064             binval = (char *)PQunescapeBytea(
  1065               (unsigned char *)PQgetvalue(res, row, col), &binlen
  1066             );
  1067             if (!binval) {
  1068               return luaL_error(L,
  1069                 "Could not allocate memory for binary unescaping."
  1070               );
  1071             }
  1072             lua_pushlstring(L, binval, binlen);
  1073             PQfreemem(binval);
  1074           } else {
  1075             lua_pushstring(L, PQgetvalue(res, row, col));
  1076           }
  1077           lua_rawseti(L, 6, col+1);
  1078         }
  1079         lua_rawseti(L, 5, row+1);
  1080       }
  1081     } else if (rows == 1) {
  1082       for (col = 0; col < cols; col++) {
  1083         if (PQgetisnull(res, 0, col)) {
  1084           lua_pushnil(L);
  1085         } else if (binary[col]) {
  1086           size_t binlen;
  1087           char *binval;
  1088           binval = (char *)PQunescapeBytea(
  1089             (unsigned char *)PQgetvalue(res, 0, col), &binlen
  1090           );
  1091           if (!binval) {
  1092             return luaL_error(L,
  1093               "Could not allocate memory for binary unescaping."
  1094             );
  1095           }
  1096           lua_pushlstring(L, binval, binlen);
  1097           PQfreemem(binval);
  1098         } else {
  1099           lua_pushstring(L, PQgetvalue(res, 0, col));
  1100         }
  1101         lua_rawseti(L, 5, col+1);
  1102       }
  1103     } else {
  1104       // no row in optrow mode
  1105       lua_pop(L, 1);
  1106       lua_pushnil(L);
  1107     }
  1108     // save result in result list:
  1109     lua_rawseti(L, 4, command_idx+1);
  1110     // extra assertion:
  1111     if (lua_gettop(L) != 4) abort();  // should not happen
  1112     // free memory acquired by libpq:
  1113     PQclear(res);
  1114   }
  1115   // trace callback at stack position 3
  1116   // result at stack position 4 (top of stack)
  1117   // if a trace callback is existent, then call:
  1118   if (lua_toboolean(L, 3)) {
  1119     lua_pushvalue(L, 3);
  1120     lua_call(L, 0, 0);
  1121   }
  1122   // put result at stack position 3:
  1123   lua_replace(L, 3);
  1124   // get output converter to stack position 4:
  1125   lua_getfield(L, 1, "output_converter");
  1126   // apply output converters and fill "_data" table according to column names:
  1127   for (command_idx = 0; command_idx < command_count; command_idx++) {
  1128     int mode;
  1129     mode = modes[command_idx];
  1130     lua_rawgeti(L, 3, command_idx+1);  // raw result at stack position 5
  1131     if (lua_toboolean(L, 5)) {
  1132       lua_getfield(L, 5, "_column_info");  // column_info list at position 6
  1133       cols = lua_objlen(L, 6);
  1134       if (mode == MONDELEFANT_QUERY_MODE_LIST) {
  1135         rows = lua_objlen(L, 5);
  1136         for (row = 0; row < rows; row++) {
  1137           lua_rawgeti(L, 5, row+1);  // row at stack position 7
  1138           lua_getfield(L, 7, "_data");  // _data table at stack position 8
  1139           for (col = 0; col < cols; col++) {
  1140             lua_rawgeti(L, 6, col+1);  // this column info at position 9
  1141             lua_getfield(L, 9, "field_name");  // 10
  1142             if (lua_toboolean(L, 4)) {
  1143               lua_pushvalue(L, 4);  // output-converter
  1144               lua_pushvalue(L, 1);  // connection
  1145               lua_rawgeti(L, 7, col+1);  // raw-value
  1146               lua_pushvalue(L, 9);  // this column info
  1147               lua_call(L, 3, 1);  // converted value at position 11
  1148             } else {
  1149               lua_rawgeti(L, 7, col+1);  // raw-value at position 11
  1150             }
  1151             lua_pushvalue(L, 11);  // 12
  1152             lua_rawseti(L, 7, col+1);
  1153             lua_rawset(L, 8);
  1154             lua_settop(L, 8);
  1155           }
  1156           lua_settop(L, 6);
  1157         }
  1158       } else {
  1159         lua_getfield(L, 5, "_data");  // _data table at stack position 7
  1160         for (col = 0; col < cols; col++) {
  1161           lua_rawgeti(L, 6, col+1);  // this column info at position 8
  1162           lua_getfield(L, 8, "field_name");  // 9
  1163           if (lua_toboolean(L, 4)) {
  1164             lua_pushvalue(L, 4);  // output-converter
  1165             lua_pushvalue(L, 1);  // connection
  1166             lua_rawgeti(L, 5, col+1);  // raw-value
  1167             lua_pushvalue(L, 8);  // this column info
  1168             lua_call(L, 3, 1);  // converted value at position 10
  1169           } else {
  1170             lua_rawgeti(L, 5, col+1);  // raw-value at position 10
  1171           }
  1172           lua_pushvalue(L, 10);  // 11
  1173           lua_rawseti(L, 5, col+1);
  1174           lua_rawset(L, 7);
  1175           lua_settop(L, 7);
  1176         }
  1177       }
  1178     }
  1179     lua_settop(L, 4);
  1180   }
  1181   // return nil as first result value, followed by result lists/objects:
  1182   lua_settop(L, 3);
  1183   lua_pushnil(L);
  1184   for (command_idx = 0; command_idx < command_count; command_idx++) {
  1185     lua_rawgeti(L, 3, command_idx+1);
  1186   }
  1187   return command_count+1;
  1188 }
  1190 // method "escalate" of error objects:
  1191 static int mondelefant_errorobject_escalate(lua_State *L) {
  1192   // check, if we may throw an error object instead of an error string:
  1193   lua_settop(L, 1);
  1194   lua_getfield(L, 1, "connection");  // 2
  1195   lua_getfield(L, 2, "error_objects");  // 3
  1196   if (lua_toboolean(L, 3)) {
  1197     // throw error object:
  1198     lua_settop(L, 1);
  1199     return lua_error(L);
  1200   } else {
  1201     // throw error string:
  1202     lua_getfield(L, 1, "message");  // 4
  1203     if (lua_isnil(L, 4)) {
  1204       return luaL_error(L, "No error message given for escalation.");
  1205     }
  1206     return lua_error(L);
  1207   }
  1208 }
  1210 // method "is_kind_of" of error objects:
  1211 static int mondelefant_errorobject_is_kind_of(lua_State *L) {
  1212   lua_settop(L, 2);
  1213   lua_getfield(L, 1, "code");  // 3
  1214   if (lua_isstring(L, 3)) {
  1215     lua_pushboolean(L,
  1216       mondelefant_check_error_class(
  1217         lua_tostring(L, 3), luaL_checkstring(L, 2)
  1218       )
  1219     );
  1220   } else {
  1221     // only happens for errors where code is not set
  1222     lua_pushboolean(L, 0);
  1223   }
  1224   return 1;
  1225 }
  1227 // method "query" of database handles:
  1228 static int mondelefant_conn_query(lua_State *L) {
  1229   int argc;
  1230   // count number of arguments:
  1231   argc = lua_gettop(L);
  1232   // insert "try_query" function/method at stack position 1:
  1233   lua_pushcfunction(L, mondelefant_conn_try_query);
  1234   lua_insert(L, 1);
  1235   // call "try_query" method:
  1236   lua_call(L, argc, LUA_MULTRET);  // results (with error) starting at index 1
  1237   // check, if error occurred:
  1238   if (lua_toboolean(L, 1)) {
  1239     // invoke escalate method of error object:
  1240     lua_pushcfunction(L, mondelefant_errorobject_escalate);
  1241     lua_pushvalue(L, 1);
  1242     lua_call(L, 1, 0);  // will raise an error
  1243     return 0;  // should not be executed
  1244   } else {
  1245     // return everything but nil error object:
  1246     return lua_gettop(L) - 1;
  1247   }
  1248 }
  1250 // library function "set_class":
  1251 static int mondelefant_set_class(lua_State *L) {
  1252   // ensure that first argument is a database result list/object:
  1253   lua_settop(L, 2);
  1254   lua_getmetatable(L, 1);  // 3
  1255   lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_RESULT_MT_REGKEY);  // 4
  1256   luaL_argcheck(L, lua_equal(L, 3, 4), 1, "not a database result");
  1257   // ensure that second argument is a database class (model):
  1258   lua_settop(L, 2);
  1259   lua_getmetatable(L, 2);  // 3
  1260   lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_CLASS_MT_REGKEY);  // 4
  1261   luaL_argcheck(L, lua_equal(L, 3, 4), 2, "not a database class");
  1262   // set attribute "_class" of result list/object to given class:
  1263   lua_settop(L, 2);
  1264   lua_pushvalue(L, 2);  // 3
  1265   lua_setfield(L, 1, "_class");
  1266   // test, if database result is a list (and not a single object):
  1267   lua_getfield(L, 1, "_type");  // 3
  1268   lua_pushliteral(L, "list");  // 4
  1269   if (lua_rawequal(L, 3, 4)) {
  1270     int i;
  1271     // set attribute "_class" of all elements to given class:
  1272     for (i=0; i < lua_objlen(L, 1); i++) {
  1273       lua_settop(L, 2);
  1274       lua_rawgeti(L, 1, i+1);  // 3
  1275       lua_pushvalue(L, 2);  // 4
  1276       lua_setfield(L, 3, "_class");
  1277     }
  1278   }
  1279   // return first argument:
  1280   lua_settop(L, 1);
  1281   return 1;
  1282 }
  1284 // library function "new_class":
  1285 static int mondelefant_new_class(lua_State *L) {
  1286   // use first argument as template or create new table:
  1287   lua_settop(L, 1);
  1288   if (!lua_toboolean(L, 1)) {
  1289     lua_settop(L, 0);
  1290     lua_newtable(L);  // 1
  1291   }
  1292   // set meta-table for database classes (models):
  1293   lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_CLASS_MT_REGKEY);  // 2
  1294   lua_setmetatable(L, 1);
  1295   // check, if "prototype" attribute is not set:
  1296   lua_pushliteral(L, "prototype");  // 2
  1297   lua_rawget(L, 1);  // 2
  1298   if (!lua_toboolean(L, 2)) {
  1299     // set "prototype" attribute to default prototype:
  1300     lua_pushliteral(L, "prototype");  // 3
  1301     lua_getfield(L,
  1302       LUA_REGISTRYINDEX,
  1303       MONDELEFANT_CLASS_PROTO_REGKEY
  1304     );  // 4
  1305     lua_rawset(L, 1);
  1306   }
  1307   // set "object" attribute to empty table, unless it is already set:
  1308   lua_settop(L, 1);
  1309   lua_pushliteral(L, "object");  // 2
  1310   lua_rawget(L, 1);  // 2
  1311   if (!lua_toboolean(L, 2)) {
  1312     lua_pushliteral(L, "object");  // 3
  1313     lua_newtable(L);  // 4
  1314     lua_rawset(L, 1);
  1315   }
  1316   // set "object_get" attribute to empty table, unless it is already set:
  1317   lua_settop(L, 1);
  1318   lua_pushliteral(L, "object_get");  // 2
  1319   lua_rawget(L, 1);  // 2
  1320   if (!lua_toboolean(L, 2)) {
  1321     lua_pushliteral(L, "object_get");  // 3
  1322     lua_newtable(L);  // 4
  1323     lua_rawset(L, 1);
  1324   }
  1325   // set "object_set" attribute to empty table, unless it is already set:
  1326   lua_settop(L, 1);
  1327   lua_pushliteral(L, "object_set");  // 2
  1328   lua_rawget(L, 1);  // 2
  1329   if (!lua_toboolean(L, 2)) {
  1330     lua_pushliteral(L, "object_set");  // 3
  1331     lua_newtable(L);  // 4
  1332     lua_rawset(L, 1);
  1333   }
  1334   // set "list" attribute to empty table, unless it is already set:
  1335   lua_settop(L, 1);
  1336   lua_pushliteral(L, "list");  // 2
  1337   lua_rawget(L, 1);  // 2
  1338   if (!lua_toboolean(L, 2)) {
  1339     lua_pushliteral(L, "list");  // 3
  1340     lua_newtable(L);  // 4
  1341     lua_rawset(L, 1);
  1342   }
  1343   // set "references" attribute to empty table, unless it is already set:
  1344   lua_settop(L, 1);
  1345   lua_pushliteral(L, "references");  // 2
  1346   lua_rawget(L, 1);  // 2
  1347   if (!lua_toboolean(L, 2)) {
  1348     lua_pushliteral(L, "references");  // 3
  1349     lua_newtable(L);  // 4
  1350     lua_rawset(L, 1);
  1351   }
  1352   // set "foreign_keys" attribute to empty table, unless it is already set:
  1353   lua_settop(L, 1);
  1354   lua_pushliteral(L, "foreign_keys");  // 2
  1355   lua_rawget(L, 1);  // 2
  1356   if (!lua_toboolean(L, 2)) {
  1357     lua_pushliteral(L, "foreign_keys");  // 3
  1358     lua_newtable(L);  // 4
  1359     lua_rawset(L, 1);
  1360   }
  1361   // return table:
  1362   lua_settop(L, 1);
  1363   return 1;
  1364 }
  1366 // method "get_reference" of classes (models):
  1367 static int mondelefant_class_get_reference(lua_State *L) {
  1368   lua_settop(L, 2);
  1369   while (lua_toboolean(L, 1)) {
  1370     // get "references" table:
  1371     lua_getfield(L, 1, "references");  // 3
  1372     // perform lookup:
  1373     lua_pushvalue(L, 2);  // 4
  1374     lua_gettable(L, 3);  // 4
  1375     // return result, if lookup was successful:
  1376     if (!lua_isnil(L, 4)) return 1;
  1377     // replace current table by its prototype:
  1378     lua_settop(L, 2);
  1379     lua_pushliteral(L, "prototype");  // 3
  1380     lua_rawget(L, 1);  // 3
  1381     lua_replace(L, 1);
  1382   }
  1383   // return nothing:
  1384   return 0;
  1385 }
  1387 // method "iterate_over_references" of classes (models):
  1388 static int mondelefant_class_iterate_over_references(lua_State *L) {
  1389   return luaL_error(L, "Reference iterator not implemented yet.");  // TODO
  1390 }
  1392 // method "get_foreign_key_reference_name" of classes (models):
  1393 static int mondelefant_class_get_foreign_key_reference_name(lua_State *L) {
  1394   lua_settop(L, 2);
  1395   while (lua_toboolean(L, 1)) {
  1396     // get "foreign_keys" table:
  1397     lua_getfield(L, 1, "foreign_keys");  // 3
  1398     // perform lookup:
  1399     lua_pushvalue(L, 2);  // 4
  1400     lua_gettable(L, 3);  // 4
  1401     // return result, if lookup was successful:
  1402     if (!lua_isnil(L, 4)) return 1;
  1403     // replace current table by its prototype:
  1404     lua_settop(L, 2);
  1405     lua_pushliteral(L, "prototype");  // 3
  1406     lua_rawget(L, 1);  // 3
  1407     lua_replace(L, 1);
  1408   }
  1409   // return nothing:
  1410   return 0;
  1411 }
  1413 // meta-method "__index" of database result lists and objects:
  1414 static int mondelefant_result_index(lua_State *L) {
  1415   const char *result_type;
  1416   // only lookup, when key is a string not beginning with an underscore:
  1417   if (lua_type(L, 2) != LUA_TSTRING || lua_tostring(L, 2)[0] == '_') {
  1418     return 0;
  1419   }
  1420   // get value of "_class" attribute, or default class, when unset:
  1421   lua_settop(L, 2);
  1422   lua_getfield(L, 1, "_class");  // 3
  1423   if (!lua_toboolean(L, 3)) {
  1424     lua_settop(L, 2);
  1425     lua_getfield(L,
  1426       LUA_REGISTRYINDEX,
  1427       MONDELEFANT_CLASS_PROTO_REGKEY
  1428     );  // 3
  1429   }
  1430   // get value of "_type" attribute:
  1431   lua_getfield(L, 1, "_type");  // 4
  1432   result_type = lua_tostring(L, 4);
  1433   // different lookup for lists and objects:
  1434   if (result_type && !strcmp(result_type, "object")) {  // object
  1435     lua_settop(L, 3);
  1436     // try inherited attributes, methods or getter functions:
  1437     while (lua_toboolean(L, 3)) {
  1438       lua_getfield(L, 3, "object");  // 4
  1439       lua_pushvalue(L, 2);  // 5
  1440       lua_gettable(L, 4);  // 5
  1441       if (!lua_isnil(L, 5)) return 1;
  1442       lua_settop(L, 3);
  1443       lua_getfield(L, 3, "object_get");  // 4
  1444       lua_pushvalue(L, 2);  // 5
  1445       lua_gettable(L, 4);  // 5
  1446       if (lua_toboolean(L, 5)) {
  1447         lua_pushvalue(L, 1);  // 6
  1448         lua_call(L, 1, 1);  // 5
  1449         return 1;
  1450       }
  1451       lua_settop(L, 3);
  1452       lua_pushliteral(L, "prototype");  // 4
  1453       lua_rawget(L, 3);  // 4
  1454       lua_replace(L, 3);
  1455     }
  1456     lua_settop(L, 2);
  1457     // try primary keys of referenced objects:
  1458     lua_pushcfunction(L,
  1459       mondelefant_class_get_foreign_key_reference_name
  1460     );  // 3
  1461     lua_getfield(L, 1, "_class");  // 4
  1462     lua_pushvalue(L, 2);  // 5
  1463     lua_call(L, 2, 1);  // 3
  1464     if (!lua_isnil(L, 3)) {
  1465       // reference name at stack position 3
  1466       lua_pushcfunction(L, mondelefant_class_get_reference);  // 4
  1467       lua_getfield(L, 1, "_class");  // 5
  1468       lua_pushvalue(L, 3);  // 6
  1469       lua_call(L, 2, 1);  // reference info at stack position 4
  1470       lua_getfield(L, 1, "_ref");  // 5
  1471       lua_getfield(L, 4, "ref");  // 6
  1472       lua_gettable(L, 5);  // 6
  1473       if (!lua_isnil(L, 6)) {
  1474         if (lua_toboolean(L, 6)) {
  1475           lua_getfield(L, 4, "that_key");  // 7
  1476           if (lua_isnil(L, 7)) {
  1477             return luaL_error(L, "Missing 'that_key' entry in model reference.");
  1478           }
  1479           lua_gettable(L, 6);  // 7
  1480         } else {
  1481           lua_pushnil(L);
  1482         }
  1483         return 1;
  1484       }
  1485     }
  1486     lua_settop(L, 2);
  1487     // try normal data field info:
  1488     lua_getfield(L, 1, "_data");  // 3
  1489     lua_pushvalue(L, 2);  // 4
  1490     lua_gettable(L, 3);  // 4
  1491     if (!lua_isnil(L, 4)) return 1;
  1492     lua_settop(L, 2);
  1493     // try cached referenced object (or cached NULL reference):
  1494     lua_getfield(L, 1, "_ref");  // 3
  1495     lua_pushvalue(L, 2);  // 4
  1496     lua_gettable(L, 3);  // 4
  1497     if (lua_isboolean(L, 4) && !lua_toboolean(L, 4)) {
  1498       lua_pushnil(L);
  1499       return 1;
  1500     } else if (!lua_isnil(L, 4)) {
  1501       return 1;
  1502     }
  1503     lua_settop(L, 2);
  1504     // try to load a referenced object:
  1505     lua_pushcfunction(L, mondelefant_class_get_reference);  // 3
  1506     lua_getfield(L, 1, "_class");  // 4
  1507     lua_pushvalue(L, 2);  // 5
  1508     lua_call(L, 2, 1);  // 3
  1509     if (!lua_isnil(L, 3)) {
  1510       lua_settop(L, 2);
  1511       lua_getfield(L, 1, "load");  // 3
  1512       lua_pushvalue(L, 1);  // 4 (self)
  1513       lua_pushvalue(L, 2);  // 5
  1514       lua_call(L, 2, 0);
  1515       lua_settop(L, 2);
  1516       lua_getfield(L, 1, "_ref");  // 3
  1517       lua_pushvalue(L, 2);  // 4
  1518       lua_gettable(L, 3);  // 4
  1519       if (lua_isboolean(L, 4) && !lua_toboolean(L, 4)) lua_pushnil(L);  // TODO: use special object instead of false
  1520       return 1;
  1521     }
  1522     return 0;
  1523   } else if (result_type && !strcmp(result_type, "list")) {  // list
  1524     lua_settop(L, 3);
  1525     // try inherited list attributes or methods:
  1526     while (lua_toboolean(L, 3)) {
  1527       lua_getfield(L, 3, "list");  // 4
  1528       lua_pushvalue(L, 2);  // 5
  1529       lua_gettable(L, 4);  // 5
  1530       if (!lua_isnil(L, 5)) return 1;
  1531       lua_settop(L, 3);
  1532       lua_pushliteral(L, "prototype");  // 4
  1533       lua_rawget(L, 3);  // 4
  1534       lua_replace(L, 3);
  1535     }
  1536   }
  1537   // return nothing:
  1538   return 0;
  1539 }
  1541 // meta-method "__newindex" of database result lists and objects:
  1542 static int mondelefant_result_newindex(lua_State *L) {
  1543   const char *result_type;
  1544   // perform rawset, unless key is a string not starting with underscore:
  1545   lua_settop(L, 3);
  1546   if (lua_type(L, 2) != LUA_TSTRING || lua_tostring(L, 2)[0] == '_') {
  1547     lua_rawset(L, 1);
  1548     return 1;
  1549   }
  1550   // get value of "_class" attribute, or default class, when unset:
  1551   lua_getfield(L, 1, "_class");  // 4
  1552   if (!lua_toboolean(L, 4)) {
  1553     lua_settop(L, 3);
  1554     lua_getfield(L,
  1555       LUA_REGISTRYINDEX,
  1556       MONDELEFANT_CLASS_PROTO_REGKEY
  1557     );  // 4
  1558   }
  1559   // get value of "_type" attribute:
  1560   lua_getfield(L, 1, "_type");  // 5
  1561   result_type = lua_tostring(L, 5);
  1562   // distinguish between lists and objects:
  1563   if (result_type && !strcmp(result_type, "object")) {  // objects
  1564     lua_settop(L, 4);
  1565     // try object setter functions:
  1566     while (lua_toboolean(L, 4)) {
  1567       lua_getfield(L, 4, "object_set");  // 5
  1568       lua_pushvalue(L, 2);  // 6
  1569       lua_gettable(L, 5);  // 6
  1570       if (lua_toboolean(L, 6)) {
  1571         lua_pushvalue(L, 1);  // 7
  1572         lua_pushvalue(L, 3);  // 8
  1573         lua_call(L, 2, 0);
  1574         return 0;
  1575       }
  1576       lua_settop(L, 4);
  1577       lua_pushliteral(L, "prototype");  // 5
  1578       lua_rawget(L, 4);  // 5
  1579       lua_replace(L, 4);
  1580     }
  1581     lua_settop(L, 3);
  1582     // check, if a object reference is changed:
  1583     lua_pushcfunction(L, mondelefant_class_get_reference);  // 4
  1584     lua_getfield(L, 1, "_class");  // 5
  1585     lua_pushvalue(L, 2);  // 6
  1586     lua_call(L, 2, 1);  // 4
  1587     if (!lua_isnil(L, 4)) {
  1588       // store object in _ref table (use false for nil):  // TODO: use special object instead of false
  1589       lua_getfield(L, 1, "_ref");  // 5
  1590       lua_pushvalue(L, 2);  // 6
  1591       if (lua_isnil(L, 3)) lua_pushboolean(L, 0);  // 7
  1592       else lua_pushvalue(L, 3);  // 7
  1593       lua_settable(L, 5);
  1594       lua_settop(L, 4);
  1595       // delete referencing key from _data table:
  1596       lua_getfield(L, 4, "this_key");  // 5
  1597       if (lua_isnil(L, 5)) {
  1598         return luaL_error(L, "Missing 'this_key' entry in model reference.");
  1599       }
  1600       lua_getfield(L, 1, "_data");  // 6
  1601       lua_pushvalue(L, 5);  // 7
  1602       lua_pushnil(L);  // 8
  1603       lua_settable(L, 6);
  1604       lua_settop(L, 5);
  1605       lua_getfield(L, 1, "_dirty");  // 6
  1606       lua_pushvalue(L, 5);  // 7
  1607       lua_pushboolean(L, 1);  // 8
  1608       lua_settable(L, 6);
  1609       return 0;
  1610     }
  1611     lua_settop(L, 3);
  1612     // store value in data field info:
  1613     lua_getfield(L, 1, "_data");  // 4
  1614     lua_pushvalue(L, 2);  // 5
  1615     lua_pushvalue(L, 3);  // 6
  1616     lua_settable(L, 4);
  1617     lua_settop(L, 3);
  1618     // mark field as dirty (needs to be UPDATEd on save):
  1619     lua_getfield(L, 1, "_dirty");  // 4
  1620     lua_pushvalue(L, 2);  // 5
  1621     lua_pushboolean(L, 1);  // 6
  1622     lua_settable(L, 4);
  1623     lua_settop(L, 3);
  1624     // reset reference cache, if neccessary:
  1625     lua_pushcfunction(L,
  1626       mondelefant_class_get_foreign_key_reference_name
  1627     );  // 4
  1628     lua_getfield(L, 1, "_class");  // 5
  1629     lua_pushvalue(L, 2);  // 6
  1630     lua_call(L, 2, 1);  // 4
  1631     if (!lua_isnil(L, 4)) {
  1632       lua_getfield(L, 1, "_ref");  // 5
  1633       lua_pushvalue(L, 4);  // 6
  1634       lua_pushnil(L);  // 7
  1635       lua_settable(L, 5);
  1636     }
  1637     return 0;
  1638   } else {  // non-objects (i.e. lists)
  1639     // perform rawset:
  1640     lua_settop(L, 3);
  1641     lua_rawset(L, 1);
  1642     return 0;
  1643   }
  1644 }
  1646 // meta-method "__index" of classes (models):
  1647 static int mondelefant_class_index(lua_State *L) {
  1648   // perform lookup in prototype:
  1649   lua_settop(L, 2);
  1650   lua_pushliteral(L, "prototype");  // 3
  1651   lua_rawget(L, 1);  // 3
  1652   lua_pushvalue(L, 2);  // 4
  1653   lua_gettable(L, 3);  // 4
  1654   return 1;
  1655 }
  1657 // registration information for functions of library:
  1658 static const struct luaL_Reg mondelefant_module_functions[] = {
  1659   {"connect", mondelefant_connect},
  1660   {"set_class", mondelefant_set_class},
  1661   {"new_class", mondelefant_new_class},
  1662   {NULL, NULL}
  1663 };
  1665 // registration information for meta-methods of database connections:
  1666 static const struct luaL_Reg mondelefant_conn_mt_functions[] = {
  1667   {"__gc", mondelefant_conn_free},
  1668   {"__index", mondelefant_conn_index},
  1669   {"__newindex", mondelefant_conn_newindex},
  1670   {NULL, NULL}
  1671 };
  1673 // registration information for methods of database connections:
  1674 static const struct luaL_Reg mondelefant_conn_methods[] = {
  1675   {"close", mondelefant_conn_close},
  1676   {"is_ok", mondelefant_conn_is_ok},
  1677   {"get_transaction_status", mondelefant_conn_get_transaction_status},
  1678   {"create_list", mondelefant_conn_create_list},
  1679   {"create_object", mondelefant_conn_create_object},
  1680   {"quote_string", mondelefant_conn_quote_string},
  1681   {"quote_binary", mondelefant_conn_quote_binary},
  1682   {"assemble_command", mondelefant_conn_assemble_command},
  1683   {"try_query", mondelefant_conn_try_query},
  1684   {"query", mondelefant_conn_query},
  1685   {NULL, NULL}
  1686 };
  1688 // registration information for meta-methods of error objects:
  1689 static const struct luaL_Reg mondelefant_errorobject_mt_functions[] = {
  1690   {NULL, NULL}
  1691 };
  1693 // registration information for methods of error objects:
  1694 static const struct luaL_Reg mondelefant_errorobject_methods[] = {
  1695   {"escalate", mondelefant_errorobject_escalate},
  1696   {"is_kind_of", mondelefant_errorobject_is_kind_of},
  1697   {NULL, NULL}
  1698 };
  1700 // registration information for meta-methods of database result lists/objects:
  1701 static const struct luaL_Reg mondelefant_result_mt_functions[] = {
  1702   {"__index", mondelefant_result_index},
  1703   {"__newindex", mondelefant_result_newindex},
  1704   {NULL, NULL}
  1705 };
  1707 // registration information for methods of database result lists/objects:
  1708 static const struct luaL_Reg mondelefant_class_mt_functions[] = {
  1709   {"__index", mondelefant_class_index},
  1710   {NULL, NULL}
  1711 };
  1713 // registration information for methods of classes (models):
  1714 static const struct luaL_Reg mondelefant_class_methods[] = {
  1715   {"get_reference", mondelefant_class_get_reference},
  1716   {"iterate_over_references", mondelefant_class_iterate_over_references},
  1717   {"get_foreign_key_reference_name",
  1718     mondelefant_class_get_foreign_key_reference_name},
  1719   {NULL, NULL}
  1720 };
  1722 // registration information for methods of database result objects (not lists!):
  1723 static const struct luaL_Reg mondelefant_object_methods[] = {
  1724   {NULL, NULL}
  1725 };
  1727 // registration information for methods of database result lists (not single objects!):
  1728 static const struct luaL_Reg mondelefant_list_methods[] = {
  1729   {NULL, NULL}
  1730 };
  1732 // luaopen function to initialize/register library:
  1733 int luaopen_mondelefant_native(lua_State *L) {
  1734   lua_settop(L, 0);
  1735   lua_newtable(L);  // module at stack position 1
  1736   luaL_register(L, NULL, mondelefant_module_functions);
  1738   lua_pushvalue(L, 1);  // 2
  1739   lua_setfield(L, LUA_REGISTRYINDEX, MONDELEFANT_MODULE_REGKEY);
  1741   lua_newtable(L);  // 2
  1742   // NOTE: only PostgreSQL is supported yet:
  1743   luaL_register(L, NULL, mondelefant_conn_methods);
  1744   lua_setfield(L, 1, "postgresql_connection_prototype");
  1745   lua_newtable(L);  // 2
  1746   lua_setfield(L, 1, "connection_prototype");
  1748   luaL_newmetatable(L, MONDELEFANT_CONN_MT_REGKEY);  // 2
  1749   luaL_register(L, NULL, mondelefant_conn_mt_functions);
  1750   lua_settop(L, 1);
  1751   luaL_newmetatable(L, MONDELEFANT_RESULT_MT_REGKEY);  // 2
  1752   luaL_register(L, NULL, mondelefant_result_mt_functions);
  1753   lua_setfield(L, 1, "result_metatable");
  1754   luaL_newmetatable(L, MONDELEFANT_CLASS_MT_REGKEY);  // 2
  1755   luaL_register(L, NULL, mondelefant_class_mt_functions);
  1756   lua_setfield(L, 1, "class_metatable");
  1758   lua_newtable(L);  // 2
  1759   lua_newtable(L);  // 3
  1760   luaL_register(L, NULL, mondelefant_object_methods);
  1761   lua_setfield(L, 2, "object");
  1762   lua_newtable(L);  // 3
  1763   lua_setfield(L, 2, "object_get");
  1764   lua_newtable(L);  // 3
  1765   lua_setfield(L, 2, "object_set");
  1766   lua_newtable(L);  // 3
  1767   luaL_register(L, NULL, mondelefant_list_methods);
  1768   lua_setfield(L, 2, "list");
  1769   lua_newtable(L);  // 3
  1770   lua_setfield(L, 2, "references");
  1771   lua_newtable(L);  // 3
  1772   lua_setfield(L, 2, "foreign_keys");
  1773   lua_pushvalue(L, 2);  // 3
  1774   lua_setfield(L, LUA_REGISTRYINDEX, MONDELEFANT_CLASS_PROTO_REGKEY);
  1775   lua_setfield(L, 1, "class_prototype");
  1777   lua_newtable(L);  // 2
  1778   lua_pushliteral(L, "k");  // 3
  1779   lua_setfield(L, 2, "__mode");
  1780   lua_newtable(L);  // 3
  1781   lua_pushvalue(L, 2);  // 4
  1782   lua_setmetatable(L, 3);
  1783   lua_setfield(L, LUA_REGISTRYINDEX, MONDELEFANT_CONN_DATA_REGKEY);
  1784   lua_settop(L, 1);
  1786   luaL_newmetatable(L, MONDELEFANT_ERROROBJECT_MT_REGKEY);  // 2
  1787   luaL_register(L, NULL, mondelefant_errorobject_mt_functions);
  1788   lua_newtable(L);  // 3
  1789   luaL_register(L, NULL, mondelefant_errorobject_methods);
  1790   lua_setfield(L, 2, "__index");
  1791   lua_setfield(L, 1, "errorobject_metatable");
  1793   return 1;
  1794 }
