webmcp

changeset 397:46ba2168693a

Improved error handling in mondelefant_native.c; Fixed bug in error handling when PQsendQuery returned 0
author jbe
date Thu Dec 10 17:23:51 2015 +0100 (2015-12-10)
parents 762ab1e87702
children ac9a4e1885da
files libraries/mondelefant/mondelefant_native.autodoc.lua libraries/mondelefant/mondelefant_native.c
line diff
     1.1 --- a/libraries/mondelefant/mondelefant_native.autodoc.lua	Wed Dec 09 21:16:24 2015 +0100
     1.2 +++ b/libraries/mondelefant/mondelefant_native.autodoc.lua	Thu Dec 10 17:23:51 2015 +0100
     1.3 @@ -2,7 +2,7 @@
     1.4  --[[--
     1.5  db_handle,                            -- database handle, or nil in case of error
     1.6  errmsg,                               -- error message
     1.7 -errcode =                             -- error code
     1.8 +db_error                              -- error object
     1.9  mondelefant.connect{
    1.10    engine          = "postgresql",     -- no other engine is supported
    1.11    conninfo        = conninfo,         -- string passed directly to PostgreSQL's libpq
    1.12 @@ -77,21 +77,19 @@
    1.13  
    1.14  
    1.15  --[[--
    1.16 -channel,           -- notification channel name
    1.17 -payload,           -- notification payload string
    1.18 -pid =              -- process ID of notifying server process
    1.19 -<db_handle>:wait(
    1.20 -  timeout          -- number of seconds to wait, 0 = do not block, nil = wait infinitely
    1.21 +db_error,              -- error object, or nil in case of success or timeout
    1.22 +channel,               -- notification channel name, or nil in case of timeout or no pending notify
    1.23 +payload,               -- notification payload string
    1.24 +pid =                  -- process ID of notifying server process
    1.25 +<db_handle>:try_wait(
    1.26 +  timeout              -- number of seconds to wait, 0 = do not block, nil = wait infinitely
    1.27  )
    1.28  
    1.29  Waits for any NOTIFY event that is being LISTENed for. One or more LISTEN commands must have been sent previously with <db_handle>:query("LISTEN channel_name").
    1.30  
    1.31 -Returns nil as first return value and an error message as second return value in case of error.
    1.32 -Returns false as first return value and a message as second return value in case of timeout.
    1.33 -
    1.34  --]]--
    1.35  -- implemented in mondelefant_native.c as
    1.36 --- static int mondelefant_conn_wait(lua_State *L)
    1.37 +-- static int mondelefant_conn_try_wait(lua_State *L)
    1.38  --//--
    1.39  
    1.40  
    1.41 @@ -196,7 +194,9 @@
    1.42  --[[--
    1.43  <db_error>:escalate()
    1.44  
    1.45 -Causes a Lua error to be thrown. If the database connection has "error_objects" set to true, then the object is thrown itself, otherwise a string is thrown.
    1.46 +Deprecated alias for error(<db_error>).
    1.47 +
    1.48 +Note: Previous versions converted the error object to a string unless the database connection had "error_objects" set to true. The current implementation simply calls error(...). It is deprecated to use this method, use error(...) instead.
    1.49  
    1.50  --]]--
    1.51  -- implemented in mondelefant_native.c as
    1.52 @@ -222,6 +222,43 @@
    1.53  
    1.54  
    1.55  --[[--
    1.56 +<db_error>.code  -- hierarchical error code (separated by dots) in camel case
    1.57 +
    1.58 +An error code in camel case notation with dots to separate hierarchy levels, e.g. "IntegrityConstraintViolation.UniqueViolation". See also <db_error>:is_kind_of(...).
    1.59 +
    1.60 +--]]--
    1.61 +-- implemented in mondelefant_native.c as
    1.62 +-- static const char *mondelefant_translate_errcode(const char *pgcode)
    1.63 +--//--
    1.64 +
    1.65 +
    1.66 +--[[--
    1.67 +<db_error>.message  -- string which summarizes the error
    1.68 +
    1.69 +A string consisting of a single line (without CR/LF) describing the error. For more detailed information on a particular error, additional fields may be set in the <db_error> object. Refer to the source code of the mondelefant_translate_errcode C function in mondelefant_native.c.
    1.70 +
    1.71 +--]]--
    1.72 +-- implemented in mondelefant_native.c
    1.73 +--//--
    1.74 +
    1.75 +
    1.76 +--[[--
    1.77 +channel,           -- notification channel name, or nil in case of timeout or no pending notify
    1.78 +payload,           -- notification payload string
    1.79 +pid =              -- process ID of notifying server process
    1.80 +<db_handle>:wait(
    1.81 +  timeout          -- number of seconds to wait, 0 = do not block, nil = wait infinitely
    1.82 +)
    1.83 +
    1.84 +Same as "try_wait" but raises an error, if a connection error occurred. Timeouts are reported by returning nil as first argument.
    1.85 +
    1.86 +--]]--
    1.87 +-- implemented in mondelefant_native.c as
    1.88 +-- static int mondelefant_conn_wait(lua_State *L)
    1.89 +--//--
    1.90 +
    1.91 +
    1.92 +--[[--
    1.93  result1,            -- result of first command
    1.94  result2,            -- result of second command
    1.95  ... =
     2.1 --- a/libraries/mondelefant/mondelefant_native.c	Wed Dec 09 21:16:24 2015 +0100
     2.2 +++ b/libraries/mondelefant/mondelefant_native.c	Thu Dec 10 17:23:51 2015 +0100
     2.3 @@ -179,17 +179,16 @@
     2.4  
     2.5  // pushing first line of a string on Lua's stack (without trailing CR/LF):
     2.6  static void mondelefant_push_first_line(lua_State *L, const char *str) {
     2.7 -  char *str2;
     2.8    size_t i = 0;
     2.9    if (!str) abort();  // should not happen
    2.10 -  str2 = strdup(str);
    2.11    while (1) {
    2.12 -    char c = str2[i];
    2.13 -    if (c == '\n' || c == '\r' || c == 0) { str2[i] = 0; break; }
    2.14 +    char c = str[i];
    2.15 +    if (c == '\n' || c == '\r' || c == 0) {
    2.16 +      lua_pushlstring(L, str, i);
    2.17 +      return;
    2.18 +    }
    2.19      i++;
    2.20 -  };
    2.21 -  lua_pushstring(L, str2);
    2.22 -  free(str2);
    2.23 +  }
    2.24  }
    2.25  
    2.26  // "connect" function of library, which establishes a database connection
    2.27 @@ -199,16 +198,18 @@
    2.28    const char *conninfo;  // string for PQconnectdb function
    2.29    PGconn *pgconn;  // PGconn object as returned by PQconnectdb function
    2.30    mondelefant_conn_t *conn;  // C-structure for userdata
    2.31 +  // expect a table as first argument:
    2.32 +  luaL_checktype(L, 1, LUA_TTABLE);
    2.33    // if engine is anything but "postgresql", then raise error:
    2.34    lua_settop(L, 1);
    2.35    lua_getfield(L, 1, "engine");  // 2
    2.36    if (!lua_toboolean(L, 2)) {
    2.37 -    return luaL_error(L, "No database engine selected.");
    2.38 +    return luaL_argerror(L, 1, "no database engine selected");
    2.39    }
    2.40    lua_pushliteral(L, "postgresql");  // 3
    2.41    if (!lua_rawequal(L, 2, 3)) {
    2.42 -    return luaL_error(L,
    2.43 -      "Only database engine 'postgresql' is supported."
    2.44 +    return luaL_argerror(L, 1,
    2.45 +      "only database engine 'postgresql' is supported"
    2.46      );
    2.47    }
    2.48    // copy conninfo string for PQconnectdb function from argument table to
    2.49 @@ -230,7 +231,7 @@
    2.50          // NOTE: numbers will be converted to strings automatically here,
    2.51          // but perhaps this will change in future versions of lua
    2.52          luaL_argcheck(L,
    2.53 -          lua_isstring(L, 2) && lua_isstring(L, 3), 1, "non-string contained"
    2.54 +          lua_isstring(L, 2) && lua_isstring(L, 3), 1, "key is not a string"
    2.55          );
    2.56          lua_pushvalue(L, 2);
    2.57          lua_pushliteral(L, "engine");
    2.58 @@ -265,24 +266,25 @@
    2.59    conninfo = lua_tostring(L, 2);
    2.60    // call PQconnectdb function of libpq:
    2.61    pgconn = PQconnectdb(conninfo);
    2.62 -  // throw errors, if neccessary:
    2.63 +  // throw or return errors, if neccessary:
    2.64    if (!pgconn) {
    2.65      return luaL_error(L,
    2.66        "Error in libpq while creating 'PGconn' structure."
    2.67      );
    2.68    }
    2.69    if (PQstatus(pgconn) != CONNECTION_OK) {
    2.70 -    const char *errmsg;
    2.71 -    lua_pushnil(L);
    2.72 -    errmsg = PQerrorMessage(pgconn);
    2.73 -    if (errmsg) {
    2.74 -      mondelefant_push_first_line(L, errmsg);
    2.75 -    } else {
    2.76 -      lua_pushliteral(L,
    2.77 -        "Error while connecting to database, but no error message given."
    2.78 -      );
    2.79 -    }
    2.80 +    lua_pushnil(L);  // 3
    2.81 +    mondelefant_push_first_line(L, PQerrorMessage(pgconn));  // 4
    2.82 +    lua_newtable(L);  // 5
    2.83 +    lua_getfield(L,
    2.84 +      LUA_REGISTRYINDEX,
    2.85 +      MONDELEFANT_ERROROBJECT_MT_REGKEY
    2.86 +    );
    2.87 +    lua_setmetatable(L, 5);
    2.88      lua_pushliteral(L, MONDELEFANT_ERRCODE_CONNECTION);
    2.89 +    lua_setfield(L, 5, "code");
    2.90 +    lua_pushvalue(L, 4);
    2.91 +    lua_setfield(L, 5, "message");
    2.92      PQfinish(pgconn);
    2.93      return 3;
    2.94    }
    2.95 @@ -390,11 +392,10 @@
    2.96  // method "close" of database handles:
    2.97  static int mondelefant_conn_close(lua_State *L) {
    2.98    mondelefant_conn_t *conn;
    2.99 -  lua_settop(L, 1);
   2.100    conn = mondelefant_get_conn(L, 1);
   2.101    PQfinish(conn->pgconn);
   2.102    conn->pgconn = NULL;
   2.103 -  lua_pushnil(L);  // 2
   2.104 +  lua_pushnil(L);
   2.105    lua_setfield(L, 1, "fd");  // set "fd" attribute to nil
   2.106    return 0;
   2.107  }
   2.108 @@ -402,7 +403,6 @@
   2.109  // method "is_okay" of database handles:
   2.110  static int mondelefant_conn_is_ok(lua_State *L) {
   2.111    mondelefant_conn_t *conn;
   2.112 -  lua_settop(L, 1);
   2.113    conn = mondelefant_get_conn(L, 1);
   2.114    lua_pushboolean(L, PQstatus(conn->pgconn) == CONNECTION_OK);
   2.115    return 1;
   2.116 @@ -411,7 +411,6 @@
   2.117  // method "get_transaction_status" of database handles:
   2.118  static int mondelefant_conn_get_transaction_status(lua_State *L) {
   2.119    mondelefant_conn_t *conn;
   2.120 -  lua_settop(L, 1);
   2.121    conn = mondelefant_get_conn(L, 1);
   2.122    switch (PQtransactionStatus(conn->pgconn)) {
   2.123    case PQTRANS_IDLE:
   2.124 @@ -432,8 +431,8 @@
   2.125    return 1;
   2.126  }
   2.127  
   2.128 -// method "wait" of database handles:
   2.129 -static int mondelefant_conn_wait(lua_State *L) {
   2.130 +// method "try_wait" of database handles:
   2.131 +static int mondelefant_conn_try_wait(lua_State *L) {
   2.132    mondelefant_conn_t *conn;
   2.133    int infinite, nonblock = 0;
   2.134    struct timespec wakeup;
   2.135 @@ -461,6 +460,7 @@
   2.136        luaL_argcheck(L, 0, 2, "not a valid timeout");
   2.137      }
   2.138    }
   2.139 +  lua_settop(L, 1);
   2.140    if (!nonblock) {
   2.141      fd = PQsocket(conn->pgconn);
   2.142      FD_ZERO(&fds);
   2.143 @@ -470,17 +470,26 @@
   2.144      {
   2.145        PGnotify *notify;
   2.146        if (!PQconsumeInput(conn->pgconn)) {
   2.147 -        lua_pushnil(L);
   2.148 -        mondelefant_push_first_line(L, PQerrorMessage(conn->pgconn));
   2.149 -        return 2;
   2.150 +        lua_newtable(L);  // 2
   2.151 +        lua_getfield(L,
   2.152 +          LUA_REGISTRYINDEX,
   2.153 +          MONDELEFANT_ERROROBJECT_MT_REGKEY
   2.154 +        );
   2.155 +        lua_setmetatable(L, 2);
   2.156 +        lua_pushliteral(L, MONDELEFANT_ERRCODE_CONNECTION);
   2.157 +        lua_setfield(L, 2, "code");
   2.158 +        mondelefant_push_first_line(L, PQerrorMessage(conn->pgconn));  // 3
   2.159 +        lua_setfield(L, 2, "message");
   2.160 +        return 1;
   2.161        }
   2.162        notify = PQnotifies(conn->pgconn);
   2.163        if (notify) {
   2.164 +        lua_pushnil(L);
   2.165          lua_pushstring(L, notify->relname);
   2.166          lua_pushstring(L, notify->extra);
   2.167          lua_pushinteger(L, notify->be_pid);
   2.168          PQfreemem(notify);
   2.169 -        return 3;
   2.170 +        return 4;
   2.171        }
   2.172      }
   2.173      if (infinite) {
   2.174 @@ -508,21 +517,22 @@
   2.175        select(fd+1, &fds, NULL, NULL, &timeout);
   2.176      }
   2.177    }
   2.178 -  lua_pushboolean(L, 0);
   2.179 -  if (nonblock) lua_pushliteral(L, "No notification pending");
   2.180 -  else lua_pushliteral(L, "Timeout while waiting for notification");
   2.181 +  lua_pushnil(L);
   2.182 +  lua_pushnil(L);
   2.183    return 2;
   2.184  }
   2.185  
   2.186  // method "create_list" of database handles:
   2.187  static int mondelefant_conn_create_list(lua_State *L) {
   2.188    // ensure that first argument is a database connection:
   2.189 -  lua_settop(L, 2);
   2.190    luaL_checkudata(L, 1, MONDELEFANT_CONN_MT_REGKEY);
   2.191    // if no second argument is given, use an empty table:
   2.192 -  if (!lua_toboolean(L, 2)) {
   2.193 -    lua_newtable(L);
   2.194 -    lua_replace(L, 2);  // new result at stack position 2
   2.195 +  if (lua_isnoneornil(L, 2)) {
   2.196 +    lua_settop(L, 1);
   2.197 +    lua_newtable(L);  // 2
   2.198 +  } else {
   2.199 +    luaL_checktype(L, 2, LUA_TTABLE);
   2.200 +    lua_settop(L, 2);
   2.201    }
   2.202    // set meta-table for database result lists/objects:
   2.203    lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_RESULT_MT_REGKEY);  // 3
   2.204 @@ -540,14 +550,16 @@
   2.205  // method "create_object" of database handles:
   2.206  static int mondelefant_conn_create_object(lua_State *L) {
   2.207    // ensure that first argument is a database connection:
   2.208 -  lua_settop(L, 2);
   2.209    luaL_checkudata(L, 1, MONDELEFANT_CONN_MT_REGKEY);
   2.210    // if no second argument is given, use an empty table:
   2.211 -  if (!lua_toboolean(L, 2)) {
   2.212 -    lua_newtable(L);
   2.213 -    lua_replace(L, 2);  // new result at stack position 2
   2.214 +  if (lua_isnoneornil(L, 2)) {
   2.215 +    lua_settop(L, 1);
   2.216 +    lua_newtable(L);  // 2
   2.217 +  } else {
   2.218 +    luaL_checktype(L, 2, LUA_TTABLE);
   2.219 +    lua_settop(L, 2);
   2.220    }
   2.221 -  //   set meta-table for database result lists/objects:
   2.222 +  // set meta-table for database result lists/objects:
   2.223    lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_RESULT_MT_REGKEY);  // 3
   2.224    lua_setmetatable(L, 2);
   2.225    // set "_connection" attribute to self:
   2.226 @@ -575,7 +587,6 @@
   2.227    char *output;
   2.228    size_t output_len;
   2.229    // get 'conn' attribute of C-struct of database connection:
   2.230 -  lua_settop(L, 2);
   2.231    conn = mondelefant_get_conn(L, 1);
   2.232    // get second argument, which must be a string:
   2.233    input = luaL_checklstring(L, 2, &input_len);
   2.234 @@ -613,7 +624,6 @@
   2.235    size_t output_len;
   2.236    luaL_Buffer buf;
   2.237    // get 'conn' attribute of C-struct of database connection:
   2.238 -  lua_settop(L, 2);
   2.239    conn = mondelefant_get_conn(L, 1);
   2.240    // get second argument, which must be a string:
   2.241    input = luaL_checklstring(L, 2, &input_len);
   2.242 @@ -645,11 +655,10 @@
   2.243    size_t template_pos = 0;
   2.244    luaL_Buffer buf;
   2.245    // get 'conn' attribute of C-struct of database connection:
   2.246 -  lua_settop(L, 2);
   2.247    conn = mondelefant_get_conn(L, 1);
   2.248    // if second argument is a string, return this string:
   2.249 -  if (lua_isstring(L, 2)) {
   2.250 -    lua_tostring(L, 2);
   2.251 +  if (lua_type(L, 2) == LUA_TSTRING) {
   2.252 +    lua_settop(L, 2);
   2.253      return 1;
   2.254    }
   2.255    // if second argument has __tostring meta-method,
   2.256 @@ -657,6 +666,8 @@
   2.257    if (luaL_callmeta(L, 2, "__tostring")) return 1;
   2.258    // otherwise, require that second argument is a table:
   2.259    luaL_checktype(L, 2, LUA_TTABLE);
   2.260 +  // set stack top:
   2.261 +  lua_settop(L, 2);
   2.262    // get first element of table, which must be a string:
   2.263    lua_rawgeti(L, 2, 1);  // 3
   2.264    luaL_argcheck(L,
   2.265 @@ -904,7 +915,7 @@
   2.266        } else if (!strcmp(modestr, "opt_object")) {
   2.267          mode = MONDELEFANT_QUERY_MODE_OPT_OBJECT;
   2.268        } else {
   2.269 -        return luaL_error(L, "Unknown query mode specified.");
   2.270 +        return luaL_argerror(L, mode_idx, "unknown query mode");
   2.271        }
   2.272      }
   2.273      modes[command_idx] = mode;
   2.274 @@ -961,110 +972,117 @@
   2.275        lua_setmetatable(L, 5);
   2.276        lua_pushvalue(L, 1);
   2.277        lua_setfield(L, 5, "connection");
   2.278 -      lua_pushinteger(L, command_idx + 1);
   2.279 -      lua_setfield(L, 5, "command_number");
   2.280        lua_pushvalue(L, 2);
   2.281        lua_setfield(L, 5, "sql_command");
   2.282 -      if (!res) {
   2.283 -        lua_pushliteral(L, MONDELEFANT_ERRCODE_RESULTCOUNT_LOW);
   2.284 -        lua_setfield(L, 5, "code");
   2.285 -        lua_pushliteral(L, "Received too few database result sets.");
   2.286 -        lua_setfield(L, 5, "message");
   2.287 -      } else if (command_idx >= command_count) {
   2.288 -        lua_pushliteral(L, MONDELEFANT_ERRCODE_RESULTCOUNT_HIGH);
   2.289 +      if (!sent_success) {
   2.290 +        lua_pushliteral(L, MONDELEFANT_ERRCODE_CONNECTION);
   2.291          lua_setfield(L, 5, "code");
   2.292 -        lua_pushliteral(L, "Received too many database result sets.");
   2.293 -        lua_setfield(L, 5, "message");
   2.294 -      } else if (
   2.295 -        pgstatus != PGRES_TUPLES_OK && pgstatus != PGRES_COMMAND_OK
   2.296 -      ) {
   2.297 -        const char *sqlstate;
   2.298 -        const char *errmsg;
   2.299 -        lua_pushstring(L, PQresultErrorField(res, PG_DIAG_SEVERITY));
   2.300 -        lua_setfield(L, 5, "pg_severity");
   2.301 -        sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE);
   2.302 -        if (sqlstate) {
   2.303 -          lua_pushstring(L, sqlstate);
   2.304 -          lua_setfield(L, 5, "pg_sqlstate");
   2.305 -          lua_pushstring(L, mondelefant_translate_errcode(sqlstate));
   2.306 -          lua_setfield(L, 5, "code");
   2.307 -        } else {
   2.308 -          lua_pushliteral(L, MONDELEFANT_ERRCODE_UNKNOWN);
   2.309 -          lua_setfield(L, 5, "code");
   2.310 -        }
   2.311 -        errmsg = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY);
   2.312 -        if (errmsg) {
   2.313 -          mondelefant_push_first_line(L, errmsg);
   2.314 -          lua_setfield(L, 5, "message");
   2.315 -          lua_pushstring(L, errmsg);
   2.316 -          lua_setfield(L, 5, "pg_message_primary");
   2.317 -        } else {
   2.318 -          lua_pushliteral(L,
   2.319 -            "Error while fetching result, but no error message given."
   2.320 -          );
   2.321 -          lua_setfield(L, 5, "message");
   2.322 -        }
   2.323 -        lua_pushstring(L, PQresultErrorField(res, PG_DIAG_MESSAGE_DETAIL));
   2.324 -        lua_setfield(L, 5, "pg_message_detail");
   2.325 -        lua_pushstring(L, PQresultErrorField(res, PG_DIAG_MESSAGE_HINT));
   2.326 -        lua_setfield(L, 5, "pg_message_hint");
   2.327 -        // NOTE: "position" and "pg_internal_position" are recalculated to
   2.328 -        // byte offsets, as Lua 5.1 is not Unicode aware.
   2.329 -        {
   2.330 -          char *tmp;
   2.331 -          tmp = PQresultErrorField(res, PG_DIAG_STATEMENT_POSITION);
   2.332 -          if (tmp) {
   2.333 -            int pos;
   2.334 -            pos = atoi(tmp) - 1;
   2.335 -            if (conn->server_encoding == MONDELEFANT_SERVER_ENCODING_UTF8) {
   2.336 -              pos = utf8_position_to_byte(command, pos);
   2.337 -            }
   2.338 -            lua_pushinteger(L, pos + 1);
   2.339 -            lua_setfield(L, 5, "position");
   2.340 -          }
   2.341 -        }
   2.342 -        {
   2.343 -          const char *internal_query;
   2.344 -          internal_query = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY);
   2.345 -          lua_pushstring(L, internal_query);
   2.346 -          lua_setfield(L, 5, "pg_internal_query");
   2.347 -          char *tmp;
   2.348 -          tmp = PQresultErrorField(res, PG_DIAG_INTERNAL_POSITION);
   2.349 -          if (tmp) {
   2.350 -            int pos;
   2.351 -            pos = atoi(tmp) - 1;
   2.352 -            if (conn->server_encoding == MONDELEFANT_SERVER_ENCODING_UTF8) {
   2.353 -              pos = utf8_position_to_byte(internal_query, pos);
   2.354 -            }
   2.355 -            lua_pushinteger(L, pos + 1);
   2.356 -            lua_setfield(L, 5, "pg_internal_position");
   2.357 -          }
   2.358 -        }
   2.359 -        lua_pushstring(L, PQresultErrorField(res, PG_DIAG_CONTEXT));
   2.360 -        lua_setfield(L, 5, "pg_context");
   2.361 -        lua_pushstring(L, PQresultErrorField(res, PG_DIAG_SOURCE_FILE));
   2.362 -        lua_setfield(L, 5, "pg_source_file");
   2.363 -        lua_pushstring(L, PQresultErrorField(res, PG_DIAG_SOURCE_LINE));
   2.364 -        lua_setfield(L, 5, "pg_source_line");
   2.365 -        lua_pushstring(L, PQresultErrorField(res, PG_DIAG_SOURCE_FUNCTION));
   2.366 -        lua_setfield(L, 5, "pg_source_function");
   2.367 -      } else if (rows < 1 && mode == MONDELEFANT_QUERY_MODE_OBJECT) {
   2.368 -        lua_pushliteral(L, MONDELEFANT_ERRCODE_QUERY1_NO_ROWS);
   2.369 -        lua_setfield(L, 5, "code");
   2.370 -        lua_pushliteral(L, "Expected one row, but got empty set.");
   2.371 -        lua_setfield(L, 5, "message");
   2.372 -      } else if (rows > 1 && mode != MONDELEFANT_QUERY_MODE_LIST) {
   2.373 -        lua_pushliteral(L, MONDELEFANT_ERRCODE_QUERY1_MULTIPLE_ROWS);
   2.374 -        lua_setfield(L, 5, "code");
   2.375 -        lua_pushliteral(L, "Got more than one result row.");
   2.376 +        mondelefant_push_first_line(L, PQerrorMessage(conn->pgconn));
   2.377          lua_setfield(L, 5, "message");
   2.378        } else {
   2.379 -        // should not happen
   2.380 -        abort();
   2.381 -      }
   2.382 -      if (res) {
   2.383 -        PQclear(res);
   2.384 -        while ((res = PQgetResult(conn->pgconn))) PQclear(res);
   2.385 +        lua_pushinteger(L, command_idx + 1);
   2.386 +        lua_setfield(L, 5, "command_number");
   2.387 +        if (!res) {
   2.388 +          lua_pushliteral(L, MONDELEFANT_ERRCODE_RESULTCOUNT_LOW);
   2.389 +          lua_setfield(L, 5, "code");
   2.390 +          lua_pushliteral(L, "Received too few database result sets.");
   2.391 +          lua_setfield(L, 5, "message");
   2.392 +        } else if (command_idx >= command_count) {
   2.393 +          lua_pushliteral(L, MONDELEFANT_ERRCODE_RESULTCOUNT_HIGH);
   2.394 +          lua_setfield(L, 5, "code");
   2.395 +          lua_pushliteral(L, "Received too many database result sets.");
   2.396 +          lua_setfield(L, 5, "message");
   2.397 +        } else if (
   2.398 +          pgstatus != PGRES_TUPLES_OK && pgstatus != PGRES_COMMAND_OK
   2.399 +        ) {
   2.400 +          const char *sqlstate;
   2.401 +          const char *errmsg;
   2.402 +          lua_pushstring(L, PQresultErrorField(res, PG_DIAG_SEVERITY));
   2.403 +          lua_setfield(L, 5, "pg_severity");
   2.404 +          sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE);
   2.405 +          if (sqlstate) {
   2.406 +            lua_pushstring(L, sqlstate);
   2.407 +            lua_setfield(L, 5, "pg_sqlstate");
   2.408 +            lua_pushstring(L, mondelefant_translate_errcode(sqlstate));
   2.409 +            lua_setfield(L, 5, "code");
   2.410 +          } else {
   2.411 +            lua_pushliteral(L, MONDELEFANT_ERRCODE_UNKNOWN);
   2.412 +            lua_setfield(L, 5, "code");
   2.413 +          }
   2.414 +          errmsg = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY);
   2.415 +          if (errmsg) {
   2.416 +            mondelefant_push_first_line(L, errmsg);
   2.417 +            lua_setfield(L, 5, "message");
   2.418 +            lua_pushstring(L, errmsg);
   2.419 +            lua_setfield(L, 5, "pg_message_primary");
   2.420 +          } else {
   2.421 +            lua_pushliteral(L,
   2.422 +              "Error while fetching result, but no error message given."
   2.423 +            );
   2.424 +            lua_setfield(L, 5, "message");
   2.425 +          }
   2.426 +          lua_pushstring(L, PQresultErrorField(res, PG_DIAG_MESSAGE_DETAIL));
   2.427 +          lua_setfield(L, 5, "pg_message_detail");
   2.428 +          lua_pushstring(L, PQresultErrorField(res, PG_DIAG_MESSAGE_HINT));
   2.429 +          lua_setfield(L, 5, "pg_message_hint");
   2.430 +          // NOTE: "position" and "pg_internal_position" are recalculated to
   2.431 +          // byte offsets, as Lua 5.2 is not Unicode aware.
   2.432 +          {
   2.433 +            char *tmp;
   2.434 +            tmp = PQresultErrorField(res, PG_DIAG_STATEMENT_POSITION);
   2.435 +            if (tmp) {
   2.436 +              int pos;
   2.437 +              pos = atoi(tmp) - 1;
   2.438 +              if (conn->server_encoding == MONDELEFANT_SERVER_ENCODING_UTF8) {
   2.439 +                pos = utf8_position_to_byte(command, pos);
   2.440 +              }
   2.441 +              lua_pushinteger(L, pos + 1);
   2.442 +              lua_setfield(L, 5, "position");
   2.443 +            }
   2.444 +          }
   2.445 +          {
   2.446 +            const char *internal_query;
   2.447 +            internal_query = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY);
   2.448 +            lua_pushstring(L, internal_query);
   2.449 +            lua_setfield(L, 5, "pg_internal_query");
   2.450 +            char *tmp;
   2.451 +            tmp = PQresultErrorField(res, PG_DIAG_INTERNAL_POSITION);
   2.452 +            if (tmp) {
   2.453 +              int pos;
   2.454 +              pos = atoi(tmp) - 1;
   2.455 +              if (conn->server_encoding == MONDELEFANT_SERVER_ENCODING_UTF8) {
   2.456 +                pos = utf8_position_to_byte(internal_query, pos);
   2.457 +              }
   2.458 +              lua_pushinteger(L, pos + 1);
   2.459 +              lua_setfield(L, 5, "pg_internal_position");
   2.460 +            }
   2.461 +          }
   2.462 +          lua_pushstring(L, PQresultErrorField(res, PG_DIAG_CONTEXT));
   2.463 +          lua_setfield(L, 5, "pg_context");
   2.464 +          lua_pushstring(L, PQresultErrorField(res, PG_DIAG_SOURCE_FILE));
   2.465 +          lua_setfield(L, 5, "pg_source_file");
   2.466 +          lua_pushstring(L, PQresultErrorField(res, PG_DIAG_SOURCE_LINE));
   2.467 +          lua_setfield(L, 5, "pg_source_line");
   2.468 +          lua_pushstring(L, PQresultErrorField(res, PG_DIAG_SOURCE_FUNCTION));
   2.469 +          lua_setfield(L, 5, "pg_source_function");
   2.470 +        } else if (rows < 1 && mode == MONDELEFANT_QUERY_MODE_OBJECT) {
   2.471 +          lua_pushliteral(L, MONDELEFANT_ERRCODE_QUERY1_NO_ROWS);
   2.472 +          lua_setfield(L, 5, "code");
   2.473 +          lua_pushliteral(L, "Expected one row, but got empty set.");
   2.474 +          lua_setfield(L, 5, "message");
   2.475 +        } else if (rows > 1 && mode != MONDELEFANT_QUERY_MODE_LIST) {
   2.476 +          lua_pushliteral(L, MONDELEFANT_ERRCODE_QUERY1_MULTIPLE_ROWS);
   2.477 +          lua_setfield(L, 5, "code");
   2.478 +          lua_pushliteral(L, "Got more than one result row.");
   2.479 +          lua_setfield(L, 5, "message");
   2.480 +        } else {
   2.481 +          // should not happen
   2.482 +          abort();
   2.483 +        }
   2.484 +        if (res) {
   2.485 +          PQclear(res);
   2.486 +          while ((res = PQgetResult(conn->pgconn))) PQclear(res);
   2.487 +        }
   2.488        }
   2.489        if (lua_toboolean(L, 3)) {
   2.490          lua_pushvalue(L, 3);
   2.491 @@ -1312,43 +1330,45 @@
   2.492    return command_count+1;
   2.493  }
   2.494  
   2.495 -// method "escalate" of error objects:
   2.496 -static int mondelefant_errorobject_escalate(lua_State *L) {
   2.497 -  // check, if we may throw an error object instead of an error string:
   2.498 -  lua_settop(L, 1);
   2.499 -  lua_getfield(L, 1, "connection");  // 2
   2.500 -  lua_getfield(L, 2, "error_objects");  // 3
   2.501 -  if (lua_toboolean(L, 3)) {
   2.502 -    // throw error object:
   2.503 +// method "is_kind_of" of error objects:
   2.504 +static int mondelefant_errorobject_is_kind_of(lua_State *L) {
   2.505 +  const char *errclass;
   2.506 +  luaL_checktype(L, 1, LUA_TTABLE);
   2.507 +  errclass = luaL_checkstring(L, 2);
   2.508 +  lua_settop(L, 2);
   2.509 +  lua_getfield(L, 1, "code");  // 3
   2.510 +  luaL_argcheck(L,
   2.511 +    lua_type(L, 3) == LUA_TSTRING,
   2.512 +    1,
   2.513 +    "field 'code' of error object is not a string"
   2.514 +  );
   2.515 +  lua_pushboolean(L,
   2.516 +    mondelefant_check_error_class(lua_tostring(L, 3), errclass)
   2.517 +  );
   2.518 +  return 1;
   2.519 +}
   2.520 +
   2.521 +// method "wait" of database handles:
   2.522 +static int mondelefant_conn_wait(lua_State *L) {
   2.523 +  int argc;
   2.524 +  // count number of arguments:
   2.525 +  argc = lua_gettop(L);
   2.526 +  // insert "try_wait" function/method at stack position 1:
   2.527 +  lua_pushcfunction(L, mondelefant_conn_try_wait);
   2.528 +  lua_insert(L, 1);
   2.529 +  // call "try_wait" method:
   2.530 +  lua_call(L, argc, LUA_MULTRET);  // results (with error) starting at index 1
   2.531 +  // check, if error occurred:
   2.532 +  if (lua_toboolean(L, 1)) {
   2.533 +    // raise error
   2.534      lua_settop(L, 1);
   2.535      return lua_error(L);
   2.536    } else {
   2.537 -    // throw error string:
   2.538 -    lua_getfield(L, 1, "message");  // 4
   2.539 -    if (lua_isnil(L, 4)) {
   2.540 -      return luaL_error(L, "No error message given for escalation.");
   2.541 -    }
   2.542 -    return lua_error(L);
   2.543 +    // return everything but nil error object:
   2.544 +    return lua_gettop(L) - 1;
   2.545    }
   2.546  }
   2.547  
   2.548 -// method "is_kind_of" of error objects:
   2.549 -static int mondelefant_errorobject_is_kind_of(lua_State *L) {
   2.550 -  lua_settop(L, 2);
   2.551 -  lua_getfield(L, 1, "code");  // 3
   2.552 -  if (lua_isstring(L, 3)) {
   2.553 -    lua_pushboolean(L,
   2.554 -      mondelefant_check_error_class(
   2.555 -        lua_tostring(L, 3), luaL_checkstring(L, 2)
   2.556 -      )
   2.557 -    );
   2.558 -  } else {
   2.559 -    // only happens for errors where code is not set
   2.560 -    lua_pushboolean(L, 0);
   2.561 -  }
   2.562 -  return 1;
   2.563 -}
   2.564 -
   2.565  // method "query" of database handles:
   2.566  static int mondelefant_conn_query(lua_State *L) {
   2.567    int argc;
   2.568 @@ -1361,11 +1381,9 @@
   2.569    lua_call(L, argc, LUA_MULTRET);  // results (with error) starting at index 1
   2.570    // check, if error occurred:
   2.571    if (lua_toboolean(L, 1)) {
   2.572 -    // invoke escalate method of error object:
   2.573 -    lua_pushcfunction(L, mondelefant_errorobject_escalate);
   2.574 -    lua_pushvalue(L, 1);
   2.575 -    lua_call(L, 1, 0);  // will raise an error
   2.576 -    return 0;  // should not be executed
   2.577 +    // raise error
   2.578 +    lua_settop(L, 1);
   2.579 +    return lua_error(L);
   2.580    } else {
   2.581      // return everything but nil error object:
   2.582      return lua_gettop(L) - 1;
   2.583 @@ -1420,11 +1438,13 @@
   2.584  
   2.585  // library function "new_class":
   2.586  static int mondelefant_new_class(lua_State *L) {
   2.587 -  // use first argument as template or create new table:
   2.588 -  lua_settop(L, 1);
   2.589 -  if (!lua_toboolean(L, 1)) {
   2.590 +  // if no argument is given, use an empty table:
   2.591 +  if (lua_isnoneornil(L, 1)) {
   2.592      lua_settop(L, 0);
   2.593      lua_newtable(L);  // 1
   2.594 +  } else {
   2.595 +    luaL_checktype(L, 1, LUA_TTABLE);
   2.596 +    lua_settop(L, 1);
   2.597    }
   2.598    // set meta-table for database classes (models):
   2.599    lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_CLASS_MT_REGKEY);  // 2
   2.600 @@ -1843,6 +1863,7 @@
   2.601    {"close", mondelefant_conn_close},
   2.602    {"is_ok", mondelefant_conn_is_ok},
   2.603    {"get_transaction_status", mondelefant_conn_get_transaction_status},
   2.604 +  {"try_wait", mondelefant_conn_try_wait},
   2.605    {"wait", mondelefant_conn_wait},
   2.606    {"create_list", mondelefant_conn_create_list},
   2.607    {"create_object", mondelefant_conn_create_object},
   2.608 @@ -1861,7 +1882,7 @@
   2.609  
   2.610  // registration information for methods of error objects:
   2.611  static const struct luaL_Reg mondelefant_errorobject_methods[] = {
   2.612 -  {"escalate", mondelefant_errorobject_escalate},
   2.613 +  {"escalate", lua_error},
   2.614    {"is_kind_of", mondelefant_errorobject_is_kind_of},
   2.615    {NULL, NULL}
   2.616  };

Impressum / About Us