From 9ac4dfb0f0ba5be3f21bd3904c99ba67543b1833 Mon Sep 17 00:00:00 2001 From: Richard Knight Date: Thu, 25 Aug 2022 18:14:53 +0100 Subject: fixed string and array memory leaks --- Plugin.cmake | 2 +- include/connection.h | 6 +++--- include/logindata.h | 4 ++-- include/mysql.h | 6 +++--- include/postgresql.h | 6 +++--- include/sqlite.h | 6 +++--- include/tools.h | 30 +++++++++++++++++++++++++++++ src/connection.cpp | 8 ++++---- src/mysql.cpp | 30 ++++++++++------------------- src/opcodes.cpp | 18 +++++++++++------- src/postgresql.cpp | 34 ++++++++++++--------------------- src/sqlite3.cpp | 41 ++++++++++++++++----------------------- src/tools.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 13 files changed, 152 insertions(+), 93 deletions(-) create mode 100644 include/tools.h create mode 100644 src/tools.cpp diff --git a/Plugin.cmake b/Plugin.cmake index cec008c..febebda 100644 --- a/Plugin.cmake +++ b/Plugin.cmake @@ -11,7 +11,7 @@ if(NOT PostgreSQL_FOUND AND NOT SQLITE3_FOUND AND NOT MYSQLCONNECTORCPP_FOUND) endif() # Source files -set(CPPFILES src/opcodes.cpp src/connection.cpp) +set(CPPFILES src/opcodes.cpp src/connection.cpp src/tools.cpp) set(INCLUDES ${CSOUND_INCLUDE_DIRS} "include") set(LIBS "") diff --git a/include/connection.h b/include/connection.h index e67ea27..cf3fb2e 100644 --- a/include/connection.h +++ b/include/connection.h @@ -19,8 +19,8 @@ */ -#ifndef CONNECTION_H -#define CONNECTION_H +#ifndef CSSQLDB_CONNECTION_H +#define CSSQLDB_CONNECTION_H #include #include "logindata.h" @@ -68,7 +68,7 @@ struct ConnectionData { void Close(csnd::Csound* csound); void Exec(char* sql); MYFLT Scalar(char* sql, int row, int col); - char* ScalarString(char* sql, int row=0, int col=0); + char* ScalarString(char* sql, csnd::Csound* csound, int row=0, int col=0); void ArrayQuery(char* sql, csnd::Csound* csound, ARRAYDAT* array); void ArrayQueryString(char* sql, csnd::Csound* csound, ARRAYDAT* array); }; diff --git a/include/logindata.h b/include/logindata.h index 8a62e6b..be8e79a 100644 --- a/include/logindata.h +++ b/include/logindata.h @@ -19,8 +19,8 @@ */ -#ifndef LOGINDATA_H -#define LOGINDATA_H +#ifndef CSSQLDB_LOGINDATA_H +#define CSSQLDB_LOGINDATA_H struct LoginData { int dbType; diff --git a/include/mysql.h b/include/mysql.h index 1abc6e3..950e9d3 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -19,8 +19,8 @@ */ -#ifndef MYSQL_H -#define MYSQL_H +#ifndef CSSQLDB_MYSQL_H +#define CSSQLDB_MYSQL_H #include #include @@ -37,7 +37,7 @@ struct MySQLConnection { void Exec(char* sql); mysql::ResultSet* Query(char *sql); MYFLT Scalar(char* sql, int row, int col); - char* ScalarString(char* sql, int row, int col); + char* ScalarString(char* sql, csnd::Csound* csound, int row, int col); void ToArray(mysql::ResultSet* result, csnd::Csound* csound, ARRAYDAT* array, bool asString); void ArrayQuery(char* sql, csnd::Csound* csound, ARRAYDAT* array); void ArrayQueryString(char* sql, csnd::Csound* csound, ARRAYDAT* array); diff --git a/include/postgresql.h b/include/postgresql.h index 5209057..eaf01c8 100644 --- a/include/postgresql.h +++ b/include/postgresql.h @@ -19,8 +19,8 @@ */ -#ifndef POSTGRESQL_H -#define POSTGRESQL_H +#ifndef CSSQLDB_POSTGRESQL_H +#define CSSQLDB_POSTGRESQL_H #include #include "libpq-fe.h" @@ -33,7 +33,7 @@ struct PostgresConnection { void Exec(char* sql); PGresult* Query(char *sql); MYFLT Scalar(char* sql, int row, int col); - char* ScalarString(char* sql, int row, int col); + char* ScalarString(char* sql, csnd::Csound* csound, int row, int col); void ToArray(PGresult* result, csnd::Csound* csound, ARRAYDAT* array, bool asString); void ArrayQuery(char* sql, csnd::Csound* csound, ARRAYDAT* array); void ArrayQueryString(char* sql, csnd::Csound* csound, ARRAYDAT* array); diff --git a/include/sqlite.h b/include/sqlite.h index 4e2b0c3..c42eb57 100644 --- a/include/sqlite.h +++ b/include/sqlite.h @@ -19,8 +19,8 @@ */ -#ifndef XSQLITE3_H -#define XSQLITE3_H +#ifndef CSSQLDB_SQLITE3_H +#define CSSQLDB_SQLITE3_H #include #include @@ -33,7 +33,7 @@ struct SqliteConnection { void Exec(char* sql); sqlite3_stmt* Query(char *sql); MYFLT Scalar(char* sql, int row, int col); - char* ScalarString(char* sql, int row, int col); + char* ScalarString(char* sql, csnd::Csound* csound, int row, int col); int RowCount(sqlite3_stmt* stmt); void ToArray(sqlite3_stmt* result, csnd::Csound* csound, ARRAYDAT* array, bool asString); void ArrayQuery(char* sql, csnd::Csound* csound, ARRAYDAT* array); diff --git a/include/tools.h b/include/tools.h new file mode 100644 index 0000000..54a0363 --- /dev/null +++ b/include/tools.h @@ -0,0 +1,30 @@ +/* + tools.h + Copyright (C) 2022 Richard Knight + + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + */ + +#ifndef CSSQLDB_TOOLS_H +#define CSSQLDB_TOOLS_H + +#include + +STRINGDAT* arrayInit(csnd::Csound* csound, ARRAYDAT* array, int rows, int cols); +void insertArrayStringItem(csnd::Csound* csound, STRINGDAT* strings, int index, char* item); + +#endif /* TOOLS_H */ diff --git a/src/connection.cpp b/src/connection.cpp index 6ad929f..43175bd 100644 --- a/src/connection.cpp +++ b/src/connection.cpp @@ -136,7 +136,7 @@ MYFLT ConnectionData::Scalar(char* sql, int row, int col) { } -char* ConnectionData::ScalarString(char* sql, int row, int col) { +char* ConnectionData::ScalarString(char* sql, csnd::Csound* csound, int row, int col) { if (!open) { throw std::runtime_error(badConnection); } @@ -144,17 +144,17 @@ char* ConnectionData::ScalarString(char* sql, int row, int col) { switch (type) { #ifdef BUILD_POSTGRES case POSTGRES: - return postgres->ScalarString(sql, row, col); + return postgres->ScalarString(sql, csound, row, col); break; #endif #ifdef BUILD_SQLITE case SQLITE: - return sqlite->ScalarString(sql, row, col); + return sqlite->ScalarString(sql, csound, row, col); break; #endif #ifdef BUILD_MYSQL case MYSQL: - return mysql->ScalarString(sql, row, col); + return mysql->ScalarString(sql, csound, row, col); break; #endif default: diff --git a/src/mysql.cpp b/src/mysql.cpp index 2362d60..381898b 100644 --- a/src/mysql.cpp +++ b/src/mysql.cpp @@ -20,6 +20,7 @@ */ #include +#include "tools.h" #include #include #include @@ -67,6 +68,7 @@ MYFLT MySQLConnection::Scalar(char* sql, int row, int col) { mysql::ResultSetMetaData* meta = res->getMetaData(); int colCount = meta->getColumnCount(); if (col > colCount - 1) { + delete res; throw std::runtime_error("column number out of range"); } @@ -78,12 +80,13 @@ MYFLT MySQLConnection::Scalar(char* sql, int row, int col) { return result; } -char* MySQLConnection::ScalarString(char* sql, int row, int col) { +char* MySQLConnection::ScalarString(char* sql, csnd::Csound* csound, int row, int col) { mysql::ResultSet* res = Query(sql); mysql::ResultSetMetaData* meta = res->getMetaData(); int colCount = meta->getColumnCount(); if (col > colCount - 1) { + delete res; throw std::runtime_error("column number out of range"); } @@ -91,7 +94,7 @@ char* MySQLConnection::ScalarString(char* sql, int row, int col) { for (int rowIndex = 0; rowIndex <= row; rowIndex++) { res->next(); } - char* result = (char*) res->getString(col + 1).c_str(); + char* result = csound->strdup((char*) res->getString(col + 1).c_str()); delete res; @@ -101,31 +104,18 @@ char* MySQLConnection::ScalarString(char* sql, int row, int col) { void MySQLConnection::ToArray(mysql::ResultSet* result, csnd::Csound* csound, ARRAYDAT* array, bool asString) { mysql::ResultSetMetaData* meta = result->getMetaData(); - int colNum = meta->getColumnCount(); - int rowNum = result->rowsCount(); - int totalResults = colNum * rowNum; - array->sizes = (int32_t*) csound->calloc(sizeof(int32_t) * 2); - array->sizes[0] = rowNum; - array->sizes[1] = colNum; - array->dimensions = 2; - CS_VARIABLE *var = array->arrayType->createVariable(csound, NULL); - array->arrayMemberSize = var->memBlockSize; - array->data = (MYFLT*) csound->calloc(var->memBlockSize * totalResults); - STRINGDAT* strings; - if (asString) { - strings = (STRINGDAT*) array->data; - } + int cols = meta->getColumnCount(); + int rows = result->rowsCount(); + STRINGDAT* strings = arrayInit(csound, array, rows, cols); int colIndex; int index = 0; while (result->next()) { colIndex = 0; - while (colIndex < colNum) { + while (colIndex < cols) { if (asString) { - char* item = (char*) result->getString(colIndex + 1).c_str(); - strings[index].size = strlen(item) + 1; - strings[index].data = csound->strdup(item); + insertArrayStringItem(csound, strings, index, (char*) result->getString(colIndex + 1).c_str()); } else { array->data[index] = (MYFLT) result->getDouble(colIndex + 1); } diff --git a/src/opcodes.cpp b/src/opcodes.cpp index 34bba02..e82ce33 100644 --- a/src/opcodes.cpp +++ b/src/opcodes.cpp @@ -95,7 +95,7 @@ public: try { switch (queryData->queryType) { case SCALARSTRING: { - std::string resultString = connection->ScalarString(queryData->sql, queryData->row, queryData->col); + std::string resultString = connection->ScalarString(queryData->sql, csound, queryData->row, queryData->col); if (charData != NULL) { csound->free(charData); } @@ -480,6 +480,7 @@ struct dbscalarstr : csnd::Plugin<1, 4> { static constexpr char const *otypes = "S"; static constexpr char const *itypes = "iSoo"; ConnectionData* connection; + char* resultString; int init() { if (!(connection = getConnection(csound, inargs[0]))) { @@ -489,9 +490,12 @@ struct dbscalarstr : csnd::Plugin<1, 4> { STRINGDAT &result = outargs.str_data(0); LOCK(connection); try { - std::string resultString = connection->ScalarString(sql.data, inargs[2], inargs[3]); - result.size = resultString.length() + 1; - result.data = csound->strdup((char*)resultString.c_str()); + if (resultString != NULL) { + csound->free(resultString); + } + resultString = connection->ScalarString(sql.data, csound, inargs[2], inargs[3]); + result.size = strlen(resultString) + 1; + result.data = resultString; } catch (const std::exception &e) { UNLOCK(connection); return csound->init_error(e.what()); @@ -642,9 +646,9 @@ struct dbscalarstr_kb : csnd::Plugin<1, 4> { STRINGDAT &result = outargs.str_data(0); LOCK(connection); try { - std::string resultString = connection->ScalarString(sql.data, inargs[2], inargs[3]); - result.size = resultString.length() + 1; - result.data = csound->strdup((char*) resultString.c_str()); + char* resultString = connection->ScalarString(sql.data, csound, inargs[2], inargs[3]); + result.size = strlen(resultString) + 1; + result.data = resultString; } catch (const std::exception &e) { UNLOCK(connection); return csound->perf_error(e.what(), this); diff --git a/src/postgresql.cpp b/src/postgresql.cpp index 6b54e29..7e194c6 100644 --- a/src/postgresql.cpp +++ b/src/postgresql.cpp @@ -20,13 +20,13 @@ */ #include +#include "tools.h" #include #include "libpq-fe.h" #include "connection.h" #include "postgresql.h" - void PostgresConnection::Init(csnd::Csound* csound, LoginData* login) { char connectionString[256]; @@ -62,9 +62,11 @@ MYFLT PostgresConnection::Scalar(char* sql, int row, int col) { int cols = PQnfields(result); if (row > rows - 1) { + PQclear(result); throw std::runtime_error("row number out of range"); } if (col > cols - 1) { + PQclear(result); throw std::runtime_error("column number out of range"); } @@ -73,52 +75,40 @@ MYFLT PostgresConnection::Scalar(char* sql, int row, int col) { return value; } -char* PostgresConnection::ScalarString(char* sql, int row, int col) { +char* PostgresConnection::ScalarString(char* sql, csnd::Csound* csound, int row, int col) { PGresult* result = Query(sql); int rows = PQntuples(result); int cols = PQnfields(result); if (row > rows - 1) { + PQclear(result); throw std::runtime_error("row number out of range"); } if (col > cols -1) { + PQclear(result); throw std::runtime_error("column number out of range"); } - char* value = (char*) PQgetvalue(result, row, col); + char* value = csound->strdup(PQgetvalue(result, row, col)); PQclear(result); return value; - } void PostgresConnection::ToArray(PGresult* result, csnd::Csound* csound, ARRAYDAT* array, bool asString) { int rows = PQntuples(result); int cols = PQnfields(result); - int totalResults = rows * cols; - array->sizes = (int32_t*) csound->calloc(sizeof(int32_t) * 2); - array->sizes[0] = rows; - array->sizes[1] = cols; - array->dimensions = 2; - CS_VARIABLE *var = array->arrayType->createVariable(csound, NULL); - array->arrayMemberSize = var->memBlockSize; - array->data = (MYFLT*) csound->calloc(var->memBlockSize * totalResults); - STRINGDAT* strings; - if (asString) { - strings = (STRINGDAT*) array->data; - } + STRINGDAT* strings = arrayInit(csound, array, rows, cols); int index = 0; - for (int rowNum = 0; rowNum < rows; ++rowNum) { - for (int colNum = 0; colNum < cols; ++colNum) { + for (int rowIndex = 0; rowIndex < rows; ++rowIndex) { + for (int colIndex = 0; colIndex < cols; ++colIndex) { if (asString) { - char* item = (char*) PQgetvalue(result, rowNum, colNum); - strings[index].size = strlen(item) + 1; - strings[index].data = csound->strdup(item); + insertArrayStringItem(csound, strings, index, (char*) PQgetvalue(result, rowIndex, colIndex)); } else { - array->data[index] = (MYFLT) atof(PQgetvalue(result, rowNum, colNum)); + array->data[index] = (MYFLT) atof(PQgetvalue(result, rowIndex, colIndex)); } index++; } diff --git a/src/sqlite3.cpp b/src/sqlite3.cpp index 9724781..cbd4af1 100644 --- a/src/sqlite3.cpp +++ b/src/sqlite3.cpp @@ -20,6 +20,7 @@ */ #include +#include "tools.h" #include #include #include "connection.h" @@ -56,14 +57,16 @@ MYFLT SqliteConnection::Scalar(char* sql, int row, int col) { sqlite3_stmt *stmt = Query(sql); int colCount = sqlite3_column_count(stmt); int rc = sqlite3_step(stmt); + + if (col > colCount -1) { + rc = sqlite3_finalize(stmt); + throw std::runtime_error("column number out of range"); + } + + rc = sqlite3_step(stmt); int rowIndex = 0; while (rc != SQLITE_DONE && rc != SQLITE_OK) { if (rowIndex == row) { - - if (col > colCount -1) { - rc = sqlite3_finalize(stmt); - throw std::runtime_error("column number out of range"); - } MYFLT result = (MYFLT) sqlite3_column_double(stmt, col); rc = sqlite3_finalize(stmt); return result; @@ -75,7 +78,7 @@ MYFLT SqliteConnection::Scalar(char* sql, int row, int col) { throw std::runtime_error("no result"); } -char* SqliteConnection::ScalarString(char* sql, int row, int col) { +char* SqliteConnection::ScalarString(char* sql, csnd::Csound* csound, int row, int col) { sqlite3_stmt *stmt = Query(sql); int colCount = sqlite3_column_count(stmt); int rc = sqlite3_step(stmt); @@ -87,7 +90,7 @@ char* SqliteConnection::ScalarString(char* sql, int row, int col) { rc = sqlite3_finalize(stmt); throw std::runtime_error("column number out of range"); } - char* result = (char*) sqlite3_column_text(stmt, col); + char* result = csound->strdup((char*) sqlite3_column_text(stmt, col)); rc = sqlite3_finalize(stmt); return result; } @@ -110,21 +113,11 @@ int SqliteConnection::RowCount(sqlite3_stmt* stmt) { return rowCount; } + void SqliteConnection::ToArray(sqlite3_stmt* stmt, csnd::Csound* csound, ARRAYDAT* array, bool asString) { - int colNum = sqlite3_column_count(stmt); - int rowNum = RowCount(stmt); - int totalResults = colNum * rowNum; - array->sizes = (int32_t*) csound->calloc(sizeof(int32_t) * 2); - array->sizes[0] = rowNum; - array->sizes[1] = colNum; - array->dimensions = 2; - CS_VARIABLE *var = array->arrayType->createVariable(csound, NULL); - array->arrayMemberSize = var->memBlockSize; - array->data = (MYFLT*) csound->calloc(var->memBlockSize * totalResults); - STRINGDAT* strings; - if (asString) { - strings = (STRINGDAT*) array->data; - } + int cols = sqlite3_column_count(stmt); + int rows = RowCount(stmt); + STRINGDAT* strings = arrayInit(csound, array, rows, cols); int colIndex; int rowIndex; @@ -132,11 +125,9 @@ void SqliteConnection::ToArray(sqlite3_stmt* stmt, csnd::Csound* csound, ARRAYDA int rc = sqlite3_step(stmt); while (rc != SQLITE_DONE && rc != SQLITE_OK) { colIndex = 0; - while (colIndex < colNum) { + while (colIndex < cols) { if (asString) { - char* item = (char*) sqlite3_column_text(stmt, colIndex); - strings[index].size = strlen(item) + 1; - strings[index].data = csound->strdup(item); + insertArrayStringItem(csound, strings, index, (char*) sqlite3_column_text(stmt, colIndex)); } else { array->data[index] = (MYFLT) sqlite3_column_double(stmt, colIndex); } diff --git a/src/tools.cpp b/src/tools.cpp new file mode 100644 index 0000000..ec8863b --- /dev/null +++ b/src/tools.cpp @@ -0,0 +1,54 @@ +/* + tools.cpp + Copyright (C) 2022 Richard Knight + + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + */ + +#include + +STRINGDAT* arrayInit(csnd::Csound* csound, ARRAYDAT* array, int rows, int cols) { + int totalResults = rows * cols; + size_t totalAllocated; + + if (array->data == NULL) { + array->sizes = (int32_t*) csound->calloc(sizeof(int32_t) * 2); + array->sizes[0] = rows; + array->sizes[1] = cols; + array->dimensions = 2; + CS_VARIABLE *var = array->arrayType->createVariable(csound->get_csound(), NULL); + array->arrayMemberSize = var->memBlockSize; + totalAllocated = array->arrayMemberSize * totalResults; + array->data = (MYFLT*) csound->calloc(totalAllocated); + } else if ((totalAllocated = array->arrayMemberSize * totalResults) > array->allocated) { + array->data = (MYFLT*) csound->realloc(array->data, totalAllocated); + memset((char*)(array->data)+array->allocated, '\0', totalAllocated - array->allocated); + array->allocated = totalAllocated; + } + + // convenience return to be used if it is a string array + return (STRINGDAT*) array->data; +} + + +void insertArrayStringItem(csnd::Csound* csound, STRINGDAT* strings, int index, char* item) { + strings[index].size = strlen(item) + 1; + if (strings[index].data != NULL) { + csound->free(strings[index].data); + } + strings[index].data = csound->strdup(item); +} \ No newline at end of file -- cgit v1.2.3