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 };