# HG changeset patch # User jbe # Date 1449764631 -3600 # Node ID 46ba2168693af13d385ff063286e0a82bd204b75 # Parent 762ab1e877028348f9150e57fe5230f3139a6b0f Improved error handling in mondelefant_native.c; Fixed bug in error handling when PQsendQuery returned 0 diff -r 762ab1e87702 -r 46ba2168693a libraries/mondelefant/mondelefant_native.autodoc.lua --- a/libraries/mondelefant/mondelefant_native.autodoc.lua Wed Dec 09 21:16:24 2015 +0100 +++ b/libraries/mondelefant/mondelefant_native.autodoc.lua Thu Dec 10 17:23:51 2015 +0100 @@ -2,7 +2,7 @@ --[[-- db_handle, -- database handle, or nil in case of error errmsg, -- error message -errcode = -- error code +db_error -- error object mondelefant.connect{ engine = "postgresql", -- no other engine is supported conninfo = conninfo, -- string passed directly to PostgreSQL's libpq @@ -77,21 +77,19 @@ --[[-- -channel, -- notification channel name -payload, -- notification payload string -pid = -- process ID of notifying server process -:wait( - timeout -- number of seconds to wait, 0 = do not block, nil = wait infinitely +db_error, -- error object, or nil in case of success or timeout +channel, -- notification channel name, or nil in case of timeout or no pending notify +payload, -- notification payload string +pid = -- process ID of notifying server process +:try_wait( + timeout -- number of seconds to wait, 0 = do not block, nil = wait infinitely ) Waits for any NOTIFY event that is being LISTENed for. One or more LISTEN commands must have been sent previously with :query("LISTEN channel_name"). -Returns nil as first return value and an error message as second return value in case of error. -Returns false as first return value and a message as second return value in case of timeout. - --]]-- -- implemented in mondelefant_native.c as --- static int mondelefant_conn_wait(lua_State *L) +-- static int mondelefant_conn_try_wait(lua_State *L) --//-- @@ -196,7 +194,9 @@ --[[-- :escalate() -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. +Deprecated alias for error(). + +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. --]]-- -- implemented in mondelefant_native.c as @@ -222,6 +222,43 @@ --[[-- +.code -- hierarchical error code (separated by dots) in camel case + +An error code in camel case notation with dots to separate hierarchy levels, e.g. "IntegrityConstraintViolation.UniqueViolation". See also :is_kind_of(...). + +--]]-- +-- implemented in mondelefant_native.c as +-- static const char *mondelefant_translate_errcode(const char *pgcode) +--//-- + + +--[[-- +.message -- string which summarizes the error + +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 object. Refer to the source code of the mondelefant_translate_errcode C function in mondelefant_native.c. + +--]]-- +-- implemented in mondelefant_native.c +--//-- + + +--[[-- +channel, -- notification channel name, or nil in case of timeout or no pending notify +payload, -- notification payload string +pid = -- process ID of notifying server process +:wait( + timeout -- number of seconds to wait, 0 = do not block, nil = wait infinitely +) + +Same as "try_wait" but raises an error, if a connection error occurred. Timeouts are reported by returning nil as first argument. + +--]]-- +-- implemented in mondelefant_native.c as +-- static int mondelefant_conn_wait(lua_State *L) +--//-- + + +--[[-- result1, -- result of first command result2, -- result of second command ... = diff -r 762ab1e87702 -r 46ba2168693a libraries/mondelefant/mondelefant_native.c --- a/libraries/mondelefant/mondelefant_native.c Wed Dec 09 21:16:24 2015 +0100 +++ b/libraries/mondelefant/mondelefant_native.c Thu Dec 10 17:23:51 2015 +0100 @@ -179,17 +179,16 @@ // pushing first line of a string on Lua's stack (without trailing CR/LF): static void mondelefant_push_first_line(lua_State *L, const char *str) { - char *str2; size_t i = 0; if (!str) abort(); // should not happen - str2 = strdup(str); while (1) { - char c = str2[i]; - if (c == '\n' || c == '\r' || c == 0) { str2[i] = 0; break; } + char c = str[i]; + if (c == '\n' || c == '\r' || c == 0) { + lua_pushlstring(L, str, i); + return; + } i++; - }; - lua_pushstring(L, str2); - free(str2); + } } // "connect" function of library, which establishes a database connection @@ -199,16 +198,18 @@ const char *conninfo; // string for PQconnectdb function PGconn *pgconn; // PGconn object as returned by PQconnectdb function mondelefant_conn_t *conn; // C-structure for userdata + // expect a table as first argument: + luaL_checktype(L, 1, LUA_TTABLE); // if engine is anything but "postgresql", then raise error: lua_settop(L, 1); lua_getfield(L, 1, "engine"); // 2 if (!lua_toboolean(L, 2)) { - return luaL_error(L, "No database engine selected."); + return luaL_argerror(L, 1, "no database engine selected"); } lua_pushliteral(L, "postgresql"); // 3 if (!lua_rawequal(L, 2, 3)) { - return luaL_error(L, - "Only database engine 'postgresql' is supported." + return luaL_argerror(L, 1, + "only database engine 'postgresql' is supported" ); } // copy conninfo string for PQconnectdb function from argument table to @@ -230,7 +231,7 @@ // NOTE: numbers will be converted to strings automatically here, // but perhaps this will change in future versions of lua luaL_argcheck(L, - lua_isstring(L, 2) && lua_isstring(L, 3), 1, "non-string contained" + lua_isstring(L, 2) && lua_isstring(L, 3), 1, "key is not a string" ); lua_pushvalue(L, 2); lua_pushliteral(L, "engine"); @@ -265,24 +266,25 @@ conninfo = lua_tostring(L, 2); // call PQconnectdb function of libpq: pgconn = PQconnectdb(conninfo); - // throw errors, if neccessary: + // throw or return errors, if neccessary: if (!pgconn) { return luaL_error(L, "Error in libpq while creating 'PGconn' structure." ); } if (PQstatus(pgconn) != CONNECTION_OK) { - const char *errmsg; - lua_pushnil(L); - errmsg = PQerrorMessage(pgconn); - if (errmsg) { - mondelefant_push_first_line(L, errmsg); - } else { - lua_pushliteral(L, - "Error while connecting to database, but no error message given." - ); - } + lua_pushnil(L); // 3 + mondelefant_push_first_line(L, PQerrorMessage(pgconn)); // 4 + lua_newtable(L); // 5 + lua_getfield(L, + LUA_REGISTRYINDEX, + MONDELEFANT_ERROROBJECT_MT_REGKEY + ); + lua_setmetatable(L, 5); lua_pushliteral(L, MONDELEFANT_ERRCODE_CONNECTION); + lua_setfield(L, 5, "code"); + lua_pushvalue(L, 4); + lua_setfield(L, 5, "message"); PQfinish(pgconn); return 3; } @@ -390,11 +392,10 @@ // method "close" of database handles: static int mondelefant_conn_close(lua_State *L) { mondelefant_conn_t *conn; - lua_settop(L, 1); conn = mondelefant_get_conn(L, 1); PQfinish(conn->pgconn); conn->pgconn = NULL; - lua_pushnil(L); // 2 + lua_pushnil(L); lua_setfield(L, 1, "fd"); // set "fd" attribute to nil return 0; } @@ -402,7 +403,6 @@ // method "is_okay" of database handles: static int mondelefant_conn_is_ok(lua_State *L) { mondelefant_conn_t *conn; - lua_settop(L, 1); conn = mondelefant_get_conn(L, 1); lua_pushboolean(L, PQstatus(conn->pgconn) == CONNECTION_OK); return 1; @@ -411,7 +411,6 @@ // method "get_transaction_status" of database handles: static int mondelefant_conn_get_transaction_status(lua_State *L) { mondelefant_conn_t *conn; - lua_settop(L, 1); conn = mondelefant_get_conn(L, 1); switch (PQtransactionStatus(conn->pgconn)) { case PQTRANS_IDLE: @@ -432,8 +431,8 @@ return 1; } -// method "wait" of database handles: -static int mondelefant_conn_wait(lua_State *L) { +// method "try_wait" of database handles: +static int mondelefant_conn_try_wait(lua_State *L) { mondelefant_conn_t *conn; int infinite, nonblock = 0; struct timespec wakeup; @@ -461,6 +460,7 @@ luaL_argcheck(L, 0, 2, "not a valid timeout"); } } + lua_settop(L, 1); if (!nonblock) { fd = PQsocket(conn->pgconn); FD_ZERO(&fds); @@ -470,17 +470,26 @@ { PGnotify *notify; if (!PQconsumeInput(conn->pgconn)) { - lua_pushnil(L); - mondelefant_push_first_line(L, PQerrorMessage(conn->pgconn)); - return 2; + lua_newtable(L); // 2 + lua_getfield(L, + LUA_REGISTRYINDEX, + MONDELEFANT_ERROROBJECT_MT_REGKEY + ); + lua_setmetatable(L, 2); + lua_pushliteral(L, MONDELEFANT_ERRCODE_CONNECTION); + lua_setfield(L, 2, "code"); + mondelefant_push_first_line(L, PQerrorMessage(conn->pgconn)); // 3 + lua_setfield(L, 2, "message"); + return 1; } notify = PQnotifies(conn->pgconn); if (notify) { + lua_pushnil(L); lua_pushstring(L, notify->relname); lua_pushstring(L, notify->extra); lua_pushinteger(L, notify->be_pid); PQfreemem(notify); - return 3; + return 4; } } if (infinite) { @@ -508,21 +517,22 @@ select(fd+1, &fds, NULL, NULL, &timeout); } } - lua_pushboolean(L, 0); - if (nonblock) lua_pushliteral(L, "No notification pending"); - else lua_pushliteral(L, "Timeout while waiting for notification"); + lua_pushnil(L); + lua_pushnil(L); return 2; } // method "create_list" of database handles: static int mondelefant_conn_create_list(lua_State *L) { // ensure that first argument is a database connection: - lua_settop(L, 2); luaL_checkudata(L, 1, MONDELEFANT_CONN_MT_REGKEY); // if no second argument is given, use an empty table: - if (!lua_toboolean(L, 2)) { - lua_newtable(L); - lua_replace(L, 2); // new result at stack position 2 + if (lua_isnoneornil(L, 2)) { + lua_settop(L, 1); + lua_newtable(L); // 2 + } else { + luaL_checktype(L, 2, LUA_TTABLE); + lua_settop(L, 2); } // set meta-table for database result lists/objects: lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_RESULT_MT_REGKEY); // 3 @@ -540,14 +550,16 @@ // method "create_object" of database handles: static int mondelefant_conn_create_object(lua_State *L) { // ensure that first argument is a database connection: - lua_settop(L, 2); luaL_checkudata(L, 1, MONDELEFANT_CONN_MT_REGKEY); // if no second argument is given, use an empty table: - if (!lua_toboolean(L, 2)) { - lua_newtable(L); - lua_replace(L, 2); // new result at stack position 2 + if (lua_isnoneornil(L, 2)) { + lua_settop(L, 1); + lua_newtable(L); // 2 + } else { + luaL_checktype(L, 2, LUA_TTABLE); + lua_settop(L, 2); } - // set meta-table for database result lists/objects: + // set meta-table for database result lists/objects: lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_RESULT_MT_REGKEY); // 3 lua_setmetatable(L, 2); // set "_connection" attribute to self: @@ -575,7 +587,6 @@ char *output; size_t output_len; // get 'conn' attribute of C-struct of database connection: - lua_settop(L, 2); conn = mondelefant_get_conn(L, 1); // get second argument, which must be a string: input = luaL_checklstring(L, 2, &input_len); @@ -613,7 +624,6 @@ size_t output_len; luaL_Buffer buf; // get 'conn' attribute of C-struct of database connection: - lua_settop(L, 2); conn = mondelefant_get_conn(L, 1); // get second argument, which must be a string: input = luaL_checklstring(L, 2, &input_len); @@ -645,11 +655,10 @@ size_t template_pos = 0; luaL_Buffer buf; // get 'conn' attribute of C-struct of database connection: - lua_settop(L, 2); conn = mondelefant_get_conn(L, 1); // if second argument is a string, return this string: - if (lua_isstring(L, 2)) { - lua_tostring(L, 2); + if (lua_type(L, 2) == LUA_TSTRING) { + lua_settop(L, 2); return 1; } // if second argument has __tostring meta-method, @@ -657,6 +666,8 @@ if (luaL_callmeta(L, 2, "__tostring")) return 1; // otherwise, require that second argument is a table: luaL_checktype(L, 2, LUA_TTABLE); + // set stack top: + lua_settop(L, 2); // get first element of table, which must be a string: lua_rawgeti(L, 2, 1); // 3 luaL_argcheck(L, @@ -904,7 +915,7 @@ } else if (!strcmp(modestr, "opt_object")) { mode = MONDELEFANT_QUERY_MODE_OPT_OBJECT; } else { - return luaL_error(L, "Unknown query mode specified."); + return luaL_argerror(L, mode_idx, "unknown query mode"); } } modes[command_idx] = mode; @@ -961,110 +972,117 @@ lua_setmetatable(L, 5); lua_pushvalue(L, 1); lua_setfield(L, 5, "connection"); - lua_pushinteger(L, command_idx + 1); - lua_setfield(L, 5, "command_number"); lua_pushvalue(L, 2); lua_setfield(L, 5, "sql_command"); - if (!res) { - lua_pushliteral(L, MONDELEFANT_ERRCODE_RESULTCOUNT_LOW); - lua_setfield(L, 5, "code"); - lua_pushliteral(L, "Received too few database result sets."); - lua_setfield(L, 5, "message"); - } else if (command_idx >= command_count) { - lua_pushliteral(L, MONDELEFANT_ERRCODE_RESULTCOUNT_HIGH); + if (!sent_success) { + lua_pushliteral(L, MONDELEFANT_ERRCODE_CONNECTION); lua_setfield(L, 5, "code"); - lua_pushliteral(L, "Received too many database result sets."); - lua_setfield(L, 5, "message"); - } else if ( - pgstatus != PGRES_TUPLES_OK && pgstatus != PGRES_COMMAND_OK - ) { - const char *sqlstate; - const char *errmsg; - lua_pushstring(L, PQresultErrorField(res, PG_DIAG_SEVERITY)); - lua_setfield(L, 5, "pg_severity"); - sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE); - if (sqlstate) { - lua_pushstring(L, sqlstate); - lua_setfield(L, 5, "pg_sqlstate"); - lua_pushstring(L, mondelefant_translate_errcode(sqlstate)); - lua_setfield(L, 5, "code"); - } else { - lua_pushliteral(L, MONDELEFANT_ERRCODE_UNKNOWN); - lua_setfield(L, 5, "code"); - } - errmsg = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY); - if (errmsg) { - mondelefant_push_first_line(L, errmsg); - lua_setfield(L, 5, "message"); - lua_pushstring(L, errmsg); - lua_setfield(L, 5, "pg_message_primary"); - } else { - lua_pushliteral(L, - "Error while fetching result, but no error message given." - ); - lua_setfield(L, 5, "message"); - } - lua_pushstring(L, PQresultErrorField(res, PG_DIAG_MESSAGE_DETAIL)); - lua_setfield(L, 5, "pg_message_detail"); - lua_pushstring(L, PQresultErrorField(res, PG_DIAG_MESSAGE_HINT)); - lua_setfield(L, 5, "pg_message_hint"); - // NOTE: "position" and "pg_internal_position" are recalculated to - // byte offsets, as Lua 5.1 is not Unicode aware. - { - char *tmp; - tmp = PQresultErrorField(res, PG_DIAG_STATEMENT_POSITION); - if (tmp) { - int pos; - pos = atoi(tmp) - 1; - if (conn->server_encoding == MONDELEFANT_SERVER_ENCODING_UTF8) { - pos = utf8_position_to_byte(command, pos); - } - lua_pushinteger(L, pos + 1); - lua_setfield(L, 5, "position"); - } - } - { - const char *internal_query; - internal_query = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY); - lua_pushstring(L, internal_query); - lua_setfield(L, 5, "pg_internal_query"); - char *tmp; - tmp = PQresultErrorField(res, PG_DIAG_INTERNAL_POSITION); - if (tmp) { - int pos; - pos = atoi(tmp) - 1; - if (conn->server_encoding == MONDELEFANT_SERVER_ENCODING_UTF8) { - pos = utf8_position_to_byte(internal_query, pos); - } - lua_pushinteger(L, pos + 1); - lua_setfield(L, 5, "pg_internal_position"); - } - } - lua_pushstring(L, PQresultErrorField(res, PG_DIAG_CONTEXT)); - lua_setfield(L, 5, "pg_context"); - lua_pushstring(L, PQresultErrorField(res, PG_DIAG_SOURCE_FILE)); - lua_setfield(L, 5, "pg_source_file"); - lua_pushstring(L, PQresultErrorField(res, PG_DIAG_SOURCE_LINE)); - lua_setfield(L, 5, "pg_source_line"); - lua_pushstring(L, PQresultErrorField(res, PG_DIAG_SOURCE_FUNCTION)); - lua_setfield(L, 5, "pg_source_function"); - } else if (rows < 1 && mode == MONDELEFANT_QUERY_MODE_OBJECT) { - lua_pushliteral(L, MONDELEFANT_ERRCODE_QUERY1_NO_ROWS); - lua_setfield(L, 5, "code"); - lua_pushliteral(L, "Expected one row, but got empty set."); - lua_setfield(L, 5, "message"); - } else if (rows > 1 && mode != MONDELEFANT_QUERY_MODE_LIST) { - lua_pushliteral(L, MONDELEFANT_ERRCODE_QUERY1_MULTIPLE_ROWS); - lua_setfield(L, 5, "code"); - lua_pushliteral(L, "Got more than one result row."); + mondelefant_push_first_line(L, PQerrorMessage(conn->pgconn)); lua_setfield(L, 5, "message"); } else { - // should not happen - abort(); - } - if (res) { - PQclear(res); - while ((res = PQgetResult(conn->pgconn))) PQclear(res); + lua_pushinteger(L, command_idx + 1); + lua_setfield(L, 5, "command_number"); + if (!res) { + lua_pushliteral(L, MONDELEFANT_ERRCODE_RESULTCOUNT_LOW); + lua_setfield(L, 5, "code"); + lua_pushliteral(L, "Received too few database result sets."); + lua_setfield(L, 5, "message"); + } else if (command_idx >= command_count) { + lua_pushliteral(L, MONDELEFANT_ERRCODE_RESULTCOUNT_HIGH); + lua_setfield(L, 5, "code"); + lua_pushliteral(L, "Received too many database result sets."); + lua_setfield(L, 5, "message"); + } else if ( + pgstatus != PGRES_TUPLES_OK && pgstatus != PGRES_COMMAND_OK + ) { + const char *sqlstate; + const char *errmsg; + lua_pushstring(L, PQresultErrorField(res, PG_DIAG_SEVERITY)); + lua_setfield(L, 5, "pg_severity"); + sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE); + if (sqlstate) { + lua_pushstring(L, sqlstate); + lua_setfield(L, 5, "pg_sqlstate"); + lua_pushstring(L, mondelefant_translate_errcode(sqlstate)); + lua_setfield(L, 5, "code"); + } else { + lua_pushliteral(L, MONDELEFANT_ERRCODE_UNKNOWN); + lua_setfield(L, 5, "code"); + } + errmsg = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY); + if (errmsg) { + mondelefant_push_first_line(L, errmsg); + lua_setfield(L, 5, "message"); + lua_pushstring(L, errmsg); + lua_setfield(L, 5, "pg_message_primary"); + } else { + lua_pushliteral(L, + "Error while fetching result, but no error message given." + ); + lua_setfield(L, 5, "message"); + } + lua_pushstring(L, PQresultErrorField(res, PG_DIAG_MESSAGE_DETAIL)); + lua_setfield(L, 5, "pg_message_detail"); + lua_pushstring(L, PQresultErrorField(res, PG_DIAG_MESSAGE_HINT)); + lua_setfield(L, 5, "pg_message_hint"); + // NOTE: "position" and "pg_internal_position" are recalculated to + // byte offsets, as Lua 5.2 is not Unicode aware. + { + char *tmp; + tmp = PQresultErrorField(res, PG_DIAG_STATEMENT_POSITION); + if (tmp) { + int pos; + pos = atoi(tmp) - 1; + if (conn->server_encoding == MONDELEFANT_SERVER_ENCODING_UTF8) { + pos = utf8_position_to_byte(command, pos); + } + lua_pushinteger(L, pos + 1); + lua_setfield(L, 5, "position"); + } + } + { + const char *internal_query; + internal_query = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY); + lua_pushstring(L, internal_query); + lua_setfield(L, 5, "pg_internal_query"); + char *tmp; + tmp = PQresultErrorField(res, PG_DIAG_INTERNAL_POSITION); + if (tmp) { + int pos; + pos = atoi(tmp) - 1; + if (conn->server_encoding == MONDELEFANT_SERVER_ENCODING_UTF8) { + pos = utf8_position_to_byte(internal_query, pos); + } + lua_pushinteger(L, pos + 1); + lua_setfield(L, 5, "pg_internal_position"); + } + } + lua_pushstring(L, PQresultErrorField(res, PG_DIAG_CONTEXT)); + lua_setfield(L, 5, "pg_context"); + lua_pushstring(L, PQresultErrorField(res, PG_DIAG_SOURCE_FILE)); + lua_setfield(L, 5, "pg_source_file"); + lua_pushstring(L, PQresultErrorField(res, PG_DIAG_SOURCE_LINE)); + lua_setfield(L, 5, "pg_source_line"); + lua_pushstring(L, PQresultErrorField(res, PG_DIAG_SOURCE_FUNCTION)); + lua_setfield(L, 5, "pg_source_function"); + } else if (rows < 1 && mode == MONDELEFANT_QUERY_MODE_OBJECT) { + lua_pushliteral(L, MONDELEFANT_ERRCODE_QUERY1_NO_ROWS); + lua_setfield(L, 5, "code"); + lua_pushliteral(L, "Expected one row, but got empty set."); + lua_setfield(L, 5, "message"); + } else if (rows > 1 && mode != MONDELEFANT_QUERY_MODE_LIST) { + lua_pushliteral(L, MONDELEFANT_ERRCODE_QUERY1_MULTIPLE_ROWS); + lua_setfield(L, 5, "code"); + lua_pushliteral(L, "Got more than one result row."); + lua_setfield(L, 5, "message"); + } else { + // should not happen + abort(); + } + if (res) { + PQclear(res); + while ((res = PQgetResult(conn->pgconn))) PQclear(res); + } } if (lua_toboolean(L, 3)) { lua_pushvalue(L, 3); @@ -1312,43 +1330,45 @@ return command_count+1; } -// method "escalate" of error objects: -static int mondelefant_errorobject_escalate(lua_State *L) { - // check, if we may throw an error object instead of an error string: - lua_settop(L, 1); - lua_getfield(L, 1, "connection"); // 2 - lua_getfield(L, 2, "error_objects"); // 3 - if (lua_toboolean(L, 3)) { - // throw error object: +// method "is_kind_of" of error objects: +static int mondelefant_errorobject_is_kind_of(lua_State *L) { + const char *errclass; + luaL_checktype(L, 1, LUA_TTABLE); + errclass = luaL_checkstring(L, 2); + lua_settop(L, 2); + lua_getfield(L, 1, "code"); // 3 + luaL_argcheck(L, + lua_type(L, 3) == LUA_TSTRING, + 1, + "field 'code' of error object is not a string" + ); + lua_pushboolean(L, + mondelefant_check_error_class(lua_tostring(L, 3), errclass) + ); + return 1; +} + +// method "wait" of database handles: +static int mondelefant_conn_wait(lua_State *L) { + int argc; + // count number of arguments: + argc = lua_gettop(L); + // insert "try_wait" function/method at stack position 1: + lua_pushcfunction(L, mondelefant_conn_try_wait); + lua_insert(L, 1); + // call "try_wait" method: + lua_call(L, argc, LUA_MULTRET); // results (with error) starting at index 1 + // check, if error occurred: + if (lua_toboolean(L, 1)) { + // raise error lua_settop(L, 1); return lua_error(L); } else { - // throw error string: - lua_getfield(L, 1, "message"); // 4 - if (lua_isnil(L, 4)) { - return luaL_error(L, "No error message given for escalation."); - } - return lua_error(L); + // return everything but nil error object: + return lua_gettop(L) - 1; } } -// method "is_kind_of" of error objects: -static int mondelefant_errorobject_is_kind_of(lua_State *L) { - lua_settop(L, 2); - lua_getfield(L, 1, "code"); // 3 - if (lua_isstring(L, 3)) { - lua_pushboolean(L, - mondelefant_check_error_class( - lua_tostring(L, 3), luaL_checkstring(L, 2) - ) - ); - } else { - // only happens for errors where code is not set - lua_pushboolean(L, 0); - } - return 1; -} - // method "query" of database handles: static int mondelefant_conn_query(lua_State *L) { int argc; @@ -1361,11 +1381,9 @@ lua_call(L, argc, LUA_MULTRET); // results (with error) starting at index 1 // check, if error occurred: if (lua_toboolean(L, 1)) { - // invoke escalate method of error object: - lua_pushcfunction(L, mondelefant_errorobject_escalate); - lua_pushvalue(L, 1); - lua_call(L, 1, 0); // will raise an error - return 0; // should not be executed + // raise error + lua_settop(L, 1); + return lua_error(L); } else { // return everything but nil error object: return lua_gettop(L) - 1; @@ -1420,11 +1438,13 @@ // library function "new_class": static int mondelefant_new_class(lua_State *L) { - // use first argument as template or create new table: - lua_settop(L, 1); - if (!lua_toboolean(L, 1)) { + // if no argument is given, use an empty table: + if (lua_isnoneornil(L, 1)) { lua_settop(L, 0); lua_newtable(L); // 1 + } else { + luaL_checktype(L, 1, LUA_TTABLE); + lua_settop(L, 1); } // set meta-table for database classes (models): lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_CLASS_MT_REGKEY); // 2 @@ -1843,6 +1863,7 @@ {"close", mondelefant_conn_close}, {"is_ok", mondelefant_conn_is_ok}, {"get_transaction_status", mondelefant_conn_get_transaction_status}, + {"try_wait", mondelefant_conn_try_wait}, {"wait", mondelefant_conn_wait}, {"create_list", mondelefant_conn_create_list}, {"create_object", mondelefant_conn_create_object}, @@ -1861,7 +1882,7 @@ // registration information for methods of error objects: static const struct luaL_Reg mondelefant_errorobject_methods[] = { - {"escalate", mondelefant_errorobject_escalate}, + {"escalate", lua_error}, {"is_kind_of", mondelefant_errorobject_is_kind_of}, {NULL, NULL} };