From d46112e2b35e0fc5f493f97575142ea5aa6aa39d Mon Sep 17 00:00:00 2001 From: TimelordUK Date: Sun, 18 Oct 2026 14:37:10 +0100 Subject: [PATCH] fix: route a statement's diagnostics in driver order (#478) A set of diagnostics with an error in it had its info routed at once and only its errors handed back, so 3621 "The statement has been terminated." came before its 2627. Now such a set is routed whole, in driver order, by the error path; a set of info only is routed at once, as before. 'more' is counted over errors only. It also counted info before, which was latent while info and errors were routed apart. In callback mode, info that follows an error is emitted a tick on, after the deferred callback. New scenario errors/two-errors-one-statement. All dup-key-* and scripts/issue-478 are agreed; the Linux pins are reordered and stay known-bug for #483. Co-Authored-By: Claude Opus 5.5 (1M context) --- lib/reader.js | 71 +++++++++---------- test/contract/HANDOVER.md | 20 +++++- .../corpus/errors/dup-key-a.callback.txt | 4 +- .../corpus/errors/dup-key-a.events.txt | 4 +- .../corpus/errors/dup-key-b.callback.txt | 4 +- .../corpus/errors/dup-key-b.events.txt | 4 +- .../corpus/errors/dup-key-c.callback.txt | 4 +- .../corpus/errors/dup-key-c.events.txt | 4 +- .../errors/dup-key-d.callback.linux.txt | 2 +- .../corpus/errors/dup-key-d.callback.txt | 4 +- .../corpus/errors/dup-key-d.events.linux.txt | 2 +- .../corpus/errors/dup-key-d.events.txt | 2 +- .../two-errors-one-statement.callback.txt | 10 +++ .../two-errors-one-statement.events.txt | 10 +++ .../corpus/scripts/issue-478.linux.txt | 8 +-- test/contract/corpus/scripts/issue-478.txt | 8 +-- ...o-errors-one-statement.callback.win32.json | 1 + ...two-errors-one-statement.events.win32.json | 1 + test/contract/scenarios.js | 2 + 19 files changed, 101 insertions(+), 64 deletions(-) create mode 100644 test/contract/corpus/errors/two-errors-one-statement.callback.txt create mode 100644 test/contract/corpus/errors/two-errors-one-statement.events.txt create mode 100644 test/contract/native/errors/two-errors-one-statement.callback.win32.json create mode 100644 test/contract/native/errors/two-errors-one-statement.events.win32.json diff --git a/lib/reader.js b/lib/reader.js index 76b5b92b..79703129 100755 --- a/lib/reader.js +++ b/lib/reader.js @@ -81,36 +81,40 @@ class Query extends BasePromises { } /* route non-critical info messages to its own event to prevent streams based readers from halting */ - // moreResults: the batch carries on after these errors, so 'more' is true for all of them + // in the order the driver gave them (#478). 'more' is counted over the errors only: an + // error has more when another error follows it, or when the batch carries on after them + // (moreResults). routeStatementError (errorsAndInfo, callback, notify, moreResults = false) { if (!Array.isArray(errorsAndInfo)) { errorsAndInfo = [errorsAndInfo] } - let i = 0 - const onlyErrors = errorsAndInfo.reduce((agg, latest) => { - if (!this.isInfo(latest)) { - agg.push(latest) - } - return agg - }, []) - const errorCount = onlyErrors.length + const errorCount = errorsAndInfo.filter(e => !this.isInfo(e)).length + let errorIndex = 0 errorsAndInfo.forEach(err => { const info = this.isInfo(err) + const more = moreResults || (info ? errorIndex < errorCount : errorIndex < errorCount - 1) + if (!info) ++errorIndex if (callback && !info) { - const more = moreResults || i < errorsAndInfo.length - 1 callback(err, null, more) } else { const ev = info ? 'info' : 'error' if (notify) { - const more = moreResults || i < errorCount - 1 - if (notify.listenerCount(ev) > 0) { - notify.emit(ev, err, more) + const emit = () => { + if (notify.listenerCount(ev) > 0) { + notify.emit(ev, err, more) + } + } + // the connection defers the user's callback by a tick: info that follows an error + // in the set waits as long, so it still comes after the error + if (callback && info && errorIndex > 0) { + setImmediate(emit) + } else { + emit() } } else { throw new Error(err) } } - ++i }) } @@ -161,25 +165,15 @@ class Query extends BasePromises { } } + // a set of diagnostics with no error in it: route the info now, and nothing is returned. + // with an error in it: route nothing, and return the whole set, info included, so that + // the error path routes it in driver order - the error first, then e.g. 3621 "The + // statement has been terminated" (#478) dispatchInfoReturnErrors (e) { - const infoMessages = [] - const errorMessages = [] - - if (e && Array.isArray(e)) { - e.forEach(errorOrInfo => { - if (this.isInfo(errorOrInfo)) { - infoMessages.push(errorOrInfo) - } else { - errorMessages.push(errorOrInfo) - } - }) - } - // route info even alongside errors: a print just before a failing statement arrives - // in the same set of diagnostics as the error - if (infoMessages.length > 0) { - this.routeStatementError(infoMessages, this.callback, this.notify, false) - } - return errorMessages + if (!e || !Array.isArray(e) || e.length === 0) return [] + if (e.some(m => !this.isInfo(m))) return e + this.routeStatementError(e, this.callback, this.notify, false) + return [] } async nativeNextResult (queryId) { @@ -189,13 +183,13 @@ class Query extends BasePromises { this.native.nextResultSet(this.queryId, this.notify.getHandle(), (e, res) => { setImmediate(() => { // may contain info messages e.g. raised by PRINT statements - do not want to reject these - const errorMessages = e ? this.dispatchInfoReturnErrors(e) : [] - if (errorMessages.length > 0 && res && !res.endOfResults) { + const withErrors = e ? this.dispatchInfoReturnErrors(e) : [] + if (withErrors.length > 0 && res && !res.endOfResults) { // a statement in the batch failed but the batch goes on: report and carry on - this.routeStatementError(errorMessages, this.callback, this.notify, true) + this.routeStatementError(withErrors, this.callback, this.notify, true) resolve(res) - } else if (errorMessages.length > 0) { - reject(errorMessages) + } else if (withErrors.length > 0) { + reject(withErrors) } else { this.infoFromNextResult = e != null && Array.isArray(e) && e.length > 0 resolve(res) @@ -351,7 +345,8 @@ class Query extends BasePromises { // the query was cancelled, so report it as such. asCancelled (errors) { if (!this.notify.getCancelSent()) return errors - const fromCancel = errors.length > 0 && errors.every(e => cancelSideEffectStates.has(e?.sqlstate)) + const real = errors.filter(e => !this.isInfo(e)) + const fromCancel = real.length > 0 && real.every(e => cancelSideEffectStates.has(e?.sqlstate)) if (!fromCancel) return errors logger.debugLazy(() => `query ${this.queryId} fetch after cancel failed with ${errors[0].sqlstate} - report as cancelled`, this.context) const cancelled = new Error('[msnodesql] Operation canceled') diff --git a/test/contract/HANDOVER.md b/test/contract/HANDOVER.md index d74309eb..d9a55ed7 100644 --- a/test/contract/HANDOVER.md +++ b/test/contract/HANDOVER.md @@ -142,9 +142,27 @@ clears the token first. New, agreed: `cancel/twice-*`, `cancel/again-after-confi `cancel/paused-again-after-confirm-at-100`, `cancel/paused-before-execute-twice`, and `cancel/paused-resumed-after-cancel-at-100` (a resume after the cancel does nothing). +## Progress — 2026-10-18: diagnostics in driver order (#478) + +`dispatchInfoReturnErrors` routed a set's info at once and handed back only its errors, so +3621 "The statement has been terminated." overtook its 2627. Now a set with an error in it is +routed whole, in driver order, by the error path. A set of info only is routed at once, as +before. `routeStatementError` counts `more` over errors only: an error has more when another +error follows it or the batch goes on. Before, it also counted info, which was latent while +info and errors were routed apart. In callback mode, info that follows an error in a set is +emitted a tick on, after the (deferred) callback. `asCancelled` ignores info. New: +`errors/two-errors-one-statement` (1779 then 1750). All `dup-key-*` and `scripts/issue-478` +are agreed; the Linux pins were reordered by hand and stay known-bug for #483. + +Found while there, not fixed: info from some DDL is mostly lost, on master too. 1756 +"Skipping FOREIGN KEY constraint ... for temporary table" from +`create table #fk (id int constraint fk_x references no_such_table(id))` came 0 of 20 times +on master and 1 of 20 here, while PRINT info always comes. It is probably native: the +diagnostics are not read when the driver returns plain `SQL_SUCCESS`. + ## Next -1. #478: route each set of diagnostics in driver order; count `more` over errors only. +1. Info lost from DDL (above): pin it with a scenario, then look at the native diagnostics reads. 2. Hold an error *at execute* while paused. `free` now waits for `done`, which removes the reason it was not held (below). 3. Prepared executions: `free` per execution, or document that only `free()` raises it. diff --git a/test/contract/corpus/errors/dup-key-a.callback.txt b/test/contract/corpus/errors/dup-key-a.callback.txt index ff8caf0b..f53d517b 100644 --- a/test/contract/corpus/errors/dup-key-a.callback.txt +++ b/test/contract/corpus/errors/dup-key-a.callback.txt @@ -1,10 +1,10 @@ # scenario: errors/dup-key-a.callback -# status: known-bug +# status: agreed # setup: create table #t (id int constraint pk_t primary key); insert #t values (1) # sql: insert #t values (1) submitted -info 01000 3621 The statement has been terminated. cb error 23000 2627 Violation of PRIMARY KEY constraint 'pk_t'. Cannot insert duplicate key in object 'dbo.#t'. The duplicate key value is (1). more=false +info 01000 3621 The statement has been terminated. done free check ok diff --git a/test/contract/corpus/errors/dup-key-a.events.txt b/test/contract/corpus/errors/dup-key-a.events.txt index 92316426..a9f69bff 100644 --- a/test/contract/corpus/errors/dup-key-a.events.txt +++ b/test/contract/corpus/errors/dup-key-a.events.txt @@ -1,10 +1,10 @@ # scenario: errors/dup-key-a.events -# status: known-bug +# status: agreed # setup: create table #t (id int constraint pk_t primary key); insert #t values (1) # sql: insert #t values (1) submitted -info 01000 3621 The statement has been terminated. error 23000 2627 Violation of PRIMARY KEY constraint 'pk_t'. Cannot insert duplicate key in object 'dbo.#t'. The duplicate key value is (1). more=false +info 01000 3621 The statement has been terminated. done free check ok diff --git a/test/contract/corpus/errors/dup-key-b.callback.txt b/test/contract/corpus/errors/dup-key-b.callback.txt index 6df82fd0..bd465ce9 100644 --- a/test/contract/corpus/errors/dup-key-b.callback.txt +++ b/test/contract/corpus/errors/dup-key-b.callback.txt @@ -1,13 +1,13 @@ # scenario: errors/dup-key-b.callback -# status: known-bug +# status: agreed # setup: create table #t (id int constraint pk_t primary key); insert #t values (1) # sql: select 1 as n; insert #t values (1) submitted meta n row column 0 1 -info 01000 3621 The statement has been terminated. cb error 23000 2627 Violation of PRIMARY KEY constraint 'pk_t'. Cannot insert duplicate key in object 'dbo.#t'. The duplicate key value is (1). more=false +info 01000 3621 The statement has been terminated. done free check ok diff --git a/test/contract/corpus/errors/dup-key-b.events.txt b/test/contract/corpus/errors/dup-key-b.events.txt index b1e0e320..1de87ff1 100644 --- a/test/contract/corpus/errors/dup-key-b.events.txt +++ b/test/contract/corpus/errors/dup-key-b.events.txt @@ -1,13 +1,13 @@ # scenario: errors/dup-key-b.events -# status: known-bug +# status: agreed # setup: create table #t (id int constraint pk_t primary key); insert #t values (1) # sql: select 1 as n; insert #t values (1) submitted meta n row column 0 1 -info 01000 3621 The statement has been terminated. error 23000 2627 Violation of PRIMARY KEY constraint 'pk_t'. Cannot insert duplicate key in object 'dbo.#t'. The duplicate key value is (1). more=false +info 01000 3621 The statement has been terminated. done free check ok diff --git a/test/contract/corpus/errors/dup-key-c.callback.txt b/test/contract/corpus/errors/dup-key-c.callback.txt index 2c239b40..7867948f 100644 --- a/test/contract/corpus/errors/dup-key-c.callback.txt +++ b/test/contract/corpus/errors/dup-key-c.callback.txt @@ -1,13 +1,13 @@ # scenario: errors/dup-key-c.callback -# status: unreviewed +# status: agreed # setup: create table #t (id int constraint pk_t primary key); insert #t values (1) # sql: select 1 as n; insert #t values (1); select 2 as m submitted meta n row column 0 1 -info 01000 3621 The statement has been terminated. cb error 23000 2627 Violation of PRIMARY KEY constraint 'pk_t'. Cannot insert duplicate key in object 'dbo.#t'. The duplicate key value is (1). more=true +info 01000 3621 The statement has been terminated. cb ok rows=1 more=true rowcount 0 meta m diff --git a/test/contract/corpus/errors/dup-key-c.events.txt b/test/contract/corpus/errors/dup-key-c.events.txt index db307cdc..06068ec5 100644 --- a/test/contract/corpus/errors/dup-key-c.events.txt +++ b/test/contract/corpus/errors/dup-key-c.events.txt @@ -1,13 +1,13 @@ # scenario: errors/dup-key-c.events -# status: unreviewed +# status: agreed # setup: create table #t (id int constraint pk_t primary key); insert #t values (1) # sql: select 1 as n; insert #t values (1); select 2 as m submitted meta n row column 0 1 -info 01000 3621 The statement has been terminated. error 23000 2627 Violation of PRIMARY KEY constraint 'pk_t'. Cannot insert duplicate key in object 'dbo.#t'. The duplicate key value is (1). more=true +info 01000 3621 The statement has been terminated. rowcount 0 meta m row diff --git a/test/contract/corpus/errors/dup-key-d.callback.linux.txt b/test/contract/corpus/errors/dup-key-d.callback.linux.txt index 3cce75fb..939706f5 100644 --- a/test/contract/corpus/errors/dup-key-d.callback.linux.txt +++ b/test/contract/corpus/errors/dup-key-d.callback.linux.txt @@ -3,8 +3,8 @@ # setup: create table #t (id int constraint pk_t primary key); insert #t values (1) # sql: insert #t values (1); select 2 as m submitted -info 01000 3621 The statement has been terminated. cb error 23000 2627 Violation of PRIMARY KEY constraint 'pk_t'. Cannot insert duplicate key in object 'dbo.#t'. The duplicate key value is (1). more=false +info 01000 3621 The statement has been terminated. done free check ok diff --git a/test/contract/corpus/errors/dup-key-d.callback.txt b/test/contract/corpus/errors/dup-key-d.callback.txt index a97f884b..5b28b152 100644 --- a/test/contract/corpus/errors/dup-key-d.callback.txt +++ b/test/contract/corpus/errors/dup-key-d.callback.txt @@ -1,10 +1,10 @@ # scenario: errors/dup-key-d.callback -# status: unreviewed +# status: agreed # setup: create table #t (id int constraint pk_t primary key); insert #t values (1) # sql: insert #t values (1); select 2 as m submitted -info 01000 3621 The statement has been terminated. cb error 23000 2627 Violation of PRIMARY KEY constraint 'pk_t'. Cannot insert duplicate key in object 'dbo.#t'. The duplicate key value is (1). more=true +info 01000 3621 The statement has been terminated. rowcount 0 meta m cb ok rows=0 more=true diff --git a/test/contract/corpus/errors/dup-key-d.events.linux.txt b/test/contract/corpus/errors/dup-key-d.events.linux.txt index 91ad08e0..e8a75256 100644 --- a/test/contract/corpus/errors/dup-key-d.events.linux.txt +++ b/test/contract/corpus/errors/dup-key-d.events.linux.txt @@ -3,8 +3,8 @@ # setup: create table #t (id int constraint pk_t primary key); insert #t values (1) # sql: insert #t values (1); select 2 as m submitted -info 01000 3621 The statement has been terminated. error 23000 2627 Violation of PRIMARY KEY constraint 'pk_t'. Cannot insert duplicate key in object 'dbo.#t'. The duplicate key value is (1). more=false +info 01000 3621 The statement has been terminated. done free check ok diff --git a/test/contract/corpus/errors/dup-key-d.events.txt b/test/contract/corpus/errors/dup-key-d.events.txt index 5c581468..d431952d 100644 --- a/test/contract/corpus/errors/dup-key-d.events.txt +++ b/test/contract/corpus/errors/dup-key-d.events.txt @@ -1,5 +1,5 @@ # scenario: errors/dup-key-d.events -# status: unreviewed +# status: agreed # setup: create table #t (id int constraint pk_t primary key); insert #t values (1) # sql: insert #t values (1); select 2 as m submitted diff --git a/test/contract/corpus/errors/two-errors-one-statement.callback.txt b/test/contract/corpus/errors/two-errors-one-statement.callback.txt new file mode 100644 index 00000000..7a73c9aa --- /dev/null +++ b/test/contract/corpus/errors/two-errors-one-statement.callback.txt @@ -0,0 +1,10 @@ +# scenario: errors/two-errors-one-statement.callback +# status: agreed +# setup: create table #t (id int constraint pk_t primary key); insert #t values (1) +# sql: alter table #t add constraint pk_t2 primary key (id) +submitted +cb error 42000 1779 Table '#t' already has a primary key defined on it. more=true +cb error 42000 1750 Could not create constraint or index. See previous errors. more=false +done +free +check ok diff --git a/test/contract/corpus/errors/two-errors-one-statement.events.txt b/test/contract/corpus/errors/two-errors-one-statement.events.txt new file mode 100644 index 00000000..c1dd715d --- /dev/null +++ b/test/contract/corpus/errors/two-errors-one-statement.events.txt @@ -0,0 +1,10 @@ +# scenario: errors/two-errors-one-statement.events +# status: agreed +# setup: create table #t (id int constraint pk_t primary key); insert #t values (1) +# sql: alter table #t add constraint pk_t2 primary key (id) +submitted +error 42000 1779 Table '#t' already has a primary key defined on it. more=true +error 42000 1750 Could not create constraint or index. See previous errors. more=false +done +free +check ok diff --git a/test/contract/corpus/scripts/issue-478.linux.txt b/test/contract/corpus/scripts/issue-478.linux.txt index e0bd382a..48313af8 100644 --- a/test/contract/corpus/scripts/issue-478.linux.txt +++ b/test/contract/corpus/scripts/issue-478.linux.txt @@ -8,24 +8,24 @@ setup: done setup: free == A insert #t values (1) A: submitted -A: info 01000 3621 The statement has been terminated. A: error 23000 2627 Violation of PRIMARY KEY constraint 'pk_t'. Cannot insert duplicate key in object 'dbo.#t'. The duplicate key value is (1). more=false +A: info 01000 3621 The statement has been terminated. A: done A: free == B select 1 as n; insert #t values (1) B: submitted B: meta n B: row -B: info 01000 3621 The statement has been terminated. B: error 23000 2627 Violation of PRIMARY KEY constraint 'pk_t'. Cannot insert duplicate key in object 'dbo.#t'. The duplicate key value is (1). more=false +B: info 01000 3621 The statement has been terminated. B: done B: free == C select 1 as n; insert #t values (1); select 2 as m C: submitted C: meta n C: row -C: info 01000 3621 The statement has been terminated. C: error 23000 2627 Violation of PRIMARY KEY constraint 'pk_t'. Cannot insert duplicate key in object 'dbo.#t'. The duplicate key value is (1). more=true +C: info 01000 3621 The statement has been terminated. C: rowcount 0 C: meta m C: row @@ -33,8 +33,8 @@ C: done C: free == D insert #t values (1); select 2 as m D: submitted -D: info 01000 3621 The statement has been terminated. D: error 23000 2627 Violation of PRIMARY KEY constraint 'pk_t'. Cannot insert duplicate key in object 'dbo.#t'. The duplicate key value is (1). more=false +D: info 01000 3621 The statement has been terminated. D: done D: free check ok diff --git a/test/contract/corpus/scripts/issue-478.txt b/test/contract/corpus/scripts/issue-478.txt index 077e214e..aaf192f4 100644 --- a/test/contract/corpus/scripts/issue-478.txt +++ b/test/contract/corpus/scripts/issue-478.txt @@ -1,5 +1,5 @@ # scenario: scripts/issue-478 -# status: known-bug +# status: agreed # script: test/contract/scripts/issue-478.js setup: submitted setup: rowcount 1 @@ -8,24 +8,24 @@ setup: done setup: free == A insert #t values (1) A: submitted -A: info 01000 3621 The statement has been terminated. A: error 23000 2627 Violation of PRIMARY KEY constraint 'pk_t'. Cannot insert duplicate key in object 'dbo.#t'. The duplicate key value is (1). more=false +A: info 01000 3621 The statement has been terminated. A: done A: free == B select 1 as n; insert #t values (1) B: submitted B: meta n B: row -B: info 01000 3621 The statement has been terminated. B: error 23000 2627 Violation of PRIMARY KEY constraint 'pk_t'. Cannot insert duplicate key in object 'dbo.#t'. The duplicate key value is (1). more=false +B: info 01000 3621 The statement has been terminated. B: done B: free == C select 1 as n; insert #t values (1); select 2 as m C: submitted C: meta n C: row -C: info 01000 3621 The statement has been terminated. C: error 23000 2627 Violation of PRIMARY KEY constraint 'pk_t'. Cannot insert duplicate key in object 'dbo.#t'. The duplicate key value is (1). more=true +C: info 01000 3621 The statement has been terminated. C: rowcount 0 C: meta m C: row diff --git a/test/contract/native/errors/two-errors-one-statement.callback.win32.json b/test/contract/native/errors/two-errors-one-statement.callback.win32.json new file mode 100644 index 00000000..4210c5a1 --- /dev/null +++ b/test/contract/native/errors/two-errors-one-statement.callback.win32.json @@ -0,0 +1 @@ +{"scenario":"errors/two-errors-one-statement.callback","transcript":["submitted","cb error 42000 1779 Table '#t' already has a primary key defined on it. more=true","cb error 42000 1750 Could not create constraint or index. See previous errors. more=false","done","free","check ok"],"log":[{"ev":"call","id":0,"t":10.3,"name":"open","args":["",0]},{"ev":"return","of":0,"t":30.2,"result":[null,{"success":true}]},{"ev":"call","id":2,"t":31.2,"name":"query","args":[0,{"query_str":"create table #t (id int constraint pk_t primary key); insert #t values (1)","query_timeout":0,"query_polling":false,"query_tz_adjustment":0,"numeric_string":false,"bigint_as_native":false,"stateChangeCallback":{"$":"function"}},[]]},{"ev":"state","of":2,"t":31.4,"change":{"handle":{"connectionId":0,"statementId":0},"oldState":"STATEMENT_CREATED","newState":"STATEMENT_BINDING"}},{"ev":"state","of":2,"t":31.5,"change":{"handle":{"connectionId":0,"statementId":0},"oldState":"STATEMENT_BINDING","newState":"STATEMENT_SUBMITTED"}},{"ev":"state","of":2,"t":32.9,"change":{"handle":{"connectionId":0,"statementId":0},"oldState":"STATEMENT_SUBMITTED","newState":"STATEMENT_READING"}},{"ev":"return","of":2,"t":32.9,"result":[null,{"meta":[],"handle":{"connectionId":0,"statementId":0},"endOfRows":true,"endOfResults":false,"rowCount":1},true]},{"ev":"call","id":7,"t":33.1,"name":"nextResultSet","args":[0,{"connectionId":0,"statementId":0}]},{"ev":"return","of":7,"t":33.2,"result":[null,{"meta":[],"handle":{"connectionId":0,"statementId":0},"endOfRows":true,"endOfResults":true,"rowCount":1}]},{"ev":"call","id":9,"t":33.6,"name":"releaseStatement","args":[0,{"connectionId":0,"statementId":0}]},{"ev":"state","of":2,"t":33.6,"change":{"handle":{"connectionId":0,"statementId":0},"oldState":"STATEMENT_READING","newState":"STATEMENT_CLOSED"}},{"ev":"return","of":9,"t":33.6,"result":[null,true]},{"ev":"call","id":12,"t":34.1,"name":"query","args":[1,{"query_str":"alter table #t add constraint pk_t2 primary key (id)","query_timeout":0,"query_polling":false,"query_tz_adjustment":0,"numeric_string":false,"bigint_as_native":false,"stateChangeCallback":{"$":"function"}},[]]},{"ev":"state","of":12,"t":34.3,"change":{"handle":{"connectionId":0,"statementId":1},"oldState":"STATEMENT_CREATED","newState":"STATEMENT_BINDING"}},{"ev":"state","of":12,"t":34.3,"change":{"handle":{"connectionId":0,"statementId":1},"oldState":"STATEMENT_BINDING","newState":"STATEMENT_SUBMITTED"}},{"ev":"state","of":12,"t":35,"change":{"handle":{"connectionId":0,"statementId":1},"oldState":"STATEMENT_SUBMITTED","newState":"STATEMENT_READING"}},{"ev":"state","of":12,"t":35,"change":{"handle":{"connectionId":0,"statementId":1},"oldState":"STATEMENT_READING","newState":"STATEMENT_ERROR"}},{"ev":"return","of":12,"t":35,"result":[[{"$":"error","message":"[Microsoft][ODBC Driver 18 for SQL Server][SQL Server]Table '#t' already has a primary key defined on it.","sqlstate":"42000","code":1779,"severity":16,"serverName":"","procName":"","lineNumber":1},{"$":"error","message":"[Microsoft][ODBC Driver 18 for SQL Server][SQL Server]Could not create constraint or index. See previous errors.","sqlstate":"42000","code":1750,"severity":16,"serverName":"","procName":"","lineNumber":1}],{"meta":[],"handle":{"connectionId":0,"statementId":1},"endOfRows":true,"endOfResults":true,"rowCount":0},false]},{"ev":"call","id":18,"t":35.1,"name":"releaseStatement","args":[1,{"connectionId":0,"statementId":1}]},{"ev":"state","of":12,"t":35.1,"change":{"handle":{"connectionId":0,"statementId":1},"oldState":"STATEMENT_ERROR","newState":"STATEMENT_CLOSED"}},{"ev":"return","of":18,"t":35.1,"result":[null,true]},{"ev":"call","id":21,"t":188.6,"name":"query","args":[2,{"query_str":"select 1 as n","query_timeout":0,"query_polling":false,"query_tz_adjustment":0,"numeric_string":false,"bigint_as_native":false,"stateChangeCallback":{"$":"function"}},[]]},{"ev":"state","of":21,"t":188.7,"change":{"handle":{"connectionId":0,"statementId":2},"oldState":"STATEMENT_CREATED","newState":"STATEMENT_BINDING"}},{"ev":"state","of":21,"t":188.7,"change":{"handle":{"connectionId":0,"statementId":2},"oldState":"STATEMENT_BINDING","newState":"STATEMENT_SUBMITTED"}},{"ev":"state","of":21,"t":189.7,"change":{"handle":{"connectionId":0,"statementId":2},"oldState":"STATEMENT_SUBMITTED","newState":"STATEMENT_READING"}},{"ev":"return","of":21,"t":189.8,"result":[null,{"meta":[{"name":"n","nullable":false,"type":"number","sqlType":"int","size":10}],"handle":{"connectionId":0,"statementId":2},"endOfRows":true,"endOfResults":false,"rowCount":0},true]},{"ev":"call","id":26,"t":190,"name":"fetchRows","args":[2,{"connectionId":0,"statementId":2},{"asArrays":true,"batchSize":50,"asObjects":false}]},{"ev":"return","of":26,"t":190,"result":[null,{"endOfRows":true,"endOfResults":false,"rowCount":0,"data":{"$":"rows","n":1,"first":[1]}}]},{"ev":"call","id":28,"t":190.2,"name":"nextResultSet","args":[2,{"connectionId":0,"statementId":2}]},{"ev":"return","of":28,"t":190.3,"result":[null,{"meta":[{"name":"n","nullable":false,"type":"number","sqlType":"int","size":10}],"handle":{"connectionId":0,"statementId":2},"endOfRows":true,"endOfResults":true,"rowCount":0}]},{"ev":"call","id":30,"t":190.4,"name":"releaseStatement","args":[2,{"connectionId":0,"statementId":2}]},{"ev":"state","of":21,"t":190.4,"change":{"handle":{"connectionId":0,"statementId":2},"oldState":"STATEMENT_READING","newState":"STATEMENT_CLOSED"}},{"ev":"return","of":30,"t":190.4,"result":[null,true]},{"ev":"call","id":33,"t":190.6,"name":"close","args":[]},{"ev":"return","of":33,"t":190.7,"result":[null,true]}]} \ No newline at end of file diff --git a/test/contract/native/errors/two-errors-one-statement.events.win32.json b/test/contract/native/errors/two-errors-one-statement.events.win32.json new file mode 100644 index 00000000..a6a02576 --- /dev/null +++ b/test/contract/native/errors/two-errors-one-statement.events.win32.json @@ -0,0 +1 @@ +{"scenario":"errors/two-errors-one-statement.events","transcript":["submitted","error 42000 1779 Table '#t' already has a primary key defined on it. more=true","error 42000 1750 Could not create constraint or index. See previous errors. more=false","done","free","check ok"],"log":[{"ev":"call","id":0,"t":10.5,"name":"open","args":["",0]},{"ev":"return","of":0,"t":30.3,"result":[null,{"success":true}]},{"ev":"call","id":2,"t":31.6,"name":"query","args":[0,{"query_str":"create table #t (id int constraint pk_t primary key); insert #t values (1)","query_timeout":0,"query_polling":false,"query_tz_adjustment":0,"numeric_string":false,"bigint_as_native":false,"stateChangeCallback":{"$":"function"}},[]]},{"ev":"state","of":2,"t":31.7,"change":{"handle":{"connectionId":0,"statementId":0},"oldState":"STATEMENT_CREATED","newState":"STATEMENT_BINDING"}},{"ev":"state","of":2,"t":31.8,"change":{"handle":{"connectionId":0,"statementId":0},"oldState":"STATEMENT_BINDING","newState":"STATEMENT_SUBMITTED"}},{"ev":"state","of":2,"t":32.9,"change":{"handle":{"connectionId":0,"statementId":0},"oldState":"STATEMENT_SUBMITTED","newState":"STATEMENT_READING"}},{"ev":"return","of":2,"t":32.9,"result":[null,{"meta":[],"handle":{"connectionId":0,"statementId":0},"endOfRows":true,"endOfResults":false,"rowCount":1},true]},{"ev":"call","id":7,"t":33.1,"name":"nextResultSet","args":[0,{"connectionId":0,"statementId":0}]},{"ev":"return","of":7,"t":33.2,"result":[null,{"meta":[],"handle":{"connectionId":0,"statementId":0},"endOfRows":true,"endOfResults":true,"rowCount":1}]},{"ev":"call","id":9,"t":33.5,"name":"releaseStatement","args":[0,{"connectionId":0,"statementId":0}]},{"ev":"state","of":2,"t":33.6,"change":{"handle":{"connectionId":0,"statementId":0},"oldState":"STATEMENT_READING","newState":"STATEMENT_CLOSED"}},{"ev":"return","of":9,"t":33.6,"result":[null,true]},{"ev":"call","id":12,"t":34,"name":"query","args":[1,{"query_str":"alter table #t add constraint pk_t2 primary key (id)","query_timeout":0,"query_polling":false,"query_tz_adjustment":0,"numeric_string":false,"bigint_as_native":false,"stateChangeCallback":{"$":"function"}},[]]},{"ev":"state","of":12,"t":34.1,"change":{"handle":{"connectionId":0,"statementId":1},"oldState":"STATEMENT_CREATED","newState":"STATEMENT_BINDING"}},{"ev":"state","of":12,"t":34.1,"change":{"handle":{"connectionId":0,"statementId":1},"oldState":"STATEMENT_BINDING","newState":"STATEMENT_SUBMITTED"}},{"ev":"state","of":12,"t":34.8,"change":{"handle":{"connectionId":0,"statementId":1},"oldState":"STATEMENT_SUBMITTED","newState":"STATEMENT_READING"}},{"ev":"state","of":12,"t":34.8,"change":{"handle":{"connectionId":0,"statementId":1},"oldState":"STATEMENT_READING","newState":"STATEMENT_ERROR"}},{"ev":"return","of":12,"t":34.9,"result":[[{"$":"error","message":"[Microsoft][ODBC Driver 18 for SQL Server][SQL Server]Table '#t' already has a primary key defined on it.","sqlstate":"42000","code":1779,"severity":16,"serverName":"","procName":"","lineNumber":1},{"$":"error","message":"[Microsoft][ODBC Driver 18 for SQL Server][SQL Server]Could not create constraint or index. See previous errors.","sqlstate":"42000","code":1750,"severity":16,"serverName":"","procName":"","lineNumber":1}],{"meta":[],"handle":{"connectionId":0,"statementId":1},"endOfRows":true,"endOfResults":true,"rowCount":0},false]},{"ev":"call","id":18,"t":34.9,"name":"releaseStatement","args":[1,{"connectionId":0,"statementId":1}]},{"ev":"state","of":12,"t":35,"change":{"handle":{"connectionId":0,"statementId":1},"oldState":"STATEMENT_ERROR","newState":"STATEMENT_CLOSED"}},{"ev":"return","of":18,"t":35,"result":[null,true]},{"ev":"call","id":21,"t":186.1,"name":"query","args":[2,{"query_str":"select 1 as n","query_timeout":0,"query_polling":false,"query_tz_adjustment":0,"numeric_string":false,"bigint_as_native":false,"stateChangeCallback":{"$":"function"}},[]]},{"ev":"state","of":21,"t":186.3,"change":{"handle":{"connectionId":0,"statementId":2},"oldState":"STATEMENT_CREATED","newState":"STATEMENT_BINDING"}},{"ev":"state","of":21,"t":186.3,"change":{"handle":{"connectionId":0,"statementId":2},"oldState":"STATEMENT_BINDING","newState":"STATEMENT_SUBMITTED"}},{"ev":"state","of":21,"t":187.3,"change":{"handle":{"connectionId":0,"statementId":2},"oldState":"STATEMENT_SUBMITTED","newState":"STATEMENT_READING"}},{"ev":"return","of":21,"t":187.3,"result":[null,{"meta":[{"name":"n","nullable":false,"type":"number","sqlType":"int","size":10}],"handle":{"connectionId":0,"statementId":2},"endOfRows":true,"endOfResults":false,"rowCount":0},true]},{"ev":"call","id":26,"t":187.5,"name":"fetchRows","args":[2,{"connectionId":0,"statementId":2},{"asArrays":true,"batchSize":50,"asObjects":false}]},{"ev":"return","of":26,"t":187.5,"result":[null,{"endOfRows":true,"endOfResults":false,"rowCount":0,"data":{"$":"rows","n":1,"first":[1]}}]},{"ev":"call","id":28,"t":187.7,"name":"nextResultSet","args":[2,{"connectionId":0,"statementId":2}]},{"ev":"return","of":28,"t":187.8,"result":[null,{"meta":[{"name":"n","nullable":false,"type":"number","sqlType":"int","size":10}],"handle":{"connectionId":0,"statementId":2},"endOfRows":true,"endOfResults":true,"rowCount":0}]},{"ev":"call","id":30,"t":187.8,"name":"releaseStatement","args":[2,{"connectionId":0,"statementId":2}]},{"ev":"state","of":21,"t":187.8,"change":{"handle":{"connectionId":0,"statementId":2},"oldState":"STATEMENT_READING","newState":"STATEMENT_CLOSED"}},{"ev":"return","of":30,"t":187.9,"result":[null,true]},{"ev":"call","id":33,"t":188,"name":"close","args":[]},{"ev":"return","of":33,"t":188.1,"result":[null,true]}]} \ No newline at end of file diff --git a/test/contract/scenarios.js b/test/contract/scenarios.js index 9aa57d99..b34d3806 100644 --- a/test/contract/scenarios.js +++ b/test/contract/scenarios.js @@ -76,6 +76,8 @@ const base = [ { name: 'errors/dup-key-b', setup: DUP_TABLE, sql: 'select 1 as n; insert #t values (1)', columns: true, modes: BOTH }, { name: 'errors/dup-key-c', setup: DUP_TABLE, sql: 'select 1 as n; insert #t values (1); select 2 as m', columns: true, modes: BOTH }, { name: 'errors/dup-key-d', setup: DUP_TABLE, sql: 'insert #t values (1); select 2 as m', columns: true, modes: BOTH }, + // one statement, two errors (1779 then 1750): only the last error has more=false + { name: 'errors/two-errors-one-statement', setup: DUP_TABLE, sql: 'alter table #t add constraint pk_t2 primary key (id)', modes: BOTH }, // ----- pause / resume (#480, #470): 51 rows is one full batch of 50 plus one ...[1, 49, 50, 51, 52, 101, 102].map(n => ({