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