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