webmcp

diff libraries/mondelefant/mondelefant_native.c @ 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
line diff
     1.1 --- a/libraries/mondelefant/mondelefant_native.c	Wed Dec 09 21:16:24 2015 +0100
     1.2 +++ b/libraries/mondelefant/mondelefant_native.c	Thu Dec 10 17:23:51 2015 +0100
     1.3 @@ -179,17 +179,16 @@
     1.4  
     1.5  // pushing first line of a string on Lua's stack (without trailing CR/LF):
     1.6  static void mondelefant_push_first_line(lua_State *L, const char *str) {
     1.7 -  char *str2;
     1.8    size_t i = 0;
     1.9    if (!str) abort();  // should not happen
    1.10 -  str2 = strdup(str);
    1.11    while (1) {
    1.12 -    char c = str2[i];
    1.13 -    if (c == '\n' || c == '\r' || c == 0) { str2[i] = 0; break; }
    1.14 +    char c = str[i];
    1.15 +    if (c == '\n' || c == '\r' || c == 0) {
    1.16 +      lua_pushlstring(L, str, i);
    1.17 +      return;
    1.18 +    }
    1.19      i++;
    1.20 -  };
    1.21 -  lua_pushstring(L, str2);
    1.22 -  free(str2);
    1.23 +  }
    1.24  }
    1.25  
    1.26  // "connect" function of library, which establishes a database connection
    1.27 @@ -199,16 +198,18 @@
    1.28    const char *conninfo;  // string for PQconnectdb function
    1.29    PGconn *pgconn;  // PGconn object as returned by PQconnectdb function
    1.30    mondelefant_conn_t *conn;  // C-structure for userdata
    1.31 +  // expect a table as first argument:
    1.32 +  luaL_checktype(L, 1, LUA_TTABLE);
    1.33    // if engine is anything but "postgresql", then raise error:
    1.34    lua_settop(L, 1);
    1.35    lua_getfield(L, 1, "engine");  // 2
    1.36    if (!lua_toboolean(L, 2)) {
    1.37 -    return luaL_error(L, "No database engine selected.");
    1.38 +    return luaL_argerror(L, 1, "no database engine selected");
    1.39    }
    1.40    lua_pushliteral(L, "postgresql");  // 3
    1.41    if (!lua_rawequal(L, 2, 3)) {
    1.42 -    return luaL_error(L,
    1.43 -      "Only database engine 'postgresql' is supported."
    1.44 +    return luaL_argerror(L, 1,
    1.45 +      "only database engine 'postgresql' is supported"
    1.46      );
    1.47    }
    1.48    // copy conninfo string for PQconnectdb function from argument table to
    1.49 @@ -230,7 +231,7 @@
    1.50          // NOTE: numbers will be converted to strings automatically here,
    1.51          // but perhaps this will change in future versions of lua
    1.52          luaL_argcheck(L,
    1.53 -          lua_isstring(L, 2) && lua_isstring(L, 3), 1, "non-string contained"
    1.54 +          lua_isstring(L, 2) && lua_isstring(L, 3), 1, "key is not a string"
    1.55          );
    1.56          lua_pushvalue(L, 2);
    1.57          lua_pushliteral(L, "engine");
    1.58 @@ -265,24 +266,25 @@
    1.59    conninfo = lua_tostring(L, 2);
    1.60    // call PQconnectdb function of libpq:
    1.61    pgconn = PQconnectdb(conninfo);
    1.62 -  // throw errors, if neccessary:
    1.63 +  // throw or return errors, if neccessary:
    1.64    if (!pgconn) {
    1.65      return luaL_error(L,
    1.66        "Error in libpq while creating 'PGconn' structure."
    1.67      );
    1.68    }
    1.69    if (PQstatus(pgconn) != CONNECTION_OK) {
    1.70 -    const char *errmsg;
    1.71 -    lua_pushnil(L);
    1.72 -    errmsg = PQerrorMessage(pgconn);
    1.73 -    if (errmsg) {
    1.74 -      mondelefant_push_first_line(L, errmsg);
    1.75 -    } else {
    1.76 -      lua_pushliteral(L,
    1.77 -        "Error while connecting to database, but no error message given."
    1.78 -      );
    1.79 -    }
    1.80 +    lua_pushnil(L);  // 3
    1.81 +    mondelefant_push_first_line(L, PQerrorMessage(pgconn));  // 4
    1.82 +    lua_newtable(L);  // 5
    1.83 +    lua_getfield(L,
    1.84 +      LUA_REGISTRYINDEX,
    1.85 +      MONDELEFANT_ERROROBJECT_MT_REGKEY
    1.86 +    );
    1.87 +    lua_setmetatable(L, 5);
    1.88      lua_pushliteral(L, MONDELEFANT_ERRCODE_CONNECTION);
    1.89 +    lua_setfield(L, 5, "code");
    1.90 +    lua_pushvalue(L, 4);
    1.91 +    lua_setfield(L, 5, "message");
    1.92      PQfinish(pgconn);
    1.93      return 3;
    1.94    }
    1.95 @@ -390,11 +392,10 @@
    1.96  // method "close" of database handles:
    1.97  static int mondelefant_conn_close(lua_State *L) {
    1.98    mondelefant_conn_t *conn;
    1.99 -  lua_settop(L, 1);
   1.100    conn = mondelefant_get_conn(L, 1);
   1.101    PQfinish(conn->pgconn);
   1.102    conn->pgconn = NULL;
   1.103 -  lua_pushnil(L);  // 2
   1.104 +  lua_pushnil(L);
   1.105    lua_setfield(L, 1, "fd");  // set "fd" attribute to nil
   1.106    return 0;
   1.107  }
   1.108 @@ -402,7 +403,6 @@
   1.109  // method "is_okay" of database handles:
   1.110  static int mondelefant_conn_is_ok(lua_State *L) {
   1.111    mondelefant_conn_t *conn;
   1.112 -  lua_settop(L, 1);
   1.113    conn = mondelefant_get_conn(L, 1);
   1.114    lua_pushboolean(L, PQstatus(conn->pgconn) == CONNECTION_OK);
   1.115    return 1;
   1.116 @@ -411,7 +411,6 @@
   1.117  // method "get_transaction_status" of database handles:
   1.118  static int mondelefant_conn_get_transaction_status(lua_State *L) {
   1.119    mondelefant_conn_t *conn;
   1.120 -  lua_settop(L, 1);
   1.121    conn = mondelefant_get_conn(L, 1);
   1.122    switch (PQtransactionStatus(conn->pgconn)) {
   1.123    case PQTRANS_IDLE:
   1.124 @@ -432,8 +431,8 @@
   1.125    return 1;
   1.126  }
   1.127  
   1.128 -// method "wait" of database handles:
   1.129 -static int mondelefant_conn_wait(lua_State *L) {
   1.130 +// method "try_wait" of database handles:
   1.131 +static int mondelefant_conn_try_wait(lua_State *L) {
   1.132    mondelefant_conn_t *conn;
   1.133    int infinite, nonblock = 0;
   1.134    struct timespec wakeup;
   1.135 @@ -461,6 +460,7 @@
   1.136        luaL_argcheck(L, 0, 2, "not a valid timeout");
   1.137      }
   1.138    }
   1.139 +  lua_settop(L, 1);
   1.140    if (!nonblock) {
   1.141      fd = PQsocket(conn->pgconn);
   1.142      FD_ZERO(&fds);
   1.143 @@ -470,17 +470,26 @@
   1.144      {
   1.145        PGnotify *notify;
   1.146        if (!PQconsumeInput(conn->pgconn)) {
   1.147 -        lua_pushnil(L);
   1.148 -        mondelefant_push_first_line(L, PQerrorMessage(conn->pgconn));
   1.149 -        return 2;
   1.150 +        lua_newtable(L);  // 2
   1.151 +        lua_getfield(L,
   1.152 +          LUA_REGISTRYINDEX,
   1.153 +          MONDELEFANT_ERROROBJECT_MT_REGKEY
   1.154 +        );
   1.155 +        lua_setmetatable(L, 2);
   1.156 +        lua_pushliteral(L, MONDELEFANT_ERRCODE_CONNECTION);
   1.157 +        lua_setfield(L, 2, "code");
   1.158 +        mondelefant_push_first_line(L, PQerrorMessage(conn->pgconn));  // 3
   1.159 +        lua_setfield(L, 2, "message");
   1.160 +        return 1;
   1.161        }
   1.162        notify = PQnotifies(conn->pgconn);
   1.163        if (notify) {
   1.164 +        lua_pushnil(L);
   1.165          lua_pushstring(L, notify->relname);
   1.166          lua_pushstring(L, notify->extra);
   1.167          lua_pushinteger(L, notify->be_pid);
   1.168          PQfreemem(notify);
   1.169 -        return 3;
   1.170 +        return 4;
   1.171        }
   1.172      }
   1.173      if (infinite) {
   1.174 @@ -508,21 +517,22 @@
   1.175        select(fd+1, &fds, NULL, NULL, &timeout);
   1.176      }
   1.177    }
   1.178 -  lua_pushboolean(L, 0);
   1.179 -  if (nonblock) lua_pushliteral(L, "No notification pending");
   1.180 -  else lua_pushliteral(L, "Timeout while waiting for notification");
   1.181 +  lua_pushnil(L);
   1.182 +  lua_pushnil(L);
   1.183    return 2;
   1.184  }
   1.185  
   1.186  // method "create_list" of database handles:
   1.187  static int mondelefant_conn_create_list(lua_State *L) {
   1.188    // ensure that first argument is a database connection:
   1.189 -  lua_settop(L, 2);
   1.190    luaL_checkudata(L, 1, MONDELEFANT_CONN_MT_REGKEY);
   1.191    // if no second argument is given, use an empty table:
   1.192 -  if (!lua_toboolean(L, 2)) {
   1.193 -    lua_newtable(L);
   1.194 -    lua_replace(L, 2);  // new result at stack position 2
   1.195 +  if (lua_isnoneornil(L, 2)) {
   1.196 +    lua_settop(L, 1);
   1.197 +    lua_newtable(L);  // 2
   1.198 +  } else {
   1.199 +    luaL_checktype(L, 2, LUA_TTABLE);
   1.200 +    lua_settop(L, 2);
   1.201    }
   1.202    // set meta-table for database result lists/objects:
   1.203    lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_RESULT_MT_REGKEY);  // 3
   1.204 @@ -540,14 +550,16 @@
   1.205  // method "create_object" of database handles:
   1.206  static int mondelefant_conn_create_object(lua_State *L) {
   1.207    // ensure that first argument is a database connection:
   1.208 -  lua_settop(L, 2);
   1.209    luaL_checkudata(L, 1, MONDELEFANT_CONN_MT_REGKEY);
   1.210    // if no second argument is given, use an empty table:
   1.211 -  if (!lua_toboolean(L, 2)) {
   1.212 -    lua_newtable(L);
   1.213 -    lua_replace(L, 2);  // new result at stack position 2
   1.214 +  if (lua_isnoneornil(L, 2)) {
   1.215 +    lua_settop(L, 1);
   1.216 +    lua_newtable(L);  // 2
   1.217 +  } else {
   1.218 +    luaL_checktype(L, 2, LUA_TTABLE);
   1.219 +    lua_settop(L, 2);
   1.220    }
   1.221 -  //   set meta-table for database result lists/objects:
   1.222 +  // set meta-table for database result lists/objects:
   1.223    lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_RESULT_MT_REGKEY);  // 3
   1.224    lua_setmetatable(L, 2);
   1.225    // set "_connection" attribute to self:
   1.226 @@ -575,7 +587,6 @@
   1.227    char *output;
   1.228    size_t output_len;
   1.229    // get 'conn' attribute of C-struct of database connection:
   1.230 -  lua_settop(L, 2);
   1.231    conn = mondelefant_get_conn(L, 1);
   1.232    // get second argument, which must be a string:
   1.233    input = luaL_checklstring(L, 2, &input_len);
   1.234 @@ -613,7 +624,6 @@
   1.235    size_t output_len;
   1.236    luaL_Buffer buf;
   1.237    // get 'conn' attribute of C-struct of database connection:
   1.238 -  lua_settop(L, 2);
   1.239    conn = mondelefant_get_conn(L, 1);
   1.240    // get second argument, which must be a string:
   1.241    input = luaL_checklstring(L, 2, &input_len);
   1.242 @@ -645,11 +655,10 @@
   1.243    size_t template_pos = 0;
   1.244    luaL_Buffer buf;
   1.245    // get 'conn' attribute of C-struct of database connection:
   1.246 -  lua_settop(L, 2);
   1.247    conn = mondelefant_get_conn(L, 1);
   1.248    // if second argument is a string, return this string:
   1.249 -  if (lua_isstring(L, 2)) {
   1.250 -    lua_tostring(L, 2);
   1.251 +  if (lua_type(L, 2) == LUA_TSTRING) {
   1.252 +    lua_settop(L, 2);
   1.253      return 1;
   1.254    }
   1.255    // if second argument has __tostring meta-method,
   1.256 @@ -657,6 +666,8 @@
   1.257    if (luaL_callmeta(L, 2, "__tostring")) return 1;
   1.258    // otherwise, require that second argument is a table:
   1.259    luaL_checktype(L, 2, LUA_TTABLE);
   1.260 +  // set stack top:
   1.261 +  lua_settop(L, 2);
   1.262    // get first element of table, which must be a string:
   1.263    lua_rawgeti(L, 2, 1);  // 3
   1.264    luaL_argcheck(L,
   1.265 @@ -904,7 +915,7 @@
   1.266        } else if (!strcmp(modestr, "opt_object")) {
   1.267          mode = MONDELEFANT_QUERY_MODE_OPT_OBJECT;
   1.268        } else {
   1.269 -        return luaL_error(L, "Unknown query mode specified.");
   1.270 +        return luaL_argerror(L, mode_idx, "unknown query mode");
   1.271        }
   1.272      }
   1.273      modes[command_idx] = mode;
   1.274 @@ -961,110 +972,117 @@
   1.275        lua_setmetatable(L, 5);
   1.276        lua_pushvalue(L, 1);
   1.277        lua_setfield(L, 5, "connection");
   1.278 -      lua_pushinteger(L, command_idx + 1);
   1.279 -      lua_setfield(L, 5, "command_number");
   1.280        lua_pushvalue(L, 2);
   1.281        lua_setfield(L, 5, "sql_command");
   1.282 -      if (!res) {
   1.283 -        lua_pushliteral(L, MONDELEFANT_ERRCODE_RESULTCOUNT_LOW);
   1.284 -        lua_setfield(L, 5, "code");
   1.285 -        lua_pushliteral(L, "Received too few database result sets.");
   1.286 -        lua_setfield(L, 5, "message");
   1.287 -      } else if (command_idx >= command_count) {
   1.288 -        lua_pushliteral(L, MONDELEFANT_ERRCODE_RESULTCOUNT_HIGH);
   1.289 +      if (!sent_success) {
   1.290 +        lua_pushliteral(L, MONDELEFANT_ERRCODE_CONNECTION);
   1.291          lua_setfield(L, 5, "code");
   1.292 -        lua_pushliteral(L, "Received too many database result sets.");
   1.293 -        lua_setfield(L, 5, "message");
   1.294 -      } else if (
   1.295 -        pgstatus != PGRES_TUPLES_OK && pgstatus != PGRES_COMMAND_OK
   1.296 -      ) {
   1.297 -        const char *sqlstate;
   1.298 -        const char *errmsg;
   1.299 -        lua_pushstring(L, PQresultErrorField(res, PG_DIAG_SEVERITY));
   1.300 -        lua_setfield(L, 5, "pg_severity");
   1.301 -        sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE);
   1.302 -        if (sqlstate) {
   1.303 -          lua_pushstring(L, sqlstate);
   1.304 -          lua_setfield(L, 5, "pg_sqlstate");
   1.305 -          lua_pushstring(L, mondelefant_translate_errcode(sqlstate));
   1.306 -          lua_setfield(L, 5, "code");
   1.307 -        } else {
   1.308 -          lua_pushliteral(L, MONDELEFANT_ERRCODE_UNKNOWN);
   1.309 -          lua_setfield(L, 5, "code");
   1.310 -        }
   1.311 -        errmsg = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY);
   1.312 -        if (errmsg) {
   1.313 -          mondelefant_push_first_line(L, errmsg);
   1.314 -          lua_setfield(L, 5, "message");
   1.315 -          lua_pushstring(L, errmsg);
   1.316 -          lua_setfield(L, 5, "pg_message_primary");
   1.317 -        } else {
   1.318 -          lua_pushliteral(L,
   1.319 -            "Error while fetching result, but no error message given."
   1.320 -          );
   1.321 -          lua_setfield(L, 5, "message");
   1.322 -        }
   1.323 -        lua_pushstring(L, PQresultErrorField(res, PG_DIAG_MESSAGE_DETAIL));
   1.324 -        lua_setfield(L, 5, "pg_message_detail");
   1.325 -        lua_pushstring(L, PQresultErrorField(res, PG_DIAG_MESSAGE_HINT));
   1.326 -        lua_setfield(L, 5, "pg_message_hint");
   1.327 -        // NOTE: "position" and "pg_internal_position" are recalculated to
   1.328 -        // byte offsets, as Lua 5.1 is not Unicode aware.
   1.329 -        {
   1.330 -          char *tmp;
   1.331 -          tmp = PQresultErrorField(res, PG_DIAG_STATEMENT_POSITION);
   1.332 -          if (tmp) {
   1.333 -            int pos;
   1.334 -            pos = atoi(tmp) - 1;
   1.335 -            if (conn->server_encoding == MONDELEFANT_SERVER_ENCODING_UTF8) {
   1.336 -              pos = utf8_position_to_byte(command, pos);
   1.337 -            }
   1.338 -            lua_pushinteger(L, pos + 1);
   1.339 -            lua_setfield(L, 5, "position");
   1.340 -          }
   1.341 -        }
   1.342 -        {
   1.343 -          const char *internal_query;
   1.344 -          internal_query = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY);
   1.345 -          lua_pushstring(L, internal_query);
   1.346 -          lua_setfield(L, 5, "pg_internal_query");
   1.347 -          char *tmp;
   1.348 -          tmp = PQresultErrorField(res, PG_DIAG_INTERNAL_POSITION);
   1.349 -          if (tmp) {
   1.350 -            int pos;
   1.351 -            pos = atoi(tmp) - 1;
   1.352 -            if (conn->server_encoding == MONDELEFANT_SERVER_ENCODING_UTF8) {
   1.353 -              pos = utf8_position_to_byte(internal_query, pos);
   1.354 -            }
   1.355 -            lua_pushinteger(L, pos + 1);
   1.356 -            lua_setfield(L, 5, "pg_internal_position");
   1.357 -          }
   1.358 -        }
   1.359 -        lua_pushstring(L, PQresultErrorField(res, PG_DIAG_CONTEXT));
   1.360 -        lua_setfield(L, 5, "pg_context");
   1.361 -        lua_pushstring(L, PQresultErrorField(res, PG_DIAG_SOURCE_FILE));
   1.362 -        lua_setfield(L, 5, "pg_source_file");
   1.363 -        lua_pushstring(L, PQresultErrorField(res, PG_DIAG_SOURCE_LINE));
   1.364 -        lua_setfield(L, 5, "pg_source_line");
   1.365 -        lua_pushstring(L, PQresultErrorField(res, PG_DIAG_SOURCE_FUNCTION));
   1.366 -        lua_setfield(L, 5, "pg_source_function");
   1.367 -      } else if (rows < 1 && mode == MONDELEFANT_QUERY_MODE_OBJECT) {
   1.368 -        lua_pushliteral(L, MONDELEFANT_ERRCODE_QUERY1_NO_ROWS);
   1.369 -        lua_setfield(L, 5, "code");
   1.370 -        lua_pushliteral(L, "Expected one row, but got empty set.");
   1.371 -        lua_setfield(L, 5, "message");
   1.372 -      } else if (rows > 1 && mode != MONDELEFANT_QUERY_MODE_LIST) {
   1.373 -        lua_pushliteral(L, MONDELEFANT_ERRCODE_QUERY1_MULTIPLE_ROWS);
   1.374 -        lua_setfield(L, 5, "code");
   1.375 -        lua_pushliteral(L, "Got more than one result row.");
   1.376 +        mondelefant_push_first_line(L, PQerrorMessage(conn->pgconn));
   1.377          lua_setfield(L, 5, "message");
   1.378        } else {
   1.379 -        // should not happen
   1.380 -        abort();
   1.381 -      }
   1.382 -      if (res) {
   1.383 -        PQclear(res);
   1.384 -        while ((res = PQgetResult(conn->pgconn))) PQclear(res);
   1.385 +        lua_pushinteger(L, command_idx + 1);
   1.386 +        lua_setfield(L, 5, "command_number");
   1.387 +        if (!res) {
   1.388 +          lua_pushliteral(L, MONDELEFANT_ERRCODE_RESULTCOUNT_LOW);
   1.389 +          lua_setfield(L, 5, "code");
   1.390 +          lua_pushliteral(L, "Received too few database result sets.");
   1.391 +          lua_setfield(L, 5, "message");
   1.392 +        } else if (command_idx >= command_count) {
   1.393 +          lua_pushliteral(L, MONDELEFANT_ERRCODE_RESULTCOUNT_HIGH);
   1.394 +          lua_setfield(L, 5, "code");
   1.395 +          lua_pushliteral(L, "Received too many database result sets.");
   1.396 +          lua_setfield(L, 5, "message");
   1.397 +        } else if (
   1.398 +          pgstatus != PGRES_TUPLES_OK && pgstatus != PGRES_COMMAND_OK
   1.399 +        ) {
   1.400 +          const char *sqlstate;
   1.401 +          const char *errmsg;
   1.402 +          lua_pushstring(L, PQresultErrorField(res, PG_DIAG_SEVERITY));
   1.403 +          lua_setfield(L, 5, "pg_severity");
   1.404 +          sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE);
   1.405 +          if (sqlstate) {
   1.406 +            lua_pushstring(L, sqlstate);
   1.407 +            lua_setfield(L, 5, "pg_sqlstate");
   1.408 +            lua_pushstring(L, mondelefant_translate_errcode(sqlstate));
   1.409 +            lua_setfield(L, 5, "code");
   1.410 +          } else {
   1.411 +            lua_pushliteral(L, MONDELEFANT_ERRCODE_UNKNOWN);
   1.412 +            lua_setfield(L, 5, "code");
   1.413 +          }
   1.414 +          errmsg = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY);
   1.415 +          if (errmsg) {
   1.416 +            mondelefant_push_first_line(L, errmsg);
   1.417 +            lua_setfield(L, 5, "message");
   1.418 +            lua_pushstring(L, errmsg);
   1.419 +            lua_setfield(L, 5, "pg_message_primary");
   1.420 +          } else {
   1.421 +            lua_pushliteral(L,
   1.422 +              "Error while fetching result, but no error message given."
   1.423 +            );
   1.424 +            lua_setfield(L, 5, "message");
   1.425 +          }
   1.426 +          lua_pushstring(L, PQresultErrorField(res, PG_DIAG_MESSAGE_DETAIL));
   1.427 +          lua_setfield(L, 5, "pg_message_detail");
   1.428 +          lua_pushstring(L, PQresultErrorField(res, PG_DIAG_MESSAGE_HINT));
   1.429 +          lua_setfield(L, 5, "pg_message_hint");
   1.430 +          // NOTE: "position" and "pg_internal_position" are recalculated to
   1.431 +          // byte offsets, as Lua 5.2 is not Unicode aware.
   1.432 +          {
   1.433 +            char *tmp;
   1.434 +            tmp = PQresultErrorField(res, PG_DIAG_STATEMENT_POSITION);
   1.435 +            if (tmp) {
   1.436 +              int pos;
   1.437 +              pos = atoi(tmp) - 1;
   1.438 +              if (conn->server_encoding == MONDELEFANT_SERVER_ENCODING_UTF8) {
   1.439 +                pos = utf8_position_to_byte(command, pos);
   1.440 +              }
   1.441 +              lua_pushinteger(L, pos + 1);
   1.442 +              lua_setfield(L, 5, "position");
   1.443 +            }
   1.444 +          }
   1.445 +          {
   1.446 +            const char *internal_query;
   1.447 +            internal_query = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY);
   1.448 +            lua_pushstring(L, internal_query);
   1.449 +            lua_setfield(L, 5, "pg_internal_query");
   1.450 +            char *tmp;
   1.451 +            tmp = PQresultErrorField(res, PG_DIAG_INTERNAL_POSITION);
   1.452 +            if (tmp) {
   1.453 +              int pos;
   1.454 +              pos = atoi(tmp) - 1;
   1.455 +              if (conn->server_encoding == MONDELEFANT_SERVER_ENCODING_UTF8) {
   1.456 +                pos = utf8_position_to_byte(internal_query, pos);
   1.457 +              }
   1.458 +              lua_pushinteger(L, pos + 1);
   1.459 +              lua_setfield(L, 5, "pg_internal_position");
   1.460 +            }
   1.461 +          }
   1.462 +          lua_pushstring(L, PQresultErrorField(res, PG_DIAG_CONTEXT));
   1.463 +          lua_setfield(L, 5, "pg_context");
   1.464 +          lua_pushstring(L, PQresultErrorField(res, PG_DIAG_SOURCE_FILE));
   1.465 +          lua_setfield(L, 5, "pg_source_file");
   1.466 +          lua_pushstring(L, PQresultErrorField(res, PG_DIAG_SOURCE_LINE));
   1.467 +          lua_setfield(L, 5, "pg_source_line");
   1.468 +          lua_pushstring(L, PQresultErrorField(res, PG_DIAG_SOURCE_FUNCTION));
   1.469 +          lua_setfield(L, 5, "pg_source_function");
   1.470 +        } else if (rows < 1 && mode == MONDELEFANT_QUERY_MODE_OBJECT) {
   1.471 +          lua_pushliteral(L, MONDELEFANT_ERRCODE_QUERY1_NO_ROWS);
   1.472 +          lua_setfield(L, 5, "code");
   1.473 +          lua_pushliteral(L, "Expected one row, but got empty set.");
   1.474 +          lua_setfield(L, 5, "message");
   1.475 +        } else if (rows > 1 && mode != MONDELEFANT_QUERY_MODE_LIST) {
   1.476 +          lua_pushliteral(L, MONDELEFANT_ERRCODE_QUERY1_MULTIPLE_ROWS);
   1.477 +          lua_setfield(L, 5, "code");
   1.478 +          lua_pushliteral(L, "Got more than one result row.");
   1.479 +          lua_setfield(L, 5, "message");
   1.480 +        } else {
   1.481 +          // should not happen
   1.482 +          abort();
   1.483 +        }
   1.484 +        if (res) {
   1.485 +          PQclear(res);
   1.486 +          while ((res = PQgetResult(conn->pgconn))) PQclear(res);
   1.487 +        }
   1.488        }
   1.489        if (lua_toboolean(L, 3)) {
   1.490          lua_pushvalue(L, 3);
   1.491 @@ -1312,43 +1330,45 @@
   1.492    return command_count+1;
   1.493  }
   1.494  
   1.495 -// method "escalate" of error objects:
   1.496 -static int mondelefant_errorobject_escalate(lua_State *L) {
   1.497 -  // check, if we may throw an error object instead of an error string:
   1.498 -  lua_settop(L, 1);
   1.499 -  lua_getfield(L, 1, "connection");  // 2
   1.500 -  lua_getfield(L, 2, "error_objects");  // 3
   1.501 -  if (lua_toboolean(L, 3)) {
   1.502 -    // throw error object:
   1.503 +// method "is_kind_of" of error objects:
   1.504 +static int mondelefant_errorobject_is_kind_of(lua_State *L) {
   1.505 +  const char *errclass;
   1.506 +  luaL_checktype(L, 1, LUA_TTABLE);
   1.507 +  errclass = luaL_checkstring(L, 2);
   1.508 +  lua_settop(L, 2);
   1.509 +  lua_getfield(L, 1, "code");  // 3
   1.510 +  luaL_argcheck(L,
   1.511 +    lua_type(L, 3) == LUA_TSTRING,
   1.512 +    1,
   1.513 +    "field 'code' of error object is not a string"
   1.514 +  );
   1.515 +  lua_pushboolean(L,
   1.516 +    mondelefant_check_error_class(lua_tostring(L, 3), errclass)
   1.517 +  );
   1.518 +  return 1;
   1.519 +}
   1.520 +
   1.521 +// method "wait" of database handles:
   1.522 +static int mondelefant_conn_wait(lua_State *L) {
   1.523 +  int argc;
   1.524 +  // count number of arguments:
   1.525 +  argc = lua_gettop(L);
   1.526 +  // insert "try_wait" function/method at stack position 1:
   1.527 +  lua_pushcfunction(L, mondelefant_conn_try_wait);
   1.528 +  lua_insert(L, 1);
   1.529 +  // call "try_wait" method:
   1.530 +  lua_call(L, argc, LUA_MULTRET);  // results (with error) starting at index 1
   1.531 +  // check, if error occurred:
   1.532 +  if (lua_toboolean(L, 1)) {
   1.533 +    // raise error
   1.534      lua_settop(L, 1);
   1.535      return lua_error(L);
   1.536    } else {
   1.537 -    // throw error string:
   1.538 -    lua_getfield(L, 1, "message");  // 4
   1.539 -    if (lua_isnil(L, 4)) {
   1.540 -      return luaL_error(L, "No error message given for escalation.");
   1.541 -    }
   1.542 -    return lua_error(L);
   1.543 +    // return everything but nil error object:
   1.544 +    return lua_gettop(L) - 1;
   1.545    }
   1.546  }
   1.547  
   1.548 -// method "is_kind_of" of error objects:
   1.549 -static int mondelefant_errorobject_is_kind_of(lua_State *L) {
   1.550 -  lua_settop(L, 2);
   1.551 -  lua_getfield(L, 1, "code");  // 3
   1.552 -  if (lua_isstring(L, 3)) {
   1.553 -    lua_pushboolean(L,
   1.554 -      mondelefant_check_error_class(
   1.555 -        lua_tostring(L, 3), luaL_checkstring(L, 2)
   1.556 -      )
   1.557 -    );
   1.558 -  } else {
   1.559 -    // only happens for errors where code is not set
   1.560 -    lua_pushboolean(L, 0);
   1.561 -  }
   1.562 -  return 1;
   1.563 -}
   1.564 -
   1.565  // method "query" of database handles:
   1.566  static int mondelefant_conn_query(lua_State *L) {
   1.567    int argc;
   1.568 @@ -1361,11 +1381,9 @@
   1.569    lua_call(L, argc, LUA_MULTRET);  // results (with error) starting at index 1
   1.570    // check, if error occurred:
   1.571    if (lua_toboolean(L, 1)) {
   1.572 -    // invoke escalate method of error object:
   1.573 -    lua_pushcfunction(L, mondelefant_errorobject_escalate);
   1.574 -    lua_pushvalue(L, 1);
   1.575 -    lua_call(L, 1, 0);  // will raise an error
   1.576 -    return 0;  // should not be executed
   1.577 +    // raise error
   1.578 +    lua_settop(L, 1);
   1.579 +    return lua_error(L);
   1.580    } else {
   1.581      // return everything but nil error object:
   1.582      return lua_gettop(L) - 1;
   1.583 @@ -1420,11 +1438,13 @@
   1.584  
   1.585  // library function "new_class":
   1.586  static int mondelefant_new_class(lua_State *L) {
   1.587 -  // use first argument as template or create new table:
   1.588 -  lua_settop(L, 1);
   1.589 -  if (!lua_toboolean(L, 1)) {
   1.590 +  // if no argument is given, use an empty table:
   1.591 +  if (lua_isnoneornil(L, 1)) {
   1.592      lua_settop(L, 0);
   1.593      lua_newtable(L);  // 1
   1.594 +  } else {
   1.595 +    luaL_checktype(L, 1, LUA_TTABLE);
   1.596 +    lua_settop(L, 1);
   1.597    }
   1.598    // set meta-table for database classes (models):
   1.599    lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_CLASS_MT_REGKEY);  // 2
   1.600 @@ -1843,6 +1863,7 @@
   1.601    {"close", mondelefant_conn_close},
   1.602    {"is_ok", mondelefant_conn_is_ok},
   1.603    {"get_transaction_status", mondelefant_conn_get_transaction_status},
   1.604 +  {"try_wait", mondelefant_conn_try_wait},
   1.605    {"wait", mondelefant_conn_wait},
   1.606    {"create_list", mondelefant_conn_create_list},
   1.607    {"create_object", mondelefant_conn_create_object},
   1.608 @@ -1861,7 +1882,7 @@
   1.609  
   1.610  // registration information for methods of error objects:
   1.611  static const struct luaL_Reg mondelefant_errorobject_methods[] = {
   1.612 -  {"escalate", mondelefant_errorobject_escalate},
   1.613 +  {"escalate", lua_error},
   1.614    {"is_kind_of", mondelefant_errorobject_is_kind_of},
   1.615    {NULL, NULL}
   1.616  };

Impressum / About Us