Return the cumulative row count after a parameter-array execute - #311
Open
fdcastel wants to merge 7 commits into
Open
Return the cumulative row count after a parameter-array execute#311fdcastel wants to merge 7 commits into
fdcastel wants to merge 7 commits into
Conversation
Eleven ArrayBindingTest cases were skipped citing a crash in the column-wise indicator offset. That crash was fixed in 0a7cc1f (SQLINTEGER to SQLLEN), so the skips come out. ParamOperationPtrSkipRows stays skipped because SQL_ATTR_PARAM_OPERATION_PTR is still not honoured, and the reactivated cases get the Firebird 6 guard the row-wise ones already have. Two new cases bind fixed-length C types with BufferLength = 0, the usage the ODBC specification describes for those types: one with the shape reported in FirebirdSQL#299 (INTEGER and BIGINT with a NULL in the third parameter set), one across DOUBLE, DATE and TIMESTAMP.
…h is 0 SQLBindParameter stores BufferLength as the element stride of a column-wise parameter array. The ODBC specification ignores BufferLength for fixed-length C types and applications pass 0 there, so every parameter set read element 0: the driver inserted N rows carrying the first set's values and reported SQL_SUCCESS. bindInputOutputParam now falls back to the C type size when the stride is 0, the way bindOutputColumn already does for SQLBindCol. getConciseSize gains SQL_C_GUID, for which it returned the type code (-11) and would have produced a negative stride. Character and binary C types keep BufferLength as their stride, so string arrays behave as before. Fixes FirebirdSQL#299
…fter SQLPrepare The executor is chosen at prepare time from the paramset size in force at that moment. An application that prepares first and sets SQL_ATTR_PARAMSET_SIZE afterwards, which the specification allows and which pyodbc's fast_executemany does, got a single execution of the first parameter set, reported as success. sqlExecute and sqlExecDirect now switch to executeStatementParamArray when the paramset size is greater than 1 at execute time.
ParamOperationPtrSkipRows and RowWiseWithOperationPtr were skipped because the driver does not honour the operation array. Both get the Firebird 6 guard the other array cases carry and run from here on.
The operation array was stored in the APD (SQL_DESC_ARRAY_STATUS_PTR) but executeStatementParamArray never read it, so a set marked SQL_PARAM_IGNORE was executed like any other and an application had no way to skip a row short of rebuilding its arrays. The loop now skips ignored sets, leaves their status at SQL_PARAM_UNUSED and does not count them in SQL_ATTR_PARAMS_PROCESSED_PTR. The status array is filled with SQL_PARAM_UNUSED before the loop, so sets that were never reached read as unused, and status elements are addressed by set number instead of through a moving pointer. The processed count is kept per set instead of being written once after the loop, so it includes a failed set as the specification describes.
Two cases check the row count after an array INSERT (five sets, five) and after an array UPDATE whose sets touch one, three and zero rows (four), and that SQLMoreResults has nothing further. The two SQLGetInfo cases now expect SQL_PARC_NO_BATCH and SQL_PAS_NO_SELECT, the values the driver's behaviour matches.
SQLGetInfo advertised SQL_PARC_BATCH, one row count per parameter set stepped with SQLMoreResults, but the driver kept only the last set's count and SQLMoreResults answered SQL_NO_DATA, so an application saw 1 (or 0 when the last set matched nothing) for a five-row insert. executeStatementParamArray now adds up the update count of every set and sqlRowCount returns that total while the last execute was an array execute; a single execute resets it. SQL_PARAM_ARRAY_ROW_COUNTS reports SQL_PARC_NO_BATCH accordingly. SQL_PARAM_ARRAY_SELECTS reports SQL_PAS_NO_SELECT: a result-set statement with a paramset larger than 1 is not routed to the array executor and runs once with the first set, which neither SQL_PAS_BATCH nor SQL_PAS_NO_BATCH describes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Builds on #308 and #310: their commits show here until they merge. This PR is the last two commits.
The problem
SQLGetInfo(SQL_PARAM_ARRAY_ROW_COUNTS)answersSQL_PARC_BATCH: one row count per parameter set, stepped withSQLMoreResults. The driver keeps only the last set's update count andSQLMoreResultsanswersSQL_NO_DATAafter a non-select, so an application following the specification gets 1 for a five-row insert (0 when the last set matched nothing), then nothing. The reporter of #299 saw the 1.SQL_PARAM_ARRAY_SELECTSanswersSQL_PAS_BATCH, one result set per set. A result-set statement with a paramset larger than 1 is not routed to the array executor: it runs once with the first set and returns that result set only, with the processed count and the status array untouched.The fix
executeStatementParamArrayadds up the update count of every set;sqlRowCountreturns that total while the last execute was an array execute, and a single execute resets it.SQL_PARAM_ARRAY_ROW_COUNTSbecomesSQL_PARC_NO_BATCH, which is what the driver now does.SQL_PARAM_ARRAY_SELECTSbecomesSQL_PAS_NO_SELECT, the closest description of what the driver does with selects; execution is not changed, so an application that sets a paramset on a select still gets the first set's rows.Per-set counts through
SQLMoreResultswould need the statement's post-execute state to change; if anyone asks for them later, nothing here stands in the way.Tests
RowCountAfterArrayInsert(five sets, count 5,SQLMoreResults=SQL_NO_DATA) andRowCountAfterArrayUpdate(three sets touching one, three and zero rows, count 4, which tells a total apart from a set count and from the last set's count). The twoSQLGetInfocases expect the new values. Against the #310 driver all four fail and the other 17 array cases pass; with this branch the array suite is 21 of 21. Full suite,CHARSET=UTF8andNONE: 392 ran, 239 passed, 153 skipped, 0 failed.Probe (ctypes, Windows x64, Firebird 5.0.3, Microsoft driver manager):
SQLRowCountafter a 5-set INSERTSQLRowCountafter a 3-set UPDATE, two sets matchSQL_PARAM_ARRAY_ROW_COUNTS/SQL_PARAM_ARRAY_SELECTSBATCH/BATCHNO_BATCH/NO_SELECTStill open
SQL_DIAG_ROW_COUNTis never assigned anywhere in the driver (sqlDiagRowCountstays 0). Not touched here.