# HG changeset patch # User jbe # Date 1452103198 -3600 # Node ID 36f0d1d5ed7dc68f113fb323491ba7506ad75d05 # Parent c5f9a1b2f2250621801a1a602099b40216224907 Further fixes in mondelefant.connect{...} (including proper handling of garbage collection in case of memory allocation errors); Code cleanup (use luaL_setmetatable, which is available since Lua 5.2) diff -r c5f9a1b2f225 -r 36f0d1d5ed7d libraries/mondelefant/mondelefant_native.c --- a/libraries/mondelefant/mondelefant_native.c Wed Jan 06 02:54:45 2016 +0100 +++ b/libraries/mondelefant/mondelefant_native.c Wed Jan 06 18:59:58 2016 +0100 @@ -195,20 +195,16 @@ // and returns a database connection handle: static int mondelefant_connect(lua_State *L) { const char *conninfo; // string for PQconnectdb function - PGconn *pgconn; // PGconn object as returned by PQconnectdb function mondelefant_conn_t *conn; // C-structure for userdata // check if string is given as first argument: if (lua_type(L, 1) != LUA_TSTRING) { // expect a table as first argument if no string is given: luaL_checktype(L, 1, LUA_TTABLE); - // copy conninfo string for PQconnectdb function from argument table to - // stack position 2: - lua_settop(L, 1); - lua_getfield(L, 1, "conninfo"); // 2 - // check if conninfo is set: - if (!lua_isnil(L, 2)) { + // extract conninfo string for PQconnectdb if possible: + lua_getfield(L, 1, "conninfo"); + if (!lua_isnil(L, -1)) { // if yes, use that value but check its type: - luaL_argcheck(L, lua_type(L, 2) == LUA_TSTRING, 2, "\"conninfo\" value is not a string"); + luaL_argcheck(L, lua_type(L, -1) == LUA_TSTRING, 1, "\"conninfo\" value is not a string"); } else { // otherwise assemble conninfo string from the named options: luaL_Buffer buf; @@ -247,61 +243,59 @@ } // ensure that string is on stack position 1 which is the top of stack: lua_replace(L, 1); - lua_settop(L, 1); } // use conninfo string on stack position 1: conninfo = lua_tostring(L, 1); + // create userdata on stack position 2: + lua_settop(L, 1); + conn = lua_newuserdata(L, sizeof(*conn)); // 2 // call PQconnectdb function of libpq: - pgconn = PQconnectdb(conninfo); - // throw or return errors, if neccessary: - if (!pgconn) { - return luaL_error(L, - "Error in libpq while creating 'PGconn' structure." - ); + conn->pgconn = PQconnectdb(conninfo); + // try emergency garbage collection on first failure: + if (!conn->pgconn) { + lua_gc(L, LUA_GCCOLLECT, 0); + conn->pgconn = PQconnectdb(conninfo); } - if (PQstatus(pgconn) != CONNECTION_OK) { - lua_pushnil(L); // 2 - mondelefant_push_first_line(L, PQerrorMessage(pgconn)); // 3 - lua_newtable(L); // 4 - lua_getfield(L, - LUA_REGISTRYINDEX, - MONDELEFANT_ERROROBJECT_MT_REGKEY - ); - lua_setmetatable(L, 4); + // throw error in case of (unexpected) error of PQconnectdb call: + if (!conn->pgconn) return luaL_error(L, + "Error in libpq while creating 'PGconn' structure." + ); + // set metatable for userdata (ensure PQfinish on unexpected error below): + luaL_setmetatable(L, MONDELEFANT_CONN_MT_REGKEY); + // check result of PQconnectdb call: + if (PQstatus(conn->pgconn) != CONNECTION_OK) { + lua_pushnil(L); // 3 + mondelefant_push_first_line(L, PQerrorMessage(conn->pgconn)); // 4 + lua_newtable(L); // 5 + luaL_setmetatable(L, MONDELEFANT_ERROROBJECT_MT_REGKEY); lua_pushliteral(L, MONDELEFANT_ERRCODE_CONNECTION); - lua_setfield(L, 4, "code"); - lua_pushvalue(L, 3); - lua_setfield(L, 4, "message"); - PQfinish(pgconn); + lua_setfield(L, 5, "code"); + lua_pushvalue(L, 4); + lua_setfield(L, 5, "message"); + // manual PQfinish (do not wait until garbage collection): + PQfinish(conn->pgconn); + conn->pgconn = NULL; return 3; } - // create userdata: - lua_settop(L, 0); - conn = lua_newuserdata(L, sizeof(*conn)); // 1 - // set 'pgconn' in C-struct of userdata: - conn->pgconn = pgconn; // set 'server_encoding' in C-struct of userdata: { const char *charset; - charset = PQparameterStatus(pgconn, "server_encoding"); + charset = PQparameterStatus(conn->pgconn, "server_encoding"); if (charset && !strcmp(charset, "UTF8")) { conn->server_encoding = MONDELEFANT_SERVER_ENCODING_UTF8; } else { conn->server_encoding = MONDELEFANT_SERVER_ENCODING_ASCII; } } - // set meta-table of userdata: - luaL_getmetatable(L, MONDELEFANT_CONN_MT_REGKEY); // 2 - lua_setmetatable(L, 1); // create and associate userdata table: lua_newtable(L); - lua_setuservalue(L, 1); + lua_setuservalue(L, 2); // store key "fd" with file descriptor of connection: - lua_pushinteger(L, PQsocket(pgconn)); - lua_setfield(L, 1, "fd"); + lua_pushinteger(L, PQsocket(conn->pgconn)); + lua_setfield(L, 2, "fd"); // store key "engine" with value "postgresql" as connection specific data: lua_pushliteral(L, "postgresql"); - lua_setfield(L, 1, "engine"); + lua_setfield(L, 2, "engine"); // return userdata: return 1; } @@ -458,11 +452,7 @@ PGnotify *notify; if (!PQconsumeInput(conn->pgconn)) { lua_newtable(L); // 2 - lua_getfield(L, - LUA_REGISTRYINDEX, - MONDELEFANT_ERROROBJECT_MT_REGKEY - ); - lua_setmetatable(L, 2); + luaL_setmetatable(L, MONDELEFANT_ERROROBJECT_MT_REGKEY); lua_pushliteral(L, MONDELEFANT_ERRCODE_CONNECTION); lua_setfield(L, 2, "code"); mondelefant_push_first_line(L, PQerrorMessage(conn->pgconn)); // 3 @@ -522,8 +512,7 @@ lua_settop(L, 2); } // set meta-table for database result lists/objects: - lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_RESULT_MT_REGKEY); // 3 - lua_setmetatable(L, 2); + luaL_setmetatable(L, MONDELEFANT_RESULT_MT_REGKEY); // set "_connection" attribute to self: lua_pushvalue(L, 1); // 3 lua_setfield(L, 2, "_connection"); @@ -547,8 +536,7 @@ lua_settop(L, 2); } // set meta-table for database result lists/objects: - lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_RESULT_MT_REGKEY); // 3 - lua_setmetatable(L, 2); + luaL_setmetatable(L, MONDELEFANT_RESULT_MT_REGKEY); // set "_connection" attribute to self: lua_pushvalue(L, 1); // 3 lua_setfield(L, 2, "_connection"); @@ -944,11 +932,7 @@ const char *command; command = lua_tostring(L, 2); lua_newtable(L); // 5 - lua_getfield(L, - LUA_REGISTRYINDEX, - MONDELEFANT_ERROROBJECT_MT_REGKEY - ); - lua_setmetatable(L, 5); + luaL_setmetatable(L, MONDELEFANT_ERROROBJECT_MT_REGKEY); lua_pushvalue(L, 1); lua_setfield(L, 5, "connection"); lua_pushvalue(L, 2); @@ -1406,8 +1390,7 @@ lua_settop(L, 1); } // set meta-table for database classes (models): - lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_CLASS_MT_REGKEY); // 2 - lua_setmetatable(L, 1); + luaL_setmetatable(L, MONDELEFANT_CLASS_MT_REGKEY); // check, if "prototype" attribute is not set: lua_pushliteral(L, "prototype"); // 2 lua_rawget(L, 1); // 2