webmcp
changeset 406:36f0d1d5ed7d
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)
author | jbe |
---|---|
date | Wed Jan 06 18:59:58 2016 +0100 (2016-01-06) |
parents | c5f9a1b2f225 |
children | 0e641ac76647 |
files | libraries/mondelefant/mondelefant_native.c |
line diff
1.1 --- a/libraries/mondelefant/mondelefant_native.c Wed Jan 06 02:54:45 2016 +0100 1.2 +++ b/libraries/mondelefant/mondelefant_native.c Wed Jan 06 18:59:58 2016 +0100 1.3 @@ -195,20 +195,16 @@ 1.4 // and returns a database connection handle: 1.5 static int mondelefant_connect(lua_State *L) { 1.6 const char *conninfo; // string for PQconnectdb function 1.7 - PGconn *pgconn; // PGconn object as returned by PQconnectdb function 1.8 mondelefant_conn_t *conn; // C-structure for userdata 1.9 // check if string is given as first argument: 1.10 if (lua_type(L, 1) != LUA_TSTRING) { 1.11 // expect a table as first argument if no string is given: 1.12 luaL_checktype(L, 1, LUA_TTABLE); 1.13 - // copy conninfo string for PQconnectdb function from argument table to 1.14 - // stack position 2: 1.15 - lua_settop(L, 1); 1.16 - lua_getfield(L, 1, "conninfo"); // 2 1.17 - // check if conninfo is set: 1.18 - if (!lua_isnil(L, 2)) { 1.19 + // extract conninfo string for PQconnectdb if possible: 1.20 + lua_getfield(L, 1, "conninfo"); 1.21 + if (!lua_isnil(L, -1)) { 1.22 // if yes, use that value but check its type: 1.23 - luaL_argcheck(L, lua_type(L, 2) == LUA_TSTRING, 2, "\"conninfo\" value is not a string"); 1.24 + luaL_argcheck(L, lua_type(L, -1) == LUA_TSTRING, 1, "\"conninfo\" value is not a string"); 1.25 } else { 1.26 // otherwise assemble conninfo string from the named options: 1.27 luaL_Buffer buf; 1.28 @@ -247,61 +243,59 @@ 1.29 } 1.30 // ensure that string is on stack position 1 which is the top of stack: 1.31 lua_replace(L, 1); 1.32 - lua_settop(L, 1); 1.33 } 1.34 // use conninfo string on stack position 1: 1.35 conninfo = lua_tostring(L, 1); 1.36 + // create userdata on stack position 2: 1.37 + lua_settop(L, 1); 1.38 + conn = lua_newuserdata(L, sizeof(*conn)); // 2 1.39 // call PQconnectdb function of libpq: 1.40 - pgconn = PQconnectdb(conninfo); 1.41 - // throw or return errors, if neccessary: 1.42 - if (!pgconn) { 1.43 - return luaL_error(L, 1.44 - "Error in libpq while creating 'PGconn' structure." 1.45 - ); 1.46 + conn->pgconn = PQconnectdb(conninfo); 1.47 + // try emergency garbage collection on first failure: 1.48 + if (!conn->pgconn) { 1.49 + lua_gc(L, LUA_GCCOLLECT, 0); 1.50 + conn->pgconn = PQconnectdb(conninfo); 1.51 } 1.52 - if (PQstatus(pgconn) != CONNECTION_OK) { 1.53 - lua_pushnil(L); // 2 1.54 - mondelefant_push_first_line(L, PQerrorMessage(pgconn)); // 3 1.55 - lua_newtable(L); // 4 1.56 - lua_getfield(L, 1.57 - LUA_REGISTRYINDEX, 1.58 - MONDELEFANT_ERROROBJECT_MT_REGKEY 1.59 - ); 1.60 - lua_setmetatable(L, 4); 1.61 + // throw error in case of (unexpected) error of PQconnectdb call: 1.62 + if (!conn->pgconn) return luaL_error(L, 1.63 + "Error in libpq while creating 'PGconn' structure." 1.64 + ); 1.65 + // set metatable for userdata (ensure PQfinish on unexpected error below): 1.66 + luaL_setmetatable(L, MONDELEFANT_CONN_MT_REGKEY); 1.67 + // check result of PQconnectdb call: 1.68 + if (PQstatus(conn->pgconn) != CONNECTION_OK) { 1.69 + lua_pushnil(L); // 3 1.70 + mondelefant_push_first_line(L, PQerrorMessage(conn->pgconn)); // 4 1.71 + lua_newtable(L); // 5 1.72 + luaL_setmetatable(L, MONDELEFANT_ERROROBJECT_MT_REGKEY); 1.73 lua_pushliteral(L, MONDELEFANT_ERRCODE_CONNECTION); 1.74 - lua_setfield(L, 4, "code"); 1.75 - lua_pushvalue(L, 3); 1.76 - lua_setfield(L, 4, "message"); 1.77 - PQfinish(pgconn); 1.78 + lua_setfield(L, 5, "code"); 1.79 + lua_pushvalue(L, 4); 1.80 + lua_setfield(L, 5, "message"); 1.81 + // manual PQfinish (do not wait until garbage collection): 1.82 + PQfinish(conn->pgconn); 1.83 + conn->pgconn = NULL; 1.84 return 3; 1.85 } 1.86 - // create userdata: 1.87 - lua_settop(L, 0); 1.88 - conn = lua_newuserdata(L, sizeof(*conn)); // 1 1.89 - // set 'pgconn' in C-struct of userdata: 1.90 - conn->pgconn = pgconn; 1.91 // set 'server_encoding' in C-struct of userdata: 1.92 { 1.93 const char *charset; 1.94 - charset = PQparameterStatus(pgconn, "server_encoding"); 1.95 + charset = PQparameterStatus(conn->pgconn, "server_encoding"); 1.96 if (charset && !strcmp(charset, "UTF8")) { 1.97 conn->server_encoding = MONDELEFANT_SERVER_ENCODING_UTF8; 1.98 } else { 1.99 conn->server_encoding = MONDELEFANT_SERVER_ENCODING_ASCII; 1.100 } 1.101 } 1.102 - // set meta-table of userdata: 1.103 - luaL_getmetatable(L, MONDELEFANT_CONN_MT_REGKEY); // 2 1.104 - lua_setmetatable(L, 1); 1.105 // create and associate userdata table: 1.106 lua_newtable(L); 1.107 - lua_setuservalue(L, 1); 1.108 + lua_setuservalue(L, 2); 1.109 // store key "fd" with file descriptor of connection: 1.110 - lua_pushinteger(L, PQsocket(pgconn)); 1.111 - lua_setfield(L, 1, "fd"); 1.112 + lua_pushinteger(L, PQsocket(conn->pgconn)); 1.113 + lua_setfield(L, 2, "fd"); 1.114 // store key "engine" with value "postgresql" as connection specific data: 1.115 lua_pushliteral(L, "postgresql"); 1.116 - lua_setfield(L, 1, "engine"); 1.117 + lua_setfield(L, 2, "engine"); 1.118 // return userdata: 1.119 return 1; 1.120 } 1.121 @@ -458,11 +452,7 @@ 1.122 PGnotify *notify; 1.123 if (!PQconsumeInput(conn->pgconn)) { 1.124 lua_newtable(L); // 2 1.125 - lua_getfield(L, 1.126 - LUA_REGISTRYINDEX, 1.127 - MONDELEFANT_ERROROBJECT_MT_REGKEY 1.128 - ); 1.129 - lua_setmetatable(L, 2); 1.130 + luaL_setmetatable(L, MONDELEFANT_ERROROBJECT_MT_REGKEY); 1.131 lua_pushliteral(L, MONDELEFANT_ERRCODE_CONNECTION); 1.132 lua_setfield(L, 2, "code"); 1.133 mondelefant_push_first_line(L, PQerrorMessage(conn->pgconn)); // 3 1.134 @@ -522,8 +512,7 @@ 1.135 lua_settop(L, 2); 1.136 } 1.137 // set meta-table for database result lists/objects: 1.138 - lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_RESULT_MT_REGKEY); // 3 1.139 - lua_setmetatable(L, 2); 1.140 + luaL_setmetatable(L, MONDELEFANT_RESULT_MT_REGKEY); 1.141 // set "_connection" attribute to self: 1.142 lua_pushvalue(L, 1); // 3 1.143 lua_setfield(L, 2, "_connection"); 1.144 @@ -547,8 +536,7 @@ 1.145 lua_settop(L, 2); 1.146 } 1.147 // set meta-table for database result lists/objects: 1.148 - lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_RESULT_MT_REGKEY); // 3 1.149 - lua_setmetatable(L, 2); 1.150 + luaL_setmetatable(L, MONDELEFANT_RESULT_MT_REGKEY); 1.151 // set "_connection" attribute to self: 1.152 lua_pushvalue(L, 1); // 3 1.153 lua_setfield(L, 2, "_connection"); 1.154 @@ -944,11 +932,7 @@ 1.155 const char *command; 1.156 command = lua_tostring(L, 2); 1.157 lua_newtable(L); // 5 1.158 - lua_getfield(L, 1.159 - LUA_REGISTRYINDEX, 1.160 - MONDELEFANT_ERROROBJECT_MT_REGKEY 1.161 - ); 1.162 - lua_setmetatable(L, 5); 1.163 + luaL_setmetatable(L, MONDELEFANT_ERROROBJECT_MT_REGKEY); 1.164 lua_pushvalue(L, 1); 1.165 lua_setfield(L, 5, "connection"); 1.166 lua_pushvalue(L, 2); 1.167 @@ -1406,8 +1390,7 @@ 1.168 lua_settop(L, 1); 1.169 } 1.170 // set meta-table for database classes (models): 1.171 - lua_getfield(L, LUA_REGISTRYINDEX, MONDELEFANT_CLASS_MT_REGKEY); // 2 1.172 - lua_setmetatable(L, 1); 1.173 + luaL_setmetatable(L, MONDELEFANT_CLASS_MT_REGKEY); 1.174 // check, if "prototype" attribute is not set: 1.175 lua_pushliteral(L, "prototype"); // 2 1.176 lua_rawget(L, 1); // 2