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..32e313a2 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; @@ -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, (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); @@ -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, (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); @@ -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, (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); @@ -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, (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); @@ -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, (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); @@ -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, (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); @@ -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, (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); @@ -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, (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); @@ -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, (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); @@ -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, (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); @@ -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, (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); @@ -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, (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); @@ -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, (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); @@ -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,34 @@ 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; + } + /* 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); 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(),