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