# HG changeset patch # User jbe # Date 1452126188 -3600 # Node ID fd5880964d9956b114320dd9e42d1dc747d4bfee # Parent 0e641ac7664754a8d99e436d72945376933ce1f2 Hack to avoid cumulating memory leaks in case of (caught) out-of-memory errors diff -r 0e641ac76647 -r fd5880964d99 libraries/mondelefant/mondelefant_native.c --- a/libraries/mondelefant/mondelefant_native.c Wed Jan 06 22:59:39 2016 +0100 +++ b/libraries/mondelefant/mondelefant_native.c Thu Jan 07 01:23:08 2016 +0100 @@ -28,10 +28,24 @@ typedef struct { PGconn *pgconn; int server_encoding; + void *todo_PQfreemem; + PGresult *todo_PQclear; } mondelefant_conn_t; #define MONDELEFANT_SERVER_ENCODING_ASCII 0 #define MONDELEFANT_SERVER_ENCODING_UTF8 1 +// hack to avoid cumulating memory leaks in case of out-of-memory errors +static void mondelefant_cleanup(mondelefant_conn_t *conn) { + if (conn->todo_PQfreemem) { + PQfreemem(conn->todo_PQfreemem); + conn->todo_PQfreemem = NULL; + } + if (conn->todo_PQclear) { + PQclear(conn->todo_PQclear); + conn->todo_PQclear = NULL; + } +} + // transform codepoint-position to byte-position for a given UTF-8 string: static size_t utf8_position_to_byte(const char *str, size_t utf8pos) { size_t bytepos; @@ -598,14 +612,23 @@ conn = mondelefant_get_conn(L, 1); // get second argument, which must be a string: input = luaL_checklstring(L, 2, &input_len); + // avoid cumulating memory leaks in case of previous out-of-memory errors: + mondelefant_cleanup(conn); // call PQescapeByteaConn, which allocates memory itself: output = (char *)PQescapeByteaConn( conn->pgconn, (const unsigned char *)input, input_len, &output_len ); - // if PQescapeByteaConn returned NULL, then throw error: if (!output) { - return luaL_error(L, "Could not allocate memory for binary quoting."); + lua_gc(L, LUA_GCCOLLECT, 0); + output = (char *)PQescapeByteaConn( + conn->pgconn, (const unsigned char *)input, input_len, &output_len + ); + if (!output) { + return luaL_error(L, "Could not allocate memory for binary quoting."); + } } + // ensure call of PQfreemem in case of unexpected out-of-memory error: + conn->todo_PQfreemem = output; // create Lua string enclosed by single quotes: luaL_buffinit(L, &buf); luaL_addchar(&buf, '\''); @@ -614,6 +637,8 @@ luaL_pushresult(&buf); // free memory allocated by PQescapeByteaConn: PQfreemem(output); + // avoid double call of PQfreemem later: + conn->todo_PQfreemem = NULL; // return Lua string: return 1; } @@ -783,6 +808,7 @@ lua_remove(L, -2); // Lua stack contains: ..., , raw-value // branch according to type of value: + // NOTE: Lua automatically converts numbers to strings if (lua_isnil(L, -1)) { // value is nil // push string "NULL" to stack: lua_pushliteral(L, "NULL"); @@ -790,9 +816,8 @@ // push strings "TRUE" or "FALSE" to stack: lua_pushstring(L, lua_toboolean(L, -1) ? "TRUE" : "FALSE"); } else if (lua_isstring(L, -1)) { // value is string or number - // NOTE: In this version of lua a number will be converted - // push output of "quote_string" method of database - // connection to stack: + // push output of "quote_string" method of database connection + // to stack: lua_tostring(L, -1); lua_pushcfunction(L, mondelefant_conn_quote_string); lua_pushvalue(L, 1); @@ -908,6 +933,8 @@ mode = modes[command_idx]; // if PQsendQuery call was successful, then fetch result data: if (sent_success) { + // avoid cumulating memory leaks in case of previous out-of-memory errors: + mondelefant_cleanup(conn); // NOTE: PQgetResult called one extra time. Break only, if all // queries have been processed and PQgetResult returned NULL. res = PQgetResult(conn->pgconn); @@ -916,6 +943,8 @@ pgstatus = PQresultStatus(res); rows = PQntuples(res); cols = PQnfields(res); + // ensure eventual call of PQclear in case of unexpected Lua errors: + conn->todo_PQclear = res; } } // handle errors: @@ -1041,6 +1070,8 @@ if (res) { PQclear(res); while ((res = PQgetResult(conn->pgconn))) PQclear(res); + // avoid double call of PQclear later: + conn->todo_PQclear = NULL; } } if (lua_toboolean(L, 3)) { @@ -1185,6 +1216,8 @@ if (lua_gettop(L) != 4) abort(); // should not happen // free memory acquired by libpq: PQclear(res); + // avoid double call of PQclear later: + conn->todo_PQclear = NULL; } // trace callback at stack position 3 // result at stack position 4 (top of stack)