A release about what a script can see. A write hands back the number of rows it affected, which is what a
conditional write needs, and every statement now waits for its work, so a failure can no longer reach the
console while last database error stays silent.
Added
store affected rows in {_rows}, on every statement that writes:insert one,insert many,
insert ... if absent,update,upsertanddelete, in the section form and the colon-free one
alike. It keeps the number of rows the statement affected, which is how a script tells "it was already
there" from "it was written", and how a read-modify-write becomes safe without a transaction: the count
of a statement whosewhererepeats the value it read is1only while nobody else got there first. A
count of0is a real answer, while a variable left unset means no statement answered — the statement
was refused or skipped, it failed, or the backend could not count it exactly. See
Affected rows for the three states, what the number means on each backend, and
a bounded retry recipe.
Changed
- Every statement waits. A write used to continue immediately unless it said
and wait: the next line
ran while it was still in flight, so a read could see the row as it was, and a failure only the database
could judge went to the console instead of intolast database error. Writes now behave the way reads
always have — the lines after a statement run once its work is done, which makes the order of a script
the order of its statements. Upgrade note: a script that relied on carrying on before a write had
finished will now wait for it, and a write that failed is now reported inlast database errorwhere it
used to be logged only.and waitis still accepted on every statement and does nothing, so no script
has to change; it can simply be left out from here on.
Fixed
insert entity if absenton MySQL swallowed every error, not only an existing row. It was
INSERT IGNORE, which turns errors into warnings, so a value too long for its column was stored cut
short and anullin anot nullcolumn became that column's default — with the script told nothing.
The insert is now sent as it is and the duplicate key is the one error the statement reads as "the row is
there", which is what PostgreSQL and MongoDB already did. The count is unchanged:1written,0it was
already there, and anything else fails like any other write.- A connection that lost the default role with no name of its own was left open for the life of the
server. Nothing could resolve to it afterwards —in connectionanduse connectionneed a name, and
it was no longer the default — so it kept its pool of ten server connections, and neither
disconnect from all connectionsnor a plugin disable could reach it.make ... the defaultcloses it
now, the same way an unnamed connection that replaces the default already closed the one it replaced. A
named connection keeps running and merely stops being the default. The statement waits for that
close, so the line after it sees the new default, and it is refused inside adatabase transaction,
because the connection it closes may be the one the transaction is running on. - A read that was refused before it ran left the previous result in its variable. A read that failed
at the database cleared the variable, while one refused earlier — no connection, an unknown table, a
wherevalue the column cannot hold, a page number of zero — did not, so the previous read's row was
still sitting there and a script could not tell "the row is gone" from "the statement did not run". Every
refusal clears it now, which is the rule thestore affected rowsvariable already followed: a
statement leaves this statement's answer, or nothing. - A whole number that did not fit its column was stored as a different number. Skript converts a
number into whatever type it is asked for, and every one of those conversions narrows: anintcolumn
turned5000000000into705032704(the low 32 bits), atinyintcolumn turned300into44, and
the database was handed a value that fitted, so nothing anywhere reported it. A number column is now
read as a number and narrowed by the plugin, which refuses a value the column cannot hold with a
message naming the column and its range — in avaluesblock, in a variable and in awherevalue
alike. A fraction written into a whole-number column is still cut towards zero, andfloatstill keeps
four bytes: only the case that was silently wrong changed. See
Types. - A failure the database itself reported did not end the transaction it happened in. Only a statement
refused before it was sent made a transaction rollback-only, so a constraint violation, a value the
server refused, or a dialect refusing a statement let the body carry on, a later statement cleared the
failure out oflast database error, and the body ended by committing the half of it that had worked.
Any statement failure now makes the transaction rollback-only: the statements after it do nothing, the
body rolls back, and the cause stays readable. - Naming the connection a transaction is already running on took the statements after it out of the
transaction. The switch was meant to be a no-op and was not: the frame it pushed carried no
transaction, so the statements under it ran on a pooled connection and committed on their own — and with
use connectionthe transaction itself was then left for the watchdog to end, dropping the work the
body had already done. The frame carries the transaction now, which is what makes the switch the no-op
it claims to be. rollback database transactionclearedlast database error. The automatic rollback of a failed
body keeps the cause; the rollback a script writes does the same now, so the line after the section
still says what went wrong.
Documentation
- A bilingual page for the row count, and the waiting material rewritten around it: the two troubleshooting
entries about silent write failures are gone, along with the behaviour they described. - The transaction timeout is written up in full: the spellings it accepts, the value it needs, when its
clock starts, that it belongs to the transaction rather than to the connection, and what a long body or
a statement that runs into the deadline does. - An audit of every page turned up the places where the docs stopped short of the code, and they are fixed
throughout: what a pool is and what bounds waiting for one; what a size in a table declaration applies
to; which statementsstore affected rowsanswers "was it written" for; what a list variable cannot
carry; what a raggedinsert manydoes; what aby idwrite on a missing row reports; what a limit
that resolves to nothing does; the order paging uses and why it is not a snapshot; what a refused read
leaves in the result variable; what a failure does to the rest of the trigger; why a table can be
"already registered"; and four recipes that could not have worked as written.
Full Changelog: v1.1.0...v1.2.0