From 315da00ace4336fd3cf1f344501e201b3539edd6 Mon Sep 17 00:00:00 2001 From: Earamma K Date: Thu, 10 Sep 2026 22:31:13 +0530 Subject: [PATCH 1/5] Add Python 3.15 wheel support and resolve SPARRAY tests on zLinux/s390x Signed-off-by: Earamma K --- .github/workflows/bld_wheels_and_upload.yml | 23 +++--- ibm_db.c | 79 ++++++++++++++------- 2 files changed, 66 insertions(+), 36 deletions(-) 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..4c752938 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; + SQLINTEGER cardinality; + SQLINTEGER 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) + SQLINTEGER cardinality) { SQLHDESC hIPD = (SQLHDESC)0; @@ -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 { @@ -9238,7 +9256,7 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO "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); 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, (SQLINTEGER)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); @@ -9323,7 +9341,7 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO "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); 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, (SQLINTEGER)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); @@ -9402,7 +9420,7 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO " cardinality=%d, actual_cardinality=%d, n(array size)=%d", curr->param_num, curr->param_type, curr->cardinality, 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, (SQLINTEGER)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); @@ -9484,7 +9502,7 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO " cardinality=%d, actual_cardinality=%d, n(array size)=%d", curr->param_num, curr->param_type, curr->cardinality, 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, (SQLINTEGER)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); @@ -9563,7 +9581,7 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO " cardinality=%d, actual_cardinality=%d, n(array size)=%d", curr->param_num, curr->param_type, curr->cardinality, 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, (SQLINTEGER)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); @@ -9923,7 +9941,7 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO " cardinality=%d, actual_cardinality=%d, n(array size)=%d", curr->param_num, curr->param_type, curr->cardinality, 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, (SQLINTEGER)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); @@ -10503,7 +10521,7 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO " cardinality=%d, actual_cardinality=%d, n(array size)=%d", curr->param_num, curr->param_type, curr->cardinality, 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, (SQLINTEGER)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); @@ -10804,7 +10822,7 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO " cardinality=%d, actual_cardinality=%d, n(array size)=%d", curr->param_num, curr->param_type, curr->cardinality, 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, (SQLINTEGER)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); @@ -10921,7 +10939,7 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO " cardinality=%d, actual_cardinality=%d, n(array size)=%d", curr->param_num, curr->param_type, curr->cardinality, 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, (SQLINTEGER)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); @@ -10997,7 +11015,7 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO " cardinality=%d, actual_cardinality=%d, n(array size)=%d", curr->param_num, curr->param_type, curr->cardinality, 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, (SQLINTEGER)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); @@ -11081,7 +11099,7 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO " cardinality=%d, actual_cardinality=%d, n(array size)=%d", curr->param_num, curr->param_type, curr->cardinality, 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, (SQLINTEGER)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); @@ -11212,7 +11230,7 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO " cardinality=%d, actual_cardinality=%d, n(array size)=%d", curr->param_num, curr->param_type, curr->cardinality, 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, (SQLINTEGER)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); @@ -11332,7 +11350,7 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO " cardinality=%d, actual_cardinality=%d, n(array size)=%d", curr->param_num, curr->param_type, curr->cardinality, 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, (SQLINTEGER)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); @@ -19958,7 +19976,9 @@ static PyObject* ibm_db_fetch_callproc(PyObject* self, PyObject* args) int idx = 1; while (curr && idx <= numOfParam) { - curr->cardinality = curr->actual_cardinality; + 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=%d, bind_indicator=%d)", curr->param_num, curr->param_type, curr->data_type, curr->cardinality, curr->bind_indicator); LogMsg(DEBUG, messageStr); @@ -19992,8 +20012,17 @@ static PyObject* ibm_db_fetch_callproc(PyObject* self, PyObject* args) if (!skip_param) { - if (curr->cardinality > 1 && curr->actual_cardinality > 0) { + if (curr->cardinality > 1 || (curr->var_pyvalue && PyList_Check(curr->var_pyvalue))) { int len = curr->actual_cardinality; + if (curr->var_pyvalue && PyList_Check(curr->var_pyvalue)) { + Py_ssize_t py_len = PyList_Size(curr->var_pyvalue); + if (py_len > len) { + len = (int)py_len; + } + } + if (len <= 0) { + len = curr->cardinality; + } PyObject *pyList = PyList_New(len); if (!pyList) { Py_DECREF(pyVal); From 303024a1b392c944dbfcdd522287c7c434d09b31 Mon Sep 17 00:00:00 2001 From: Earamma K Date: Fri, 11 Sep 2026 10:25:19 +0530 Subject: [PATCH 2/5] Add Python 3.15 wheel support and resolve SPARRAY tests on zLinux/s390x Signed-off-by: Earamma K --- setup.py | 1 + 1 file changed, 1 insertion(+) 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(), From 83b72da995c4f7c1b3a3cde6d634dc6966bdbbd0 Mon Sep 17 00:00:00 2001 From: Earamma K Date: Thu, 17 Sep 2026 12:41:14 +0530 Subject: [PATCH 3/5] Use SQLLEN instead of SQLINTEGER for array cardinality --- ibm_db.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/ibm_db.c b/ibm_db.c index 4c752938..c3453aa1 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 */ - SQLINTEGER cardinality; - SQLINTEGER 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, - SQLINTEGER cardinality) + SQLLEN cardinality) { SQLHDESC hIPD = (SQLHDESC)0; @@ -9256,7 +9256,7 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO "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); LogMsg(DEBUG, messageStr); - _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLINTEGER)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); @@ -9341,7 +9341,7 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO "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); LogMsg(DEBUG, messageStr); - _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLINTEGER)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); @@ -9420,7 +9420,7 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO " cardinality=%d, actual_cardinality=%d, n(array size)=%d", curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); LogMsg(DEBUG, messageStr); - _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLINTEGER)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); @@ -9502,7 +9502,7 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO " cardinality=%d, actual_cardinality=%d, n(array size)=%d", curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); LogMsg(DEBUG, messageStr); - _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLINTEGER)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); @@ -9581,7 +9581,7 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO " cardinality=%d, actual_cardinality=%d, n(array size)=%d", curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); LogMsg(DEBUG, messageStr); - _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLINTEGER)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); @@ -9941,7 +9941,7 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO " cardinality=%d, actual_cardinality=%d, n(array size)=%d", curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); LogMsg(DEBUG, messageStr); - _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLINTEGER)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); @@ -10521,7 +10521,7 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO " cardinality=%d, actual_cardinality=%d, n(array size)=%d", curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); LogMsg(DEBUG, messageStr); - _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLINTEGER)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); @@ -10822,7 +10822,7 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO " cardinality=%d, actual_cardinality=%d, n(array size)=%d", curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); LogMsg(DEBUG, messageStr); - _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLINTEGER)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); @@ -10939,7 +10939,7 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO " cardinality=%d, actual_cardinality=%d, n(array size)=%d", curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); LogMsg(DEBUG, messageStr); - _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLINTEGER)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); @@ -11015,7 +11015,7 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO " cardinality=%d, actual_cardinality=%d, n(array size)=%d", curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); LogMsg(DEBUG, messageStr); - _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLINTEGER)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); @@ -11099,7 +11099,7 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO " cardinality=%d, actual_cardinality=%d, n(array size)=%d", curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); LogMsg(DEBUG, messageStr); - _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLINTEGER)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); @@ -11230,7 +11230,7 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO " cardinality=%d, actual_cardinality=%d, n(array size)=%d", curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); LogMsg(DEBUG, messageStr); - _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLINTEGER)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); @@ -11350,7 +11350,7 @@ static int _python_ibm_db_bind_data(stmt_handle *stmt_res, param_node *curr, PyO " cardinality=%d, actual_cardinality=%d, n(array size)=%d", curr->param_num, curr->param_type, curr->cardinality, curr->actual_cardinality, (int)n); LogMsg(DEBUG, messageStr); - _python_ibm_db_set_array_param_cardinality(stmt_res, curr, (SQLINTEGER)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); @@ -20013,11 +20013,11 @@ static PyObject* ibm_db_fetch_callproc(PyObject* self, PyObject* args) if (!skip_param) { if (curr->cardinality > 1 || (curr->var_pyvalue && PyList_Check(curr->var_pyvalue))) { - int len = curr->actual_cardinality; + SQLLEN len = curr->actual_cardinality; if (curr->var_pyvalue && PyList_Check(curr->var_pyvalue)) { Py_ssize_t py_len = PyList_Size(curr->var_pyvalue); if (py_len > len) { - len = (int)py_len; + len = py_len; } } if (len <= 0) { From 58582c88d5e04f47323ef8187b8c9841c3bb2755 Mon Sep 17 00:00:00 2001 From: Earamma K Date: Tue, 22 Sep 2026 12:52:14 +0530 Subject: [PATCH 4/5] Fix array output cardinality handling in fetch_callproc --- ibm_db.c | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/ibm_db.c b/ibm_db.c index c3453aa1..32e313a2 100644 --- a/ibm_db.c +++ b/ibm_db.c @@ -20012,17 +20012,34 @@ static PyObject* ibm_db_fetch_callproc(PyObject* self, PyObject* args) if (!skip_param) { - if (curr->cardinality > 1 || (curr->var_pyvalue && PyList_Check(curr->var_pyvalue))) { - SQLLEN len = curr->actual_cardinality; - if (curr->var_pyvalue && PyList_Check(curr->var_pyvalue)) { - Py_ssize_t py_len = PyList_Size(curr->var_pyvalue); - if (py_len > len) { - len = py_len; - } - } - if (len <= 0) { - len = curr->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; } + /* Use the driver's reported count as the list length, even if it + * shrank from the bound size. Only fall back to the bound list's + * own length when the declared cardinality is <= 1 (not set up as + * a real array) but the Python side still bound a list to it, and + * the driver gave no usable count back. */ + SQLLEN len = (curr->actual_cardinality == 0 && curr->cardinality <= 1 && is_list_bound) + ? PyList_Size(curr->var_pyvalue) + : 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); From 1a134827f810f4055b6815fd9338f09817abeebd Mon Sep 17 00:00:00 2001 From: Earamma K Date: Wed, 30 Sep 2026 21:40:39 +0530 Subject: [PATCH 5/5] Fix SPARRAY cardinality handling and add regression coverage for the review feedback. --- ibm_db.c | 124 +++++++------- .../test_sparray_cardinality_regressions.py | 153 ++++++++++++++++++ 2 files changed, 213 insertions(+), 64 deletions(-) create mode 100644 ibm_db_tests/test_sparray_cardinality_regressions.py diff --git a/ibm_db.c b/ibm_db.c index 32e313a2..72a37d45 100644 --- a/ibm_db.c +++ b/ibm_db.c @@ -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); @@ -9253,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, (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 @@ -9338,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, (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 @@ -9417,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, (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 @@ -9499,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, (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 @@ -9578,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, (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 @@ -9938,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, (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 @@ -10518,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, (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 @@ -10819,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, (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 @@ -10936,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, (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 @@ -11012,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, (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 @@ -11096,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, (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 @@ -11227,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, (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 @@ -11347,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, (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 @@ -19976,11 +19976,13 @@ static PyObject* ibm_db_fetch_callproc(PyObject* self, PyObject* args) int idx = 1; while (curr && idx <= numOfParam) { + /* 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=%d, bind_indicator=%d)", - curr->param_num, curr->param_type, curr->data_type, curr->cardinality, curr->bind_indicator); + 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); @@ -20029,14 +20031,8 @@ static PyObject* ibm_db_fetch_callproc(PyObject* self, PyObject* args) curr = curr->next; continue; } - /* Use the driver's reported count as the list length, even if it - * shrank from the bound size. Only fall back to the bound list's - * own length when the declared cardinality is <= 1 (not set up as - * a real array) but the Python side still bound a list to it, and - * the driver gave no usable count back. */ - SQLLEN len = (curr->actual_cardinality == 0 && curr->cardinality <= 1 && is_list_bound) - ? PyList_Size(curr->var_pyvalue) - : curr->actual_cardinality; + /* 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); 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 ... +