Daniele Varrazzo [Thu, 19 May 2022 21:41:26 +0000 (23:41 +0200)]
Test: fix mypy 0.941 run
This seems a mypy shortcoming fixed in 0.950 as the CI didn't complain.
The row factory definition was exotic but arguably correct. Not worth
bumping up the min version for it anyway.
Denis Laxalde [Mon, 16 May 2022 17:16:53 +0000 (19:16 +0200)]
fix: only use covariant Row type variable
The real variance of Row type variable is covariant, per definition of
RowMaker (RowMaker[B] is a subtype of RowMaker[A] if B is a subtype of
A; similar to the Box type in [mypy documentation][]).
By dropping the Row type variable from Cursor argument in RowFactory, we
are now able to only use the covariant Row variable (previously Row_co,
now Row). Indeed, RowFactory does not actually depend on Cursor on being
parametrized on Row; rather it's the other way around (Cursor's Row type
variable comes from its row factory).
Denis Laxalde [Sun, 15 May 2022 12:28:36 +0000 (14:28 +0200)]
fix: use a more generic connection type in {Client,Server}Cursor
This makes them similar to plain Cursor class, i.e. being parametrized
on sync or async connection type, not the row type (which is handled
through the second type variable).
Daniele Varrazzo [Sat, 14 May 2022 08:22:44 +0000 (10:22 +0200)]
fix: don't barf on errors with blank sqlstate
Such messages are not entirely valid (sqlstate is documented to be
always present), however we receive them after a SHOW HELP in the
PgBouncer admin database.
The SHOW HELP actually does generate a sqlstate `00000` but the message
is somehow parsed incorrectly by the libpq, which goes on to report an
error:
message contents do not agree with length in message type "N"
See #303. PgBouncer issue reported upstream in
https://github.com/pgbouncer/pgbouncer/issues/718
Daniele Varrazzo [Thu, 12 May 2022 22:16:13 +0000 (00:16 +0200)]
test: disable tests throwing copy to executemany in client-side cursors
The async one sometimes gets in an allocation loop. On my laptop it gets
OOM'd quickly. On Github Actions it might be the cause of test taking an
unusually long time.
Daniele Varrazzo [Thu, 12 May 2022 21:47:46 +0000 (23:47 +0200)]
fix: don't forbid all PQexec queries in client-side cursors
We can still use PQexecParams with an empty list of parameters. This has
advantages, such as the possibility of implementing stream(), and less
faffing with differences between Cursor and ClientCursor.
Daniele Varrazzo [Sat, 14 May 2022 22:24:17 +0000 (00:24 +0200)]
test: skip testing random multirange arrays with empty last elements
Previously we were skipping the ones with an empty first element,
because of a known shortcoming in finding the right OID. Now that we
scan the whole array to find all the elements' classes, it's the last
entry which might break dumping.
Daniele Varrazzo [Fri, 13 May 2022 14:54:54 +0000 (16:54 +0200)]
fix: raise DataError if IntDumper tries to dump another type
Allow to dump int subclasses, but not other numeric type, which can lead
to truncation. As seen in #301, the Python implementation truncates; the
c implementation is reporting failing with a TypeError, but on Python
3.8 it raises a deprecation warning instead.
The condition largely happens dumping array of mixed types. However we
should probably test with array of mixed class in a more generic way.
This allows a more natural creation of cursors subclasses, without the
need of tweaking the connection's cursor_factory and allowing to pass
arbitrary init arguments to the cursor.
Daniele Varrazzo [Sun, 20 Mar 2022 18:24:53 +0000 (19:24 +0100)]
feat: add `Transformer.as_literal()` to convert literals to sql
This allows to keep a cache oid -> representation that will be
invalidated automatically if encoding or oid mapping changes, as well as
optimising the function in C.
Daniele Varrazzo [Sun, 20 Mar 2022 15:21:40 +0000 (16:21 +0100)]
refactor: `TypeInfo.alt_name` renamed to `regtype`
This has now acquired a precise meaning: it is the name of the type as
SQL snippet (it may contain invalid names, which get quoted, or a schema
name). It can be included literally in a query to make a valid cast.
The previous name wasn't documented and wasn't part of any public API so
we can probably rename it without much problem.
Allow to reuse the same transformer in nested context, instead of
creating new ones. Even if transformer copies are shallow, we may end up
creating several in certain places.
Denis Laxalde [Tue, 10 May 2022 10:11:21 +0000 (12:11 +0200)]
fix: make transaction status check account for pipeline mode
We turn _check_intrans() into a generator _check_intrans_gen() in order
to call _pipeline._sync_gen() if the connection is in pipeline mode so
as to retrieve an accurate connection status.
This makes the safety guard about 'autocommit' when inside a transaction
work in pipeline mode, thus removing the xfail in transaction tests.
In test_autocommit_unknown, we now catch OperationalError which is
raised by conn.wait() rather than ProgrammingError previously which is
no longer reached.
Denis Laxalde [Tue, 10 May 2022 07:10:06 +0000 (09:10 +0200)]
fix: wrap transaction in an outer pipeline if the connection has one
Before entering a transaction on a connection in pipeline mode, we open an
outer pipeline to ensure that a Sync is after the transaction exists;
similarly to the inner pipeline, this is to ensure that the connection
state is restored at transaction exit similarly to the non-pipeline
case. The inner pipeline is not enough because we need to account for
the exit transaction statement (COMMIT or ROLLBACK) and then sync its
result.
This fixes transactions tests failing in previous commit.
Denis Laxalde [Tue, 10 May 2022 07:01:07 +0000 (09:01 +0200)]
test: parametrize transaction tests with pipeline on/off
The 'conn' fixture is overloaded in transaction tests, pulling the
'pipeline' fixture to have them run with or without a pipeline.
When checking for connection's transaction state, just after entering
the transaction() block (e.g. in test_basic), we need to sync the
pipeline in order to get the state right. This is just for tests as the
exact transaction state, in pipeline mode, is not deterministic since
query results are not fetched directly.
This is because the connection state is not correct at the end of
transaction, we'll fix this in next commit.
Some others were adjusted as follows:
* when entering the transaction() block, e.g. in test_begins_on_enter,
we eventually call pipeline.sync() in order to get the state right
(this is just for tests as the exact transaction state, in pipeline
mode, is not deterministic since query results are not fetched
directly);
* test_context_inerror_rollback_no_clobber[pipeline=on] is skipped, as
explained in comment inline;
* test_prohibits_use_of_commit_rollback_autocommit[pipeline=on] is
xfail, because Connection._check_intrans() does not check for pipeline
mode (see TODO inline about a possible solution);
* test_str now checks for [<txstatus>, pipeline=ON] in str(Transaction).
fix: fix integration between pipelines and nested transaction
Refactor to remove all the knowledge about pipelines from the
transaction object. Replaced by just syncing before entering the
transaction.
Had to drop the pipeline exit, which will now fetch results whatever the
level. If not doing so, a final sync should have been forced to exit
with a clean state from an inner transaction block.
test: drop TODO points and emergency rollbacks from pipeline tests
The previous changeset restored some sanity. The behaviour in
`test_errors_raised_on_commit` is not inconsistent with the non-pipeline
mode because the two are not comparable: in non-pipeline mode the
exception is raised by `execute()`, not by `commit()`. If an exception
is raised in `commit()` itself, it is already handled consistently, as
`test_error_on_commit` shows.
Denis Laxalde [Mon, 9 May 2022 19:14:59 +0000 (21:14 +0200)]
fix: wrap transaction in pipelines if the connection has one
When entering a transaction on a connection in pipeline mode, we open an
inner pipeline to ensure that a Sync is emitted at the end of
transaction thus restoring the connection in its expected state (i.e.
the same as in non-pipeline mode).
Make sure we receive the commit message, so we can raise an immediate
exception, as we do for connection-handled transactions.
Highlight, in the test, a behaviour difference between commit in
pipeline and non-pipeline mode. In the latter, after a failed commit,
the connection is left IDLE; in pipeline mode it is left INERROR so we
need a further rollback to keep on using it. Will look if this can be
made consistent.
fix: fix nested transaction entering in pipeline mode
If the connection is idle, it might be that we are missing results.
Receive them to align the connection state to what the transaction
object expects, otherwise the assert in _push_savepoint() will fail.
This maintains the expectation that, after commit(), the operation is
really persisted. As per postgres pipeline docs:
The client must not assume that work is committed when it sends a
COMMIT — only when the corresponding result is received to confirm the
commit is complete.
With this change we are effectively receiving the result of the commit,
eventually throwing an exception if it happened.
This allows a sync() call to establish an high level synchronization
point between the application and the server. If there were errors in
the processing so far, sync will throw the exception.
test: add test to verify the wrong array oid with numbers
The array binary dumper does the right thing; the text one picks
numeric[] unconditionally. It was clearly made on purpose, but #293
shows that it's a bad idea.