From 38a1ae0ed4d372b8321d2aee042831aee61e4691 Mon Sep 17 00:00:00 2001 From: Bikram Chatterjee Date: Mon, 2 Sep 2019 09:31:21 +0200 Subject: [PATCH] Resource counter API (#136) * raise exception with full DPI error * add testcase catch_error_message * add testcase catch_error_message_conn * remove two debug prints in eunit * fix broken fixture * fixed MARCO args formatting * add a comma * re-work, clean WIP currently segfault * warnings * fix tuples in dpi:dafe call, add error handling to call macro, create an own connection for the ping test * change error message test to use invalid password instead of TNS to avoid timeout * catch everything in execStmt macro * change execStmt into a function * separate stmt and conn tests into their own fixtures * adapt and fix old CT test cases, implement dpiData_setDouble * Partial common params support in creating connection (#59) * raise exception with full DPI error * add testcase catch_error_message * add testcase catch_error_message_conn * remove two debug prints in eunit * fix broken fixture * Added property utf8 when creating a new connection. * fixed MARCO args formatting * add a comma * temporary files and folders * read config from map - WIP * re-work, clean WIP currently segfault * warnings * fix tuples in dpi:dafe call, add error handling to call macro, create an own connection for the ping test * change error message test to use invalid password instead of TNS to avoid timeout * catch everything in execStmt macro * change execStmt into a function * separate stmt and conn tests into their own fixtures * adapt and fix old CT test cases, implement dpiData_setDouble * WIP * implement common test infrastructure, implement test job insertIntoTable * remove some unneeded macros * data resource free fixes * add bindData job, add setup for running tests on one persistent dpi slave * local fix of a memory leak, fix merge changing of eunit tests by taking changes from a newer branch * raise exception with full DPI error * add testcase catch_error_message * add testcase catch_error_message_conn * remove two debug prints in eunit * fix broken fixture * fixed MARCO args formatting * add a comma * re-work, clean WIP currently segfault * warnings * fix tuples in dpi:dafe call, add error handling to call macro, create an own connection for the ping test * change error message test to use invalid password instead of TNS to avoid timeout * catch everything in execStmt macro * change execStmt into a function * separate stmt and conn tests into their own fixtures * adapt and fix old CT test cases, implement dpiData_setDouble * Partial common params support in creating connection (#59) * raise exception with full DPI error * add testcase catch_error_message * add testcase catch_error_message_conn * remove two debug prints in eunit * fix broken fixture * Added property utf8 when creating a new connection. * fixed MARCO args formatting * add a comma * temporary files and folders * read config from map - WIP * re-work, clean WIP currently segfault * warnings * fix tuples in dpi:dafe call, add error handling to call macro, create an own connection for the ping test * change error message test to use invalid password instead of TNS to avoid timeout * catch everything in execStmt macro * change execStmt into a function * separate stmt and conn tests into their own fixtures * adapt and fix old CT test cases, implement dpiData_setDouble * WIP * implement common test infrastructure, implement test job insertIntoTable * remove some unneeded macros * add bindData job, add setup for running tests on one persistent dpi slave * local fix of a memory leak, fix merge changing of eunit tests by taking changes from a newer branch * formatting * fix mangled rebase * set test cases * quick feature testing * ignored ct reports eunit wanring fixes removed deprecated eunit * ct sample config fix priv_dir look up for ct * benchmarking WIP * no tests * sanitized * indent to spaces * removed files from common-test branch * removed ct specific ignores * reverted to master * cosmetic fixes to master * delete reverted * reverted to master * dataPtr_res reused * test coverage WIP * Full test coverage * resource counting WIP * res (de)alloc macrofied with counters eunit added * all test pass typo fixed * upgrade implemented * data 100% cover renamed to cover dpi_eunit merged in * WIP * cosmetics * error DPI_ORACLE_TYPE_NUM_FROM_ATOM no match * major re-fectoring and cleanup * run moved to test * cosmetics * no change * cov files * hardcoded seed for conract rand sequence * escript fixed seed for const rand sequence * read back * test cosmetics * debugging stmt release leak WIP * print formatting fixes resource count fixes * CRLF @ EOF * removed un reachable code --- c_src/dpiConn_nif.c | 49 ++++++++----------- c_src/dpiContext_nif.c | 13 ++--- c_src/dpiData_nif.c | 26 +++++----- c_src/dpiStmt_nif.c | 57 ++++++++++------------ c_src/dpiVar_nif.c | 15 +++--- c_src/dpi_nif.c | 87 ++++++++++++++++++++++++++++++++- c_src/dpi_nif.h | 53 +++++++++++++++++--- src/dpi.erl | 4 ++ test/console.escript | 11 +++-- test/cover_tests.erl | 108 +++++++++++++++++++++++++++++++++++++++-- 10 files changed, 317 insertions(+), 106 deletions(-) diff --git a/c_src/dpiConn_nif.c b/c_src/dpiConn_nif.c index 333e29a..bd2736d 100644 --- a/c_src/dpiConn_nif.c +++ b/c_src/dpiConn_nif.c @@ -38,8 +38,7 @@ DPI_NIF_FUN(conn_create) dpiCommonCreateParams commonParams; RAISE_EXCEPTION_ON_DPI_ERROR( contextRes->context, - dpiContext_initCommonCreateParams(contextRes->context, &commonParams), - NULL); + dpiContext_initCommonCreateParams(contextRes->context, &commonParams)); if (commonParamsMapSize > 0) { @@ -71,10 +70,10 @@ DPI_NIF_FUN(conn_create) } } - dpiConn_res *connRes = - enif_alloc_resource(dpiConn_type, sizeof(dpiConn_res)); + dpiConn_res *connRes; + ALLOC_RESOURCE(connRes, dpiConn); - RAISE_EXCEPTION_ON_DPI_ERROR( + RAISE_EXCEPTION_ON_DPI_ERROR_RESOURCE( contextRes->context, dpiConn_create( contextRes->context, (const char *)userName.data, userName.size, @@ -83,7 +82,7 @@ DPI_NIF_FUN(conn_create) &commonParams, NULL, // TODO implement connCreateParams &connRes->conn), - connRes); + connRes, dpiConn); // Save context into connection for access from dpiError connRes->context = contextRes->context; @@ -117,16 +116,16 @@ DPI_NIF_FUN(conn_prepareStmt) if (!enif_inspect_binary(env, argv[3], &tag)) BADARG_EXCEPTION(3, "binary/string tag"); - dpiStmt_res *stmtRes = - enif_alloc_resource(dpiStmt_type, sizeof(dpiStmt_res)); + dpiStmt_res *stmtRes; + ALLOC_RESOURCE(stmtRes, dpiStmt); - RAISE_EXCEPTION_ON_DPI_ERROR( + RAISE_EXCEPTION_ON_DPI_ERROR_RESOURCE( connRes->context, dpiConn_prepareStmt( connRes->conn, scrollable, (const char *)sql.data, sql.size, tag.size > 0 ? (const char *)tag.data : NULL, tag.size, &stmtRes->stmt), - stmtRes); + stmtRes, dpiStmt); stmtRes->context = connRes->context; @@ -175,16 +174,16 @@ DPI_NIF_FUN(conn_newVar) if (enif_compare(argv[7], ATOM_NULL)) BADARG_EXCEPTION(7, "atom objType"); - dpiVar_res *varRes = - enif_alloc_resource(dpiVar_type, sizeof(dpiVar_res)); + dpiVar_res *varRes; + ALLOC_RESOURCE(varRes, dpiVar); - RAISE_EXCEPTION_ON_DPI_ERROR( + RAISE_EXCEPTION_ON_DPI_ERROR_RESOURCE( connRes->context, dpiConn_newVar( connRes->conn, oracleTypeNum, nativeTypeNum, maxArraySize, size, sizeIsBytes, isArray, NULL, &varRes->var, &data), - varRes); + varRes, dpiVar); varRes->context = connRes->context; @@ -196,7 +195,7 @@ DPI_NIF_FUN(conn_newVar) varRes->head = NULL; for (int i = maxArraySize - 1; i >= 0; i--) { - dataRes = enif_alloc_resource(dpiDataPtr_type, sizeof(dpiDataPtr_res)); + ALLOC_RESOURCE(dataRes, dpiDataPtr); dataRes->stmtRes = NULL; dataRes->next = NULL; dataRes->isQueryValue = 0; @@ -234,9 +233,7 @@ DPI_NIF_FUN(conn_commit) BADARG_EXCEPTION(0, "resource connection"); RAISE_EXCEPTION_ON_DPI_ERROR( - connRes->context, - dpiConn_commit(connRes->conn), - NULL); + connRes->context, dpiConn_commit(connRes->conn)); RETURNED_TRACE; return ATOM_OK; @@ -252,9 +249,7 @@ DPI_NIF_FUN(conn_rollback) BADARG_EXCEPTION(0, "resource connection"); RAISE_EXCEPTION_ON_DPI_ERROR( - connRes->context, - dpiConn_rollback(connRes->conn), - NULL); + connRes->context, dpiConn_rollback(connRes->conn)); RETURNED_TRACE; return ATOM_OK; @@ -270,9 +265,7 @@ DPI_NIF_FUN(conn_ping) BADARG_EXCEPTION(0, "resource connection"); RAISE_EXCEPTION_ON_DPI_ERROR( - connRes->context, - dpiConn_ping(connRes->conn), - NULL); + connRes->context, dpiConn_ping(connRes->conn)); RETURNED_TRACE; return ATOM_OK; @@ -314,8 +307,9 @@ DPI_NIF_FUN(conn_close) dpiConn_close( connRes->conn, mode, tag.size > 0 ? (const char *)tag.data : NULL, - tag.size), - NULL); + tag.size)); + + RELEASE_RESOURCE(connRes, dpiConn); RETURNED_TRACE; return ATOM_OK; @@ -389,8 +383,7 @@ DPI_NIF_FUN(conn_setClientIdentifier) RAISE_EXCEPTION_ON_DPI_ERROR( connRes->context, dpiConn_setClientIdentifier( - connRes->conn, (const char *)value.data, value.size), - NULL); + connRes->conn, (const char *)value.data, value.size)); return ATOM_OK; } diff --git a/c_src/dpiContext_nif.c b/c_src/dpiContext_nif.c index 42429a9..302f828 100644 --- a/c_src/dpiContext_nif.c +++ b/c_src/dpiContext_nif.c @@ -20,8 +20,8 @@ DPI_NIF_FUN(context_create) if (!enif_get_uint(env, argv[1], &minor)) BADARG_EXCEPTION(1, "uint minor"); - dpiContext_res *contextRes = - enif_alloc_resource(dpiContext_type, sizeof(dpiContext_res)); + dpiContext_res *contextRes; + ALLOC_RESOURCE(contextRes, dpiContext); // RAISE_EXCEPTION_ON_DPI_ERROR macro can't be used since we need to return // the error details too @@ -29,7 +29,8 @@ DPI_NIF_FUN(context_create) if (DPI_FAILURE == dpiContext_create(major, minor, &contextRes->context, &error)) { - enif_release_resource(contextRes); + RELEASE_RESOURCE(contextRes, dpiContext); + RETURNED_TRACE; return enif_raise_exception( env, @@ -55,9 +56,9 @@ DPI_NIF_FUN(context_destroy) BADARG_EXCEPTION(0, "resource context"); RAISE_EXCEPTION_ON_DPI_ERROR( - contextRes->context, dpiContext_destroy(contextRes->context), NULL); + contextRes->context, dpiContext_destroy(contextRes->context)); - enif_release_resource(contextRes); + RELEASE_RESOURCE(contextRes, dpiContext); RETURNED_TRACE; return ATOM_OK; @@ -76,7 +77,7 @@ DPI_NIF_FUN(context_getClientVersion) RAISE_EXCEPTION_ON_DPI_ERROR( contextRes->context, - dpiContext_getClientVersion(contextRes->context, &version), NULL); + dpiContext_getClientVersion(contextRes->context, &version)); ERL_NIF_TERM map = enif_make_new_map(env); diff --git a/c_src/dpiData_nif.c b/c_src/dpiData_nif.c index 2984cde..006e9e0 100644 --- a/c_src/dpiData_nif.c +++ b/c_src/dpiData_nif.c @@ -26,14 +26,6 @@ void dpiData_res_dtor(ErlNifEnv *env, void *resource) void dpiDataPtr_res_dtor(ErlNifEnv *env, void *resource) { CALL_TRACE; - - dpiDataPtr_res *data = (dpiDataPtr_res *)resource; - if (data->stmtRes) - { - enif_release_resource(data->stmtRes); - data->stmtRes = NULL; - } - RETURNED_TRACE; } @@ -41,7 +33,9 @@ DPI_NIF_FUN(data_ctor) { CHECK_ARGCOUNT(0); - dpiData_res *data = enif_alloc_resource(dpiData_type, sizeof(dpiData_res)); + dpiData_res *data; + ALLOC_RESOURCE(data, dpiData); + data->dpiData.isNull = 1; // starts out being null // erlang process independent environment to persist data between NIF calls @@ -351,7 +345,7 @@ DPI_NIF_FUN(data_get) if (!stmtRes) { // first time - stmtRes = enif_alloc_resource(dpiStmt_type, sizeof(dpiStmt_res)); + ALLOC_RESOURCE(stmtRes, dpiStmt); dataRes->stmtRes = stmtRes; } stmtRes->stmt = data->value.asStmt; @@ -364,8 +358,7 @@ DPI_NIF_FUN(data_get) uint32_t stringlen; RAISE_EXCEPTION_ON_DPI_ERROR( dataRes->context, - dpiRowid_getStringValue(data->value.asRowid, &string, &stringlen), - NULL); + dpiRowid_getStringValue(data->value.asRowid, &string, &stringlen)); ErlNifBinary bin; enif_alloc_binary(stringlen, &bin); memcpy(bin.data, string, stringlen); @@ -448,14 +441,19 @@ DPI_NIF_FUN(data_release) if (enif_get_resource(env, argv[0], dpiData_type, (void **)&res.dataRes)) { // nothing to set to NULL - enif_release_resource(res.dataRes); + RELEASE_RESOURCE(res.dataRes, dpiData); } else if (enif_get_resource( env, argv[0], dpiDataPtr_type, (void **)&res.dataPtrRes)) { + if (res.dataPtrRes->stmtRes) + { + RELEASE_RESOURCE(res.dataPtrRes->stmtRes, dpiStmt); + res.dataPtrRes->stmtRes = NULL; + } res.dataPtrRes->dpiDataPtr = NULL; if (res.dataPtrRes->isQueryValue == 1) - enif_release_resource(res.dataPtrRes); + RELEASE_RESOURCE(res.dataPtrRes, dpiDataPtr); } else BADARG_EXCEPTION(0, "resource data"); diff --git a/c_src/dpiStmt_nif.c b/c_src/dpiStmt_nif.c index 4ebe955..e0cbc5d 100644 --- a/c_src/dpiStmt_nif.c +++ b/c_src/dpiStmt_nif.c @@ -42,8 +42,7 @@ DPI_NIF_FUN(stmt_execute) RAISE_EXCEPTION_ON_DPI_ERROR( stmtRes->context, - dpiStmt_execute(stmtRes->stmt, mode, &numCols), - NULL); + dpiStmt_execute(stmtRes->stmt, mode, &numCols)); RETURNED_TRACE; return enif_make_uint(env, numCols); @@ -75,15 +74,14 @@ DPI_NIF_FUN(stmt_executeMany) do { if (!enif_is_atom(env, head)) - RAISE_STR_EXCEPTION("mode must be a list of atoms"); + RAISE_STR_EXCEPTION("mode must be a list of atoms"); DPI_EXEC_MODE_FROM_ATOM(head, m); mode |= m; } while (enif_get_list_cell(env, tail, &head, &tail)); RAISE_EXCEPTION_ON_DPI_ERROR( stmtRes->context, - dpiStmt_executeMany(stmtRes->stmt, mode, numIters), - NULL); + dpiStmt_executeMany(stmtRes->stmt, mode, numIters)); RETURNED_TRACE; return ATOM_OK; @@ -102,8 +100,7 @@ DPI_NIF_FUN(stmt_fetch) RAISE_EXCEPTION_ON_DPI_ERROR( stmtRes->context, - dpiStmt_fetch(stmtRes->stmt, &found, &bufferRowIndex), - NULL); + dpiStmt_fetch(stmtRes->stmt, &found, &bufferRowIndex)); ERL_NIF_TERM map = enif_make_new_map(env); enif_make_map_put( @@ -134,18 +131,19 @@ DPI_NIF_FUN(stmt_getQueryValue) if (!enif_get_uint(env, argv[1], &pos)) BADARG_EXCEPTION(1, "uint pos"); - dpiDataPtr_res *data = enif_alloc_resource( - dpiDataPtr_type, sizeof(dpiDataPtr_res)); + dpiDataPtr_res *data; + ALLOC_RESOURCE(data, dpiDataPtr); + data->next = NULL; data->stmtRes = NULL; data->isQueryValue = 1; data->context = stmtRes->context; - RAISE_EXCEPTION_ON_DPI_ERROR( + RAISE_EXCEPTION_ON_DPI_ERROR_RESOURCE( stmtRes->context, dpiStmt_getQueryValue( stmtRes->stmt, pos, &nativeTypeNum, &(data->dpiDataPtr)), - data); + data, dpiDataPtr); data->type = nativeTypeNum; ERL_NIF_TERM dpiDataRes = enif_make_resource(env, data); @@ -182,8 +180,7 @@ DPI_NIF_FUN(stmt_getQueryInfo) dpiQueryInfo queryInfo; RAISE_EXCEPTION_ON_DPI_ERROR( stmtRes->context, - dpiStmt_getQueryInfo(stmtRes->stmt, pos, &queryInfo), - NULL); + dpiStmt_getQueryInfo(stmtRes->stmt, pos, &queryInfo)); dpiDataTypeInfo dti = queryInfo.typeInfo; ERL_NIF_TERM typeInfo = enif_make_new_map(env); @@ -255,8 +252,7 @@ DPI_NIF_FUN(stmt_getNumQueryColumns) RAISE_EXCEPTION_ON_DPI_ERROR( stmtRes->context, - dpiStmt_getNumQueryColumns(stmtRes->stmt, &numQueryColumns), - NULL); + dpiStmt_getNumQueryColumns(stmtRes->stmt, &numQueryColumns)); RETURNED_TRACE; return enif_make_uint(env, numQueryColumns); @@ -282,8 +278,8 @@ DPI_NIF_FUN(stmt_bindValueByPos) RAISE_EXCEPTION_ON_DPI_ERROR( stmtRes->context, - dpiStmt_bindValueByPos(stmtRes->stmt, pos, bindType, &dataRes->dpiData), - NULL); + dpiStmt_bindValueByPos(stmtRes->stmt, pos, bindType, + &dataRes->dpiData)); RETURNED_TRACE; return ATOM_OK; @@ -311,8 +307,7 @@ DPI_NIF_FUN(stmt_bindValueByName) stmtRes->context, dpiStmt_bindValueByName( stmtRes->stmt, (const char *)binary.data, binary.size, bindType, - &dataRes->dpiData), - NULL); + &dataRes->dpiData)); RETURNED_TRACE; return ATOM_OK; @@ -335,7 +330,7 @@ DPI_NIF_FUN(stmt_bindByPos) RAISE_EXCEPTION_ON_DPI_ERROR( stmtRes->context, - dpiStmt_bindByPos(stmtRes->stmt, pos, varRes->var), NULL); + dpiStmt_bindByPos(stmtRes->stmt, pos, varRes->var)); RETURNED_TRACE; return ATOM_OK; @@ -359,8 +354,8 @@ DPI_NIF_FUN(stmt_bindByName) RAISE_EXCEPTION_ON_DPI_ERROR( stmtRes->context, dpiStmt_bindByName( - stmtRes->stmt, (const char *)binary.data, binary.size, varRes->var), - NULL); + stmtRes->stmt, (const char *)binary.data, binary.size, + varRes->var)); RETURNED_TRACE; return ATOM_OK; @@ -378,10 +373,12 @@ DPI_NIF_FUN(stmt_close) if (!enif_inspect_binary(env, argv[1], &tag)) BADARG_EXCEPTION(1, "string tag"); - RAISE_EXCEPTION_ON_DPI_ERROR( + RAISE_EXCEPTION_ON_DPI_ERROR_RESOURCE( stmtRes->context, dpiStmt_close(stmtRes->stmt, (const char *)tag.data, tag.size), - stmtRes); + stmtRes, dpiStmt); + + RELEASE_RESOURCE(stmtRes, dpiStmt); RETURNED_TRACE; return ATOM_OK; @@ -397,10 +394,9 @@ DPI_NIF_FUN(stmt_getInfo) if (!enif_get_resource(env, argv[0], dpiStmt_type, (void **)&stmtRes)) BADARG_EXCEPTION(0, "resource statement"); - RAISE_EXCEPTION_ON_DPI_ERROR( - stmtRes->context, - dpiStmt_getInfo(stmtRes->stmt, &info), - stmtRes); + RAISE_EXCEPTION_ON_DPI_ERROR_RESOURCE( + stmtRes->context, dpiStmt_getInfo(stmtRes->stmt, &info), + stmtRes, dpiStmt); ERL_NIF_TERM map = enif_make_new_map(env); @@ -501,7 +497,7 @@ DPI_NIF_FUN(stmt_define) RAISE_EXCEPTION_ON_DPI_ERROR( stmtRes->context, - dpiStmt_define(stmtRes->stmt, pos, varRes->var), NULL); + dpiStmt_define(stmtRes->stmt, pos, varRes->var)); RETURNED_TRACE; return ATOM_OK; @@ -539,8 +535,7 @@ DPI_NIF_FUN(stmt_defineValue) dpiStmt_defineValue( stmtRes->stmt, pos, oraType, nativeType, size, sizeIsBytes, NULL // TODO: support dpiObjectType - ), - NULL); + )); RETURNED_TRACE; return ATOM_OK; diff --git a/c_src/dpiVar_nif.c b/c_src/dpiVar_nif.c index 80cde3b..cf4d072 100644 --- a/c_src/dpiVar_nif.c +++ b/c_src/dpiVar_nif.c @@ -23,7 +23,7 @@ DPI_NIF_FUN(var_setNumElementsInArray) RAISE_EXCEPTION_ON_DPI_ERROR( vRes->context, - dpiVar_setNumElementsInArray(vRes->var, numElements), NULL); + dpiVar_setNumElementsInArray(vRes->var, numElements)); RETURNED_TRACE; return ATOM_OK; @@ -48,8 +48,7 @@ DPI_NIF_FUN(var_setFromBytes) RAISE_EXCEPTION_ON_DPI_ERROR( vRes->context, dpiVar_setFromBytes( - vRes->var, pos, (const char *)value.data, value.size), - NULL); + vRes->var, pos, (const char *)value.data, value.size)); RETURNED_TRACE; return ATOM_OK; @@ -64,18 +63,17 @@ DPI_NIF_FUN(var_release) if ((!enif_get_resource(env, argv[0], dpiVar_type, (void **)&vRes))) BADARG_EXCEPTION(0, "resource var"); - RAISE_EXCEPTION_ON_DPI_ERROR( - vRes->context, dpiVar_release(vRes->var), NULL); + RAISE_EXCEPTION_ON_DPI_ERROR(vRes->context, dpiVar_release(vRes->var)); dpiDataPtr_res *t_itr; for (dpiDataPtr_res *itr = vRes->head; itr != NULL;) { t_itr = itr; itr = itr->next; - enif_release_resource(t_itr); + RELEASE_RESOURCE(t_itr, dpiDataPtr); } - enif_release_resource(vRes); + RELEASE_RESOURCE(vRes, dpiVar); RETURNED_TRACE; return ATOM_OK; @@ -98,8 +96,7 @@ DPI_NIF_FUN(var_getReturnedData) dpiData *data; RAISE_EXCEPTION_ON_DPI_ERROR( varRes->context, - dpiVar_getReturnedData(varRes->var, pos, &numElements, &data), - NULL); + dpiVar_getReturnedData(varRes->var, pos, &numElements, &data)); ERL_NIF_TERM dataList = enif_make_list(env, 0); diff --git a/c_src/dpi_nif.c b/c_src/dpi_nif.c index 8062152..800d8e9 100644 --- a/c_src/dpi_nif.c +++ b/c_src/dpi_nif.c @@ -17,12 +17,15 @@ ERL_NIF_TERM ATOM_FALSE; ERL_NIF_TERM ATOM_ERROR; ERL_NIF_TERM ATOM_ENOMEM; +DPI_NIF_FUN(resource_count); + static ErlNifFunc nif_funcs[] = { DPICONTEXT_NIFS, DPICONN_NIFS, DPISTMT_NIFS, DPIDATA_NIFS, - DPIVAR_NIFS}; + DPIVAR_NIFS, + {"resource_count", 0, resource_count}}; typedef struct { @@ -33,6 +36,36 @@ typedef struct /******************************************************************************* * Helper internal functions ******************************************************************************/ +DPI_NIF_FUN(resource_count) +{ + CHECK_ARGCOUNT(0); + + oranif_st *st = (oranif_st *)enif_priv_data(env); + + ERL_NIF_TERM ret = enif_make_new_map(env); + ret = enif_make_new_map(env); + enif_make_map_put( + env, ret, enif_make_atom(env, "context"), + enif_make_ulong(env, st->dpiContext_count), &ret); + enif_make_map_put( + env, ret, enif_make_atom(env, "connection"), + enif_make_ulong(env, st->dpiConn_count), &ret); + enif_make_map_put( + env, ret, enif_make_atom(env, "statement"), + enif_make_ulong(env, st->dpiStmt_count), &ret); + enif_make_map_put( + env, ret, enif_make_atom(env, "variable"), + enif_make_ulong(env, st->dpiVar_count), &ret); + enif_make_map_put( + env, ret, enif_make_atom(env, "data"), + enif_make_ulong(env, st->dpiData_count), &ret); + enif_make_map_put( + env, ret, enif_make_atom(env, "datapointer"), + enif_make_ulong(env, st->dpiDataPtr_count), &ret); + + RETURNED_TRACE; + return ret; +} ERL_NIF_TERM dpiErrorInfoMap(ErlNifEnv *env, dpiErrorInfo e) { @@ -91,6 +124,27 @@ static int load(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM load_info) { CALL_TRACE; + oranif_st *st = enif_alloc(sizeof(oranif_st)); + if (st == NULL) + { + E("failed allocate private structure of %zd bytes\r\n", sizeof(oranif_st)); + return 1; + } + + st->lock = enif_mutex_create("oranif"); + if (st->lock == NULL) + { + E("failed to create oranif mutex\r\n"); + return 1; + } + + st->dpiVar_count = 0; + st->dpiData_count = 0; + st->dpiStmt_count = 0; + st->dpiConn_count = 0; + st->dpiContext_count = 0; + st->dpiDataPtr_count = 0; + DEF_RES(dpiContext); DEF_RES(dpiConn); DEF_RES(dpiStmt); @@ -105,6 +159,8 @@ static int load(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM load_info) ATOM_ERROR = enif_make_atom(env, "error"); ATOM_ENOMEM = enif_make_atom(env, "enomem"); + *priv_data = (void *)st; + RETURNED_TRACE; return 0; } @@ -113,6 +169,31 @@ static int upgrade(ErlNifEnv *env, void **priv_data, void **old_priv_data, ERL_NIF_TERM load_info) { CALL_TRACE; + + oranif_st *st = enif_alloc(sizeof(oranif_st)); + if (st == NULL) + { + E("failed allocate private structure of %zd bytes\r\n", sizeof(oranif_st)); + return 1; + } + + st->lock = enif_mutex_create("oranif"); + if (st->lock == NULL) + { + E("failed to create oranif mutex\r\n"); + return 1; + } + + oranif_st *old_st = (oranif_st *)*old_priv_data; + st->dpiVar_count = old_st->dpiVar_count; + st->dpiData_count = old_st->dpiData_count; + st->dpiStmt_count = old_st->dpiStmt_count; + st->dpiConn_count = old_st->dpiConn_count; + st->dpiContext_count = old_st->dpiContext_count; + st->dpiDataPtr_count = old_st->dpiDataPtr_count; + + *priv_data = (void *)st; + RETURNED_TRACE; return 0; } @@ -120,6 +201,10 @@ static int upgrade(ErlNifEnv *env, void **priv_data, static void unload(ErlNifEnv *env, void *priv_data) { CALL_TRACE; + + enif_mutex_destroy(((oranif_st *)priv_data)->lock); + enif_free(priv_data); + RETURNED_TRACE; } diff --git a/c_src/dpi_nif.h b/c_src/dpi_nif.h index a0d3def..bcedd74 100644 --- a/c_src/dpi_nif.h +++ b/c_src/dpi_nif.h @@ -153,14 +153,22 @@ extern ERL_NIF_TERM ATOM_ENOMEM; ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) extern ERL_NIF_TERM dpiErrorInfoMap(ErlNifEnv *, dpiErrorInfo); -#define RAISE_EXCEPTION_ON_DPI_ERROR(_ctx, _exprn, _opt_res) \ - if (DPI_FAILURE == (_exprn)) \ - { \ - dpiErrorInfo __err; \ - if (_opt_res) \ - enif_release_resource(_opt_res); \ - dpiContext_getError(_ctx, &__err); \ - RAISE_EXCEPTION(dpiErrorInfoMap(env, __err)); \ +#define RAISE_EXCEPTION_ON_DPI_ERROR(_ctx, _exprn) \ + if (DPI_FAILURE == (_exprn)) \ + { \ + dpiErrorInfo __err; \ + dpiContext_getError(_ctx, &__err); \ + RAISE_EXCEPTION(dpiErrorInfoMap(env, __err)); \ + } + +#define RAISE_EXCEPTION_ON_DPI_ERROR_RESOURCE(_ctx, _exprn, _opt_res, _res) \ + if (DPI_FAILURE == (_exprn)) \ + { \ + dpiErrorInfo __err; \ + if (_opt_res) \ + RELEASE_RESOURCE(_opt_res, _res); \ + dpiContext_getError(_ctx, &__err); \ + RAISE_EXCEPTION(dpiErrorInfoMap(env, __err)); \ } #define CASE_MACRO2STR(_Macro, _StrVar) \ @@ -177,4 +185,33 @@ extern ERL_NIF_TERM dpiErrorInfoMap(ErlNifEnv *, dpiErrorInfo); _assign = enif_make_atom(env, #_macro); \ break +typedef struct +{ + unsigned long dpiContext_count; + unsigned long dpiConn_count; + unsigned long dpiStmt_count; + unsigned long dpiData_count; + unsigned long dpiDataPtr_count; + unsigned long dpiVar_count; + ErlNifMutex *lock; +} oranif_st; + +#define ALLOC_RESOURCE(_var, _dpiType) \ + { \ + oranif_st *st = (oranif_st *)enif_priv_data(env); \ + enif_mutex_lock(st->lock); \ + _var = enif_alloc_resource(_dpiType##_type, sizeof(_dpiType##_res)); \ + st->_dpiType##_count++; \ + enif_mutex_unlock(st->lock); \ + } + +#define RELEASE_RESOURCE(_var, _dpiType) \ + { \ + oranif_st *st = (oranif_st *)enif_priv_data(env); \ + enif_mutex_lock(st->lock); \ + enif_release_resource(_var); \ + st->_dpiType##_count--; \ + enif_mutex_unlock(st->lock); \ + } + #endif // _DPI_NIF_H_ diff --git a/src/dpi.erl b/src/dpi.erl index 74974a4..aa51524 100644 --- a/src/dpi.erl +++ b/src/dpi.erl @@ -6,6 +6,8 @@ -export([load_unsafe/0]). -export([safe/2, safe/3, safe/4]). +-export([resource_count/0]). + -include("dpiContext.hrl"). -include("dpiConn.hrl"). -include("dpiStmt.hrl"). @@ -131,3 +133,5 @@ safe(SlaveNode, Fun, Args) when is_function(Fun), is_list(Args) -> -spec safe(atom(), function()) -> term(). safe(SlaveNode, Fun) when is_function(Fun)-> slave_call(SlaveNode, erlang, apply, [Fun, []]). + +resource_count() -> ?NIF_NOT_LOADED. diff --git a/test/console.escript b/test/console.escript index 757068d..5717451 100644 --- a/test/console.escript +++ b/test/console.escript @@ -5,13 +5,14 @@ -define(DPI_MAJOR_VERSION, 3). -define(DPI_MINOR_VERSION, 0). -define(TNS, - <<"(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=" - "(PROTOCOL=tcp)(HOST=127.0.0.1)(PORT=1521)))" - "(CONNECT_DATA=(SERVICE_NAME=XE)))">> + <<"(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=" + "(PROTOCOL=tcp)(HOST=127.0.0.1)(PORT=1521)))" + "(CONNECT_DATA=(SERVICE_NAME=XE)))">> ). main([]) -> - dpi:load_unsafe(), - Context = dpi:context_create(?DPI_MAJOR_VERSION, ?DPI_MINOR_VERSION), + dpi:load_unsafe(), + io:format("START ~p~n", [dpi:resource_count()]), + Context = dpi:context_create(?DPI_MAJOR_VERSION, ?DPI_MINOR_VERSION), Conn = dpi:conn_create( Context, <<"scott">>, <<"regit">>, ?TNS, #{encoding => "AL32UTF8", nencoding => "AL32UTF8"}, #{} diff --git a/test/cover_tests.erl b/test/cover_tests.erl index bacf15f..20ee05b 100644 --- a/test/cover_tests.erl +++ b/test/cover_tests.erl @@ -88,7 +88,8 @@ contextGetClientVersion(TestCtx) -> dpiCall(TestCtx, context_getClientVersion, [Context]), ?assert(is_integer(CRNum)), ?assert(is_integer(CVNum)), - ?assert(is_integer(CFNum)). + ?assert(is_integer(CFNum)), + dpiCall(TestCtx, context_destroy, [Context]). %------------------------------------------------------------------------------- % Connection APIs @@ -490,6 +491,8 @@ stmtExecuteMany_varGetReturnedData(#{session := Conn} = TestCtx) -> [D] = maps:get(data, Result), ?assert(byte_size(dpiCall(TestCtx, data_get, [D])) > 0) end || Idx <- Indices], + dpiCall(TestCtx, var_release, [Var]), + dpiCall(TestCtx, var_release, [VarRowId]), dpiCall(TestCtx, stmt_close, [Stmt, <<>>]). stmtExecute(#{session := Conn} = TestCtx) -> @@ -1399,9 +1402,10 @@ dataGetStmt(#{session := Conn} = TestCtx) -> dpiCall(TestCtx, data_release, [DataChoice]), dpiCall(TestCtx, var_release, [VarChoice]), dpiCall(TestCtx, data_release, [DataStmt]), - dpiCall(TestCtx, var_release, [VarStmt]). + dpiCall(TestCtx, var_release, [VarStmt]), + dpiCall(TestCtx, stmt_close, [Stmt, <<>>]). -dataGetInt64(#{session := Conn} = TestCtx) -> +dataGetInt64(TestCtx) -> ?ASSERT_EX( "Unable to retrieve resource data/ptr from arg0", dpiCall(TestCtx, data_getInt64, [?BAD_REF]) @@ -1456,6 +1460,66 @@ dataRelease(#{session := Conn} = TestCtx) -> ?assertEqual(ok, dpiCall(TestCtx, data_release, [Data1])), dpiCall(TestCtx, var_release, [Var]). +resourceCounting(#{context := Context, session := Conn} = TestCtx) -> + #{tns := Tns, user := User, password := Password} = getConfig(), + Indices = lists:seq(1, 5), + #{ + context := ICtxs, + variable := IVars, + connection := IConns, + data := IDatas, + statement := IStmts, + datapointer := IDataPtrs + } = InitialRC = dpiCall(TestCtx, resource_count, []), + Resources = [{ + dpiCall( + TestCtx, context_create, [?DPI_MAJOR_VERSION, ?DPI_MINOR_VERSION] + ), + dpiCall( + TestCtx, conn_create, [ + Context, User, Password, Tns, + #{encoding => "AL32UTF8", nencoding => "AL32UTF8"}, #{} + ] + ), + dpiCall( + TestCtx, conn_prepareStmt, + [Conn, false, <<"select * from dual">>, <<>>] + ), + dpiCall( + TestCtx, conn_newVar, + [Conn, 'DPI_ORACLE_TYPE_NATIVE_DOUBLE', 'DPI_NATIVE_TYPE_DOUBLE', + 1, 0, false, false, null] + ), + dpiCall(TestCtx, data_ctor, []) + } || _ <- Indices], + + #{ + context := Ctxs, + variable := Vars, + connection := Conns, + data := Datas, + statement := Stmts, + datapointer := DataPtrs + } = dpiCall(TestCtx, resource_count, []), + ?assertEqual(5, Ctxs - ICtxs), + ?assertEqual(5, Vars - IVars), + ?assertEqual(5, Conns - IConns), + ?assertEqual(5, Stmts - IStmts), + ?assertEqual(5, Datas - IDatas), + ?assertEqual(5, DataPtrs - IDataPtrs), + + lists:foreach( + fun({Ctx, LConn, Stmt, #{var := Var}, Data}) -> + ok = dpiCall(TestCtx, var_release, [Var]), + ok = dpiCall(TestCtx, stmt_close, [Stmt, <<>>]), + ok = dpiCall(TestCtx, conn_close, [LConn, [], <<>>]), + ok = dpiCall(TestCtx, context_destroy, [Ctx]), + ok = dpiCall(TestCtx, data_release, [Data]) + end, + Resources + ), + ?assertEqual(InitialRC, dpiCall(TestCtx, resource_count, [])). + %------------------------------------------------------------------------------- % eunit infrastructure callbacks %------------------------------------------------------------------------------- @@ -1471,6 +1535,15 @@ setup(#{safe := true}) -> setup_context(TestCtx) -> SlaveCtx = setup(TestCtx), + maps:fold( + fun(K, V, _) -> + if V > 0 -> ?debugFmt("~p ~p = ~p", [?FUNCTION_NAME, K, V]); + true -> ok + end + end, + noacc, + dpiCall(SlaveCtx, resource_count, []) + ), SlaveCtx#{ context => dpiCall( SlaveCtx, context_create, [?DPI_MAJOR_VERSION, ?DPI_MINOR_VERSION] @@ -1480,6 +1553,15 @@ setup_context(TestCtx) -> setup_connecion(TestCtx) -> ContextCtx = #{context := Context} = setup_context(TestCtx), #{tns := Tns, user := User, password := Password} = getConfig(), + maps:fold( + fun + (_K, 0, _) -> ok; + (context, 1, _) -> ok; + (K, V, _) -> ?assertEqual({K, 0}, {K, V}) + end, + noacc, + dpiCall(ContextCtx, resource_count, []) + ), ContextCtx#{ session => dpiCall( ContextCtx, conn_create, @@ -1492,9 +1574,26 @@ setup_connecion(TestCtx) -> cleanup(#{session := Connnnection} = Ctx) -> dpiCall(Ctx, conn_close, [Connnnection, [], <<>>]), + maps:fold( + fun + (_K, 0, _) -> ok; + (context, 1, _) -> ok; + (K, V, _) -> ?debugFmt("~p ~p = ~p", [?FUNCTION_NAME, K, V]) + end, + noacc, + dpiCall(Ctx, resource_count, []) + ), cleanup(maps:without([session], Ctx)); cleanup(#{context := Context} = Ctx) -> dpiCall(Ctx, context_destroy, [Context]), + maps:fold( + fun + (_K, 0, _) -> ok; + (K, V, _) -> ?assertEqual({K, 0}, {K, V}) + end, + noacc, + dpiCall(Ctx, resource_count, []) + ), cleanup(maps:without([context], Ctx)); cleanup(_) -> ok. @@ -1581,7 +1680,8 @@ getConfig() -> ?F(dataGetStmt), ?F(dataGetInt64), ?F(dataGetBytes), - ?F(dataRelease) + ?F(dataRelease), + ?F(resourceCounting) ]). unsafe_no_context_test_() ->