diff --git a/.github/workflows/bld_wheels_and_upload.yml b/.github/workflows/bld_wheels_and_upload.yml index 6cf0dce6..c95bb583 100644 --- a/.github/workflows/bld_wheels_and_upload.yml +++ b/.github/workflows/bld_wheels_and_upload.yml @@ -21,10 +21,11 @@ jobs: steps: - uses: actions/checkout@v5 - name: Build wheels - uses: pypa/cibuildwheel@v3.4.1 + uses: pypa/cibuildwheel@v4.2.0 env: CIBW_BUILD: "*-win_amd64" - CIBW_SKIP: "cp38-* *t-*" + CIBW_SKIP: "*t-*" + CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: "python -c \"import shutil,sys; shutil.copy2(sys.argv[1], sys.argv[2])\" {wheel} {dest_dir}" - name: Inject ibm_db_dll.pth into wheels run: python scripts/inject_pth_into_wheel.py wheelhouse @@ -44,10 +45,11 @@ jobs: steps: - uses: actions/checkout@v5 - name: Build wheels - uses: pypa/cibuildwheel@v3.4.1 + uses: pypa/cibuildwheel@v4.2.0 env: CIBW_BUILD: "*-win32" - CIBW_SKIP: "cp38-* *t-*" + CIBW_SKIP: "*t-*" + CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: "python -c \"import shutil,sys; shutil.copy2(sys.argv[1], sys.argv[2])\" {wheel} {dest_dir}" - name: Inject ibm_db_dll.pth into wheels run: python scripts/inject_pth_into_wheel.py wheelhouse @@ -67,7 +69,7 @@ jobs: steps: - uses: actions/checkout@v5 - name: Build wheels - uses: pypa/cibuildwheel@v3.4.1 + uses: pypa/cibuildwheel@v4.2.0 env: CIBW_ARCHS_LINUX: "x86_64 i686" # clidriver v12.1 needs glibc >= 2.34 and CPUs supporting the x86_64-v2 @@ -75,7 +77,7 @@ jobs: # so manylinux2014 (glibc 2.17) can no longer be used here. CIBW_MANYLINUX_I686_IMAGE: manylinux_2_34 CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_34 - CIBW_SKIP: "cp38-* *t-* *-musllinux_* *-*linux_{aarch64,ppc64le}" + CIBW_SKIP: "*t-* *-musllinux_* *-*linux_{aarch64,ppc64le}" CIBW_REPAIR_WHEEL_COMMAND_LINUX: auditwheel repair --disable-isa-ext-check @@ -106,9 +108,9 @@ jobs: steps: - uses: actions/checkout@v5 - name: Build wheels - uses: pypa/cibuildwheel@v3.4.1 + uses: pypa/cibuildwheel@v4.2.0 env: - CIBW_SKIP: "cp38-* *t-*" + CIBW_SKIP: "*t-*" MACOSX_DEPLOYMENT_TARGET: 14.0 - uses: actions/upload-artifact@v6 @@ -126,10 +128,10 @@ jobs: steps: - uses: actions/checkout@v5 - name: Build wheels - uses: pypa/cibuildwheel@v3.4.1 + uses: pypa/cibuildwheel@v4.2.0 env: CIBW_ARCHS: "x86_64" - CIBW_SKIP: "cp38-* *t-*" + CIBW_SKIP: "*t-*" MACOSX_DEPLOYMENT_TARGET: 10.15 - uses: actions/upload-artifact@v6 @@ -156,7 +158,6 @@ jobs: rm -rf "$TARBALL" tar -czf "$TARBALL" "$DIRNAME" rm -rf "$DIRNAME" - - name: Upload sdist uses: actions/upload-artifact@v6 with: diff --git a/ibm_db.c b/ibm_db.c index 256ac805..72a37d45 100644 --- a/ibm_db.c +++ b/ibm_db.c @@ -176,8 +176,8 @@ typedef struct _param_cache_node SQLINTEGER *ivalueArray; /* Temp storage array of values */ double *fvalueArray; /* Temp storage array of values */ SQLINTEGER *bind_indicator_array; /* Temp storage array of values */ - SQLSMALLINT cardinality; - SQLSMALLINT actual_cardinality; + SQLLEN cardinality; + SQLLEN actual_cardinality; struct _param_cache_node *next; /* Pointer to next node */ } param_node; @@ -510,7 +510,7 @@ char *strtoupper(char *data, int max) #ifndef __MVS__ static void _python_ibm_db_set_array_param_cardinality(stmt_handle *stmt_res, param_node *curr, - SQLSMALLINT cardinality) + SQLLEN cardinality) { SQLHDESC hIPD = (SQLHDESC)0; @@ -522,8 +522,8 @@ static void _python_ibm_db_set_array_param_cardinality(stmt_handle *stmt_res, LogMsg(INFO, "entry _python_ibm_db_set_array_param_cardinality()"); snprintf(messageStr, sizeof(messageStr), - "Setting array param descriptor fields: param_num=%d, param_type=%d, cardinality=%d", - curr->param_num, curr->param_type, cardinality); + "Setting array param descriptor fields: param_num=%d, param_type=%d, cardinality=%lld", + curr->param_num, curr->param_type, (long long)cardinality); LogMsg(DEBUG, messageStr); rc = SQLGetStmtAttr(stmt_res->hstmt, SQL_ATTR_IMP_PARAM_DESC, &hIPD, 0, NULL); @@ -546,25 +546,43 @@ static void _python_ibm_db_set_array_param_cardinality(stmt_handle *stmt_res, if (curr->param_type == SQL_PARAM_INPUT && hIPD != NULL) { rc = SQLSetDescField(hIPD, curr->param_num, SQL_DESC_CARDINALITY, - (SQLPOINTER)(intptr_t)cardinality, SQL_IS_SMALLINT); + (SQLPOINTER)(intptr_t)cardinality, SQL_IS_INTEGER); if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { LogMsg(ERROR, "Failed to set SQL_DESC_CARDINALITY on hIPD(INPUT)"); } else { LogMsg(DEBUG, "Set SQL_DESC_CARDINALITY on hIPD(INPUT)"); } - rc = SQLSetDescField(hAPD, curr->param_num, SQL_DESC_CARDINALITY_PTR, + rc = SQLSetDescField(hIPD, curr->param_num, SQL_DESC_CARDINALITY_PTR, (SQLPOINTER)&curr->actual_cardinality, SQL_IS_POINTER); if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { - LogMsg(ERROR, "Failed to set SQL_DESC_CARDINALITY_PTR on hAPD(INPUT)"); + LogMsg(ERROR, "Failed to set SQL_DESC_CARDINALITY_PTR on hIPD(INPUT)"); } else { - LogMsg(DEBUG, "Set SQL_DESC_CARDINALITY_PTR on hAPD(INPUT)"); + LogMsg(DEBUG, "Set SQL_DESC_CARDINALITY_PTR on hIPD(INPUT)"); + } + + if (hAPD != NULL) { + rc = SQLSetDescField(hAPD, curr->param_num, SQL_DESC_CARDINALITY, + (SQLPOINTER)(intptr_t)cardinality, SQL_IS_INTEGER); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + LogMsg(ERROR, "Failed to set SQL_DESC_CARDINALITY on hAPD(INPUT)"); + } else { + LogMsg(DEBUG, "Set SQL_DESC_CARDINALITY on hAPD(INPUT)"); + } + + rc = SQLSetDescField(hAPD, curr->param_num, SQL_DESC_CARDINALITY_PTR, + (SQLPOINTER)&curr->actual_cardinality, SQL_IS_POINTER); + if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { + LogMsg(ERROR, "Failed to set SQL_DESC_CARDINALITY_PTR on hAPD(INPUT)"); + } else { + LogMsg(DEBUG, "Set SQL_DESC_CARDINALITY_PTR on hAPD(INPUT)"); + } } } else if (curr->param_type == SQL_PARAM_OUTPUT && hAPD != NULL) { rc = SQLSetDescField(hAPD, curr->param_num, SQL_DESC_CARDINALITY, - (SQLPOINTER)(intptr_t)cardinality, SQL_IS_SMALLINT); + (SQLPOINTER)(intptr_t)cardinality, SQL_IS_INTEGER); if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { LogMsg(ERROR, "Failed to set SQL_DESC_CARDINALITY on hAPD(OUTPUT)"); } else { @@ -583,7 +601,7 @@ static void _python_ibm_db_set_array_param_cardinality(stmt_handle *stmt_res, else if (curr->param_type == SQL_PARAM_INPUT_OUTPUT) { if (hIPD != NULL) { rc = SQLSetDescField(hIPD, curr->param_num, SQL_DESC_CARDINALITY, - (SQLPOINTER)(intptr_t)cardinality, SQL_IS_SMALLINT); + (SQLPOINTER)(intptr_t)cardinality, SQL_IS_INTEGER); if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { LogMsg(ERROR, "Failed to set SQL_DESC_CARDINALITY on hIPD(INOUT)"); } else { @@ -601,7 +619,7 @@ static void _python_ibm_db_set_array_param_cardinality(stmt_handle *stmt_res, if (hAPD != NULL) { rc = SQLSetDescField(hAPD, curr->param_num, SQL_DESC_CARDINALITY, - (SQLPOINTER)(intptr_t)cardinality, SQL_IS_SMALLINT); + (SQLPOINTER)(intptr_t)cardinality, SQL_IS_INTEGER); if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) { LogMsg(ERROR, "Failed to set SQL_DESC_CARDINALITY on hAPD(INOUT)"); } else { @@ -9235,13 +9253,13 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO #ifndef __MVS__ if (stmt_res->is_stored_procedure) { snprintf(messageStr, sizeof(messageStr), - "Before set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%d, actual_cardinality=%d, n(array size)=%d", - curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); + "Before set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%lld, actual_cardinality=%lld, n(array size)=%d", + curr->param_num, curr->param_type, (long long)curr->cardinality, (long long)curr->actual_cardinality, (int)n); LogMsg(DEBUG, messageStr); - _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLSMALLINT)n); + _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLLEN)n); snprintf(messageStr, sizeof(messageStr), - "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%d, actual_cardinality=%d", - curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality); + "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%lld, actual_cardinality=%lld", + curr->param_num, curr->param_type, (long long)curr->cardinality, (long long)curr->actual_cardinality); LogMsg(DEBUG, messageStr); } #endif @@ -9320,13 +9338,13 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO #ifndef __MVS__ if (stmt_res->is_stored_procedure) { snprintf(messageStr, sizeof(messageStr), - "Before set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%d, actual_cardinality=%d, n(array size)=%d", - curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); + "Before set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%lld, actual_cardinality=%lld, n(array size)=%d", + curr->param_num, curr->param_type, (long long)curr->cardinality, (long long)curr->actual_cardinality, (int)n); LogMsg(DEBUG, messageStr); - _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLSMALLINT)n); + _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLLEN)n); snprintf(messageStr, sizeof(messageStr), - "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%d, actual_cardinality=%d", - curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality); + "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%lld, actual_cardinality=%lld", + curr->param_num, curr->param_type, (long long)curr->cardinality, (long long)curr->actual_cardinality); LogMsg(DEBUG, messageStr); } #endif @@ -9399,13 +9417,13 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO #ifndef __MVS__ if (stmt_res->is_stored_procedure) { snprintf(messageStr, sizeof(messageStr),"Before set_array_param_cardinality: param_num=%d, param_type=%d," - " cardinality=%d, actual_cardinality=%d, n(array size)=%d", - curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); + " cardinality=%lld, actual_cardinality=%lld, n(array size)=%d", + curr->param_num, curr->param_type, (long long)curr->cardinality, (long long)curr->actual_cardinality, (int)n); LogMsg(DEBUG, messageStr); - _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLSMALLINT)n); + _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLLEN)n); snprintf(messageStr, sizeof(messageStr), - "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%d, actual_cardinality=%d", - curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality); + "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%lld, actual_cardinality=%lld", + curr->param_num, curr->param_type, (long long)curr->cardinality, (long long)curr->actual_cardinality); LogMsg(DEBUG, messageStr); } #endif @@ -9481,13 +9499,13 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO #ifndef __MVS__ if (stmt_res->is_stored_procedure) { snprintf(messageStr, sizeof(messageStr),"Before set_array_param_cardinality: param_num=%d, param_type=%d," - " cardinality=%d, actual_cardinality=%d, n(array size)=%d", - curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); + " cardinality=%lld, actual_cardinality=%lld, n(array size)=%d", + curr->param_num, curr->param_type, (long long)curr->cardinality, (long long)curr->actual_cardinality, (int)n); LogMsg(DEBUG, messageStr); - _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLSMALLINT)n); + _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLLEN)n); snprintf(messageStr, sizeof(messageStr), - "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%d, actual_cardinality=%d", - curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality); + "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%lld, actual_cardinality=%lld", + curr->param_num, curr->param_type, (long long)curr->cardinality, (long long)curr->actual_cardinality); LogMsg(DEBUG, messageStr); } #endif @@ -9560,13 +9578,13 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO #ifndef __MVS__ if (stmt_res->is_stored_procedure) { snprintf(messageStr, sizeof(messageStr),"Before set_array_param_cardinality: param_num=%d, param_type=%d," - " cardinality=%d, actual_cardinality=%d, n(array size)=%d", - curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); + " cardinality=%lld, actual_cardinality=%lld, n(array size)=%d", + curr->param_num, curr->param_type, (long long)curr->cardinality, (long long)curr->actual_cardinality, (int)n); LogMsg(DEBUG, messageStr); - _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLSMALLINT)n); + _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLLEN)n); snprintf(messageStr, sizeof(messageStr), - "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%d, actual_cardinality=%d", - curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality); + "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%lld, actual_cardinality=%lld", + curr->param_num, curr->param_type, (long long)curr->cardinality, (long long)curr->actual_cardinality); LogMsg(DEBUG, messageStr); } #endif @@ -9920,13 +9938,13 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO #ifndef __MVS__ if (stmt_res->is_stored_procedure) { snprintf(messageStr, sizeof(messageStr),"Before set_array_param_cardinality: param_num=%d, param_type=%d," - " cardinality=%d, actual_cardinality=%d, n(array size)=%d", - curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); + " cardinality=%lld, actual_cardinality=%lld, n(array size)=%d", + curr->param_num, curr->param_type, (long long)curr->cardinality, (long long)curr->actual_cardinality, (int)n); LogMsg(DEBUG, messageStr); - _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLSMALLINT)n); + _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLLEN)n); snprintf(messageStr, sizeof(messageStr), - "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%d, actual_cardinality=%d", - curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality); + "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%lld, actual_cardinality=%lld", + curr->param_num, curr->param_type, (long long)curr->cardinality, (long long)curr->actual_cardinality); LogMsg(DEBUG, messageStr); } #endif @@ -10500,13 +10518,13 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO #ifndef __MVS__ if (stmt_res->is_stored_procedure) { snprintf(messageStr, sizeof(messageStr),"Before set_array_param_cardinality: param_num=%d, param_type=%d," - " cardinality=%d, actual_cardinality=%d, n(array size)=%d", - curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); + " cardinality=%lld, actual_cardinality=%lld, n(array size)=%d", + curr->param_num, curr->param_type, (long long)curr->cardinality, (long long)curr->actual_cardinality, (int)n); LogMsg(DEBUG, messageStr); - _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLSMALLINT)n); + _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLLEN)n); snprintf(messageStr, sizeof(messageStr), - "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%d, actual_cardinality=%d", - curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality); + "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%lld, actual_cardinality=%lld", + curr->param_num, curr->param_type, (long long)curr->cardinality, (long long)curr->actual_cardinality); LogMsg(DEBUG, messageStr); } #endif @@ -10801,13 +10819,13 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO #ifndef __MVS__ if (stmt_res->is_stored_procedure) { snprintf(messageStr, sizeof(messageStr),"Before set_array_param_cardinality: param_num=%d, param_type=%d," - " cardinality=%d, actual_cardinality=%d, n(array size)=%d", - curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); + " cardinality=%lld, actual_cardinality=%lld, n(array size)=%d", + curr->param_num, curr->param_type, (long long)curr->cardinality, (long long)curr->actual_cardinality, (int)n); LogMsg(DEBUG, messageStr); - _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLSMALLINT)n); + _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLLEN)n); snprintf(messageStr, sizeof(messageStr), - "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%d, actual_cardinality=%d", - curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality); + "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%lld, actual_cardinality=%lld", + curr->param_num, curr->param_type, (long long)curr->cardinality, (long long)curr->actual_cardinality); LogMsg(DEBUG, messageStr); } #endif @@ -10918,13 +10936,13 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO #ifndef __MVS__ if (stmt_res->is_stored_procedure) { snprintf(messageStr, sizeof(messageStr),"Before set_array_param_cardinality: param_num=%d, param_type=%d," - " cardinality=%d, actual_cardinality=%d, n(array size)=%d", - curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); + " cardinality=%lld, actual_cardinality=%lld, n(array size)=%d", + curr->param_num, curr->param_type, (long long)curr->cardinality, (long long)curr->actual_cardinality, (int)n); LogMsg(DEBUG, messageStr); - _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLSMALLINT)n); + _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLLEN)n); snprintf(messageStr, sizeof(messageStr), - "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%d, actual_cardinality=%d", - curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality); + "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%lld, actual_cardinality=%lld", + curr->param_num, curr->param_type, (long long)curr->cardinality, (long long)curr->actual_cardinality); LogMsg(DEBUG, messageStr); } #endif @@ -10994,13 +11012,13 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO #ifndef __MVS__ if (stmt_res->is_stored_procedure) { snprintf(messageStr, sizeof(messageStr),"Before set_array_param_cardinality: param_num=%d, param_type=%d," - " cardinality=%d, actual_cardinality=%d, n(array size)=%d", - curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); + " cardinality=%lld, actual_cardinality=%lld, n(array size)=%d", + curr->param_num, curr->param_type, (long long)curr->cardinality, (long long)curr->actual_cardinality, (int)n); LogMsg(DEBUG, messageStr); - _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLSMALLINT)n); + _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLLEN)n); snprintf(messageStr, sizeof(messageStr), - "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%d, actual_cardinality=%d", - curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality); + "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%lld, actual_cardinality=%lld", + curr->param_num, curr->param_type, (long long)curr->cardinality, (long long)curr->actual_cardinality); LogMsg(DEBUG, messageStr); } #endif @@ -11078,13 +11096,13 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO #ifndef __MVS__ if (stmt_res->is_stored_procedure) { snprintf(messageStr, sizeof(messageStr),"Before set_array_param_cardinality: param_num=%d, param_type=%d," - " cardinality=%d, actual_cardinality=%d, n(array size)=%d", - curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); + " cardinality=%lld, actual_cardinality=%lld, n(array size)=%d", + curr->param_num, curr->param_type, (long long)curr->cardinality, (long long)curr->actual_cardinality, (int)n); LogMsg(DEBUG, messageStr); - _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLSMALLINT)n); + _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLLEN)n); snprintf(messageStr, sizeof(messageStr), - "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%d, actual_cardinality=%d", - curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality); + "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%lld, actual_cardinality=%lld", + curr->param_num, curr->param_type, (long long)curr->cardinality, (long long)curr->actual_cardinality); LogMsg(DEBUG, messageStr); } #endif @@ -11209,13 +11227,13 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO #ifndef __MVS__ if (stmt_res->is_stored_procedure) { snprintf(messageStr, sizeof(messageStr),"Before set_array_param_cardinality: param_num=%d, param_type=%d," - " cardinality=%d, actual_cardinality=%d, n(array size)=%d", - curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); + " cardinality=%lld, actual_cardinality=%lld, n(array size)=%d", + curr->param_num, curr->param_type, (long long)curr->cardinality, (long long)curr->actual_cardinality, (int)n); LogMsg(DEBUG, messageStr); - _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLSMALLINT)n); + _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLLEN)n); snprintf(messageStr, sizeof(messageStr), - "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%d, actual_cardinality=%d", - curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality); + "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%lld, actual_cardinality=%lld", + curr->param_num, curr->param_type, (long long)curr->cardinality, (long long)curr->actual_cardinality); LogMsg(DEBUG, messageStr); } #endif @@ -11329,13 +11347,13 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO #ifndef __MVS__ if (stmt_res->is_stored_procedure) { snprintf(messageStr, sizeof(messageStr),"Before set_array_param_cardinality: param_num=%d, param_type=%d," - " cardinality=%d, actual_cardinality=%d, n(array size)=%d", - curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); + " cardinality=%lld, actual_cardinality=%lld, n(array size)=%d", + curr->param_num, curr->param_type, (long long)curr->cardinality, (long long)curr->actual_cardinality, (int)n); LogMsg(DEBUG, messageStr); - _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLSMALLINT)n); + _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLLEN)n); snprintf(messageStr, sizeof(messageStr), - "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%d, actual_cardinality=%d", - curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality); + "After set_array_param_cardinality: param_num=%d, param_type=%d, cardinality=%lld, actual_cardinality=%lld", + curr->param_num, curr->param_type, (long long)curr->cardinality, (long long)curr->actual_cardinality); LogMsg(DEBUG, messageStr); } #endif @@ -19958,9 +19976,13 @@ static PyObject* ibm_db_fetch_callproc(PyObject* self, PyObject* args) int idx = 1; while (curr && idx <= numOfParam) { - curr->cardinality = curr->actual_cardinality; - snprintf(messageStr, sizeof(messageStr), "Processing parameter %d (param_type=%d, data_type=%d, cardinality=%d, bind_indicator=%d)", - curr->param_num, curr->param_type, curr->data_type, curr->cardinality, curr->bind_indicator); + /* Keep the bound cardinality for array detection, but use the driver's + * actual cardinality below so a shorter output is never padded. */ + if (curr->actual_cardinality > 0 && curr->actual_cardinality > curr->cardinality) { + curr->cardinality = curr->actual_cardinality; + } + snprintf(messageStr, sizeof(messageStr), "Processing parameter %d (param_type=%d, data_type=%d, cardinality=%lld, bind_indicator=%d)", + curr->param_num, curr->param_type, curr->data_type, (long long)curr->cardinality, curr->bind_indicator); LogMsg(DEBUG, messageStr); PyObject *pyVal = Py_None; Py_INCREF(pyVal); @@ -19992,8 +20014,28 @@ static PyObject* ibm_db_fetch_callproc(PyObject* self, PyObject* args) if (!skip_param) { - if (curr->cardinality > 1 && curr->actual_cardinality > 0) { - int len = curr->actual_cardinality; + int is_list_bound = curr->var_pyvalue && PyList_Check(curr->var_pyvalue); + if (curr->cardinality > 1 || is_list_bound) { + snprintf(messageStr, sizeof(messageStr), + "Array output param %d: cardinality=%lld, actual_cardinality=%lld, is_list_bound=%d", + curr->param_num, (long long)curr->cardinality, (long long)curr->actual_cardinality, is_list_bound); + LogMsg(DEBUG, messageStr); + if (curr->actual_cardinality < 0) { + /* Negative actual_cardinality signals the whole array is NULL/unknown; + * do not pad it out to the bound size with stale buffer contents. */ + LogMsg(DEBUG, "Array output param: actual_cardinality < 0, returning None"); + Py_DECREF(pyVal); + pyVal = Py_None; + Py_INCREF(pyVal); + PyTuple_SET_ITEM(outTuple, idx++, pyVal); + curr = curr->next; + continue; + } + /* Zero is a valid driver-reported cardinality for an empty array. */ + SQLLEN len = curr->actual_cardinality; + snprintf(messageStr, sizeof(messageStr), + "Array output param %d: resolved output list length=%lld", curr->param_num, (long long)len); + LogMsg(DEBUG, messageStr); PyObject *pyList = PyList_New(len); if (!pyList) { Py_DECREF(pyVal); diff --git a/ibm_db_tests/test_sparray_cardinality_regressions.py b/ibm_db_tests/test_sparray_cardinality_regressions.py new file mode 100644 index 00000000..e7ee2c21 --- /dev/null +++ b/ibm_db_tests/test_sparray_cardinality_regressions.py @@ -0,0 +1,153 @@ +from __future__ import print_function +import unittest +import ibm_db +import config +from testfunctions import IbmDbTestFunctions + +class IbmDbTestCase(unittest.TestCase): + + def test_sparray_cardinality_regressions(self): + obj = IbmDbTestFunctions() + obj.assert_expect(self.run_test_sparray_cardinality_regressions) + + def run_test_sparray_cardinality_regressions(self): + conn = ibm_db.connect(config.database, config.user, config.password) + if not conn: + print("no connection") + return + + cleanup = ( + "DROP PROCEDURE REGRESSION_ARRAY_LARGE_INPUT", + "DROP PROCEDURE REGRESSION_ARRAY_PARTIAL_OUT", + "DROP PROCEDURE REGRESSION_ARRAY_EMPTY_OUT", + "DROP PROCEDURE REGRESSION_ARRAY_NULL_OUT", + "DROP TYPE REGRESSION_LARGE_ARRAY", + "DROP TYPE REGRESSION_SMALL_ARRAY", + ) + for statement in cleanup: + try: + ibm_db.exec_immediate(conn, statement) + except Exception: + pass + + # Cardinality above SQLSMALLINT range must not be truncated. + ibm_db.exec_immediate( + conn, "CREATE TYPE REGRESSION_LARGE_ARRAY AS INTEGER ARRAY[32768]") + ibm_db.exec_immediate(conn, """ + CREATE PROCEDURE REGRESSION_ARRAY_LARGE_INPUT( + IN var1 REGRESSION_LARGE_ARRAY, + OUT var2 INTEGER + ) + LANGUAGE SQL + BEGIN + SET var2 = CARDINALITY(var1); + END + """) + stmt = ibm_db.prepare(conn, "CALL REGRESSION_ARRAY_LARGE_INPUT(?, ?)") + ibm_db.bind_param(stmt, 1, list(range(32768)), ibm_db.SQL_PARAM_INPUT) + ibm_db.bind_param(stmt, 2, 0, ibm_db.SQL_PARAM_OUTPUT, ibm_db.SQL_INTEGER) + ibm_db.execute(stmt) + result = ibm_db.fetch_callproc(stmt) + print("Large input cardinality:", result[2]) + + # Driver reports fewer elements than were bound. + ibm_db.exec_immediate( + conn, "CREATE TYPE REGRESSION_SMALL_ARRAY AS INTEGER ARRAY[10]") + ibm_db.exec_immediate(conn, """ + CREATE PROCEDURE REGRESSION_ARRAY_PARTIAL_OUT( + OUT var1 REGRESSION_SMALL_ARRAY + ) + LANGUAGE SQL + BEGIN + SET var1[1] = 111; + SET var1[2] = 222; + SET var1[3] = 333; + END + """) + stmt = ibm_db.prepare(conn, "CALL REGRESSION_ARRAY_PARTIAL_OUT(?)") + ibm_db.bind_param(stmt, 1, [999] * 10, ibm_db.SQL_PARAM_OUTPUT) + ibm_db.execute(stmt) + result = ibm_db.fetch_callproc(stmt) + print("Partial output array:", result[1]) + + # Unassigned OUT array comes back as a NULL array. + ibm_db.exec_immediate(conn, """ + CREATE PROCEDURE REGRESSION_ARRAY_NULL_OUT( + OUT var1 REGRESSION_SMALL_ARRAY + ) + LANGUAGE SQL + BEGIN + DECLARE unused INTEGER DEFAULT 0; + SET unused = 1; + END + """) + stmt = ibm_db.prepare(conn, "CALL REGRESSION_ARRAY_NULL_OUT(?)") + ibm_db.bind_param(stmt, 1, [999] * 10, ibm_db.SQL_PARAM_OUTPUT) + ibm_db.execute(stmt) + result = ibm_db.fetch_callproc(stmt) + print("Null output array:", result[1]) + + ibm_db.exec_immediate(conn, "DROP PROCEDURE REGRESSION_ARRAY_PARTIAL_OUT") + ibm_db.exec_immediate(conn, "DROP PROCEDURE REGRESSION_ARRAY_NULL_OUT") + ibm_db.exec_immediate(conn, "DROP TYPE REGRESSION_SMALL_ARRAY") + + # Empty OUT array reported as cardinality 0. + ibm_db.exec_immediate( + conn, "CREATE TYPE REGRESSION_SMALL_ARRAY AS INTEGER ARRAY[2]") + ibm_db.exec_immediate(conn, """ + CREATE PROCEDURE REGRESSION_ARRAY_EMPTY_OUT( + OUT var1 REGRESSION_SMALL_ARRAY + ) + LANGUAGE SQL + BEGIN + SET var1 = CAST(ARRAY[] AS REGRESSION_SMALL_ARRAY); + END + """) + stmt = ibm_db.prepare(conn, "CALL REGRESSION_ARRAY_EMPTY_OUT(?)") + ibm_db.bind_param(stmt, 1, [999, 999], ibm_db.SQL_PARAM_OUTPUT) + ibm_db.execute(stmt) + result = ibm_db.fetch_callproc(stmt) + print("Empty output array:", result[1]) + + ibm_db.exec_immediate(conn, "DROP PROCEDURE REGRESSION_ARRAY_EMPTY_OUT") + ibm_db.exec_immediate(conn, "DROP TYPE REGRESSION_SMALL_ARRAY") + + # Declared cardinality of 1 still honours a shrink to zero. + ibm_db.exec_immediate( + conn, "CREATE TYPE REGRESSION_SMALL_ARRAY AS INTEGER ARRAY[1]") + ibm_db.exec_immediate(conn, """ + CREATE PROCEDURE REGRESSION_ARRAY_EMPTY_OUT( + OUT var1 REGRESSION_SMALL_ARRAY + ) + LANGUAGE SQL + BEGIN + SET var1 = CAST(ARRAY[] AS REGRESSION_SMALL_ARRAY); + END + """) + stmt = ibm_db.prepare(conn, "CALL REGRESSION_ARRAY_EMPTY_OUT(?)") + ibm_db.bind_param(stmt, 1, [999], ibm_db.SQL_PARAM_OUTPUT) + ibm_db.execute(stmt) + result = ibm_db.fetch_callproc(stmt) + print("Single cardinality output array:", result[1]) + + for statement in cleanup: + try: + ibm_db.exec_immediate(conn, statement) + except Exception: + pass + ibm_db.close(conn) + +#__END__ +#__LUW_EXPECTED__ +#Large input cardinality: 32768 +#Partial output array: [111, 222, 333] +#Null output array: None +#Empty output array: [] +#Single cardinality output array: [] +#__ZOS_EXPECTED__ +#... same as LUW ... +#__SYSTEMI_EXPECTED__ +#... same as LUW ... +#__IDS_EXPECTED__ +#... same as LUW ... + diff --git a/setup.py b/setup.py index 56004a06..e6a1aa15 100644 --- a/setup.py +++ b/setup.py @@ -560,6 +560,7 @@ def run(self): 'Programming Language :: Python :: 3.12', 'Programming Language :: Python :: 3.13', 'Programming Language :: Python :: 3.14', + 'Programming Language :: Python :: 3.15', 'Topic :: Database :: Front-Ends'], long_description = open(readme).read(),