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