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