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