From b6f15d7407ab521f76b1fd4feee0c4abe2a47cc9 Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Sun, 5 Jun 2022 23:27:10 +0200 Subject: [PATCH] fix(crdb): pq tests adapted --- tests/fix_crdb.py | 9 +++++++-- tests/pq/test_async.py | 23 ++++++++++++++--------- tests/pq/test_copy.py | 2 ++ tests/pq/test_escaping.py | 8 +++++--- tests/pq/test_exec.py | 1 + tests/pq/test_misc.py | 1 + tests/pq/test_pgconn.py | 19 ++++++++++++++----- tests/pq/test_pgresult.py | 15 ++++++++++----- tests/test_sql.py | 10 ++++------ 9 files changed, 58 insertions(+), 30 deletions(-) diff --git a/tests/fix_crdb.py b/tests/fix_crdb.py index b9e8373f1..d989582c8 100644 --- a/tests/fix_crdb.py +++ b/tests/fix_crdb.py @@ -77,6 +77,10 @@ def crdb_time_precision(*args): return skip_crdb(*args, reason="time precision") +def crdb_scs_off(*args): + return skip_crdb(*args, reason="standard_conforming_strings=off") + + # mapping from reason description to ticket number _crdb_reasons = { "2-phase commit": 22329, @@ -86,8 +90,8 @@ _crdb_reasons = { "cast adds tz": 51692, "cidr": 18846, "composite": 27792, - "copy": 41608, "copy canceled": 81559, + "copy": 41608, "cursor with hold": 77101, "deferrable": 48307, "do": 17511, @@ -96,13 +100,14 @@ _crdb_reasons = { "infinity date": 41564, "interval style": 35807, "large objects": 243, - "server-side cursor": 41412, "negative interval": 81577, "nested array": 32552, "notify": 41522, "password_encryption": 42519, "pg_terminate_backend": 35897, "range": 41282, + "severity_nonlocalized": 81794, "scroll cursor": 77102, + "server-side cursor": 41412, "stored procedure": 1751, } diff --git a/tests/pq/test_async.py b/tests/pq/test_async.py index fee58f075..113b54844 100644 --- a/tests/pq/test_async.py +++ b/tests/pq/test_async.py @@ -1,5 +1,7 @@ -import pytest from select import select + +import pytest + import psycopg from psycopg import pq from psycopg.generators import execute @@ -15,7 +17,8 @@ def test_send_query(pgconn): # Long query to make sure we have to wait on send pgconn.send_query( - b"/* %s */ select pg_sleep(0.01); select 1 as foo;" % (b"x" * 1_000_000) + b"/* %s */ select 'x' as f from pg_sleep(0.01); select 1 as foo;" + % (b"x" * 1_000_000) ) # send loop @@ -53,8 +56,8 @@ def test_send_query(pgconn): assert len(results) == 2 assert results[0].nfields == 1 - assert results[0].fname(0) == b"pg_sleep" - assert results[0].get_value(0, 0) == b"" + assert results[0].fname(0) == b"f" + assert results[0].get_value(0, 0) == b"x" assert results[1].nfields == 1 assert results[1].fname(0) == b"foo" assert results[1].get_value(0, 0) == b"1" @@ -63,14 +66,15 @@ def test_send_query(pgconn): def test_send_query_compact_test(pgconn): # Like the above test but use psycopg facilities for compactness pgconn.send_query( - b"/* %s */ select pg_sleep(0.01); select 1 as foo;" % (b"x" * 1_000_000) + b"/* %s */ select 'x' as f from pg_sleep(0.01); select 1 as foo;" + % (b"x" * 1_000_000) ) results = execute_wait(pgconn) assert len(results) == 2 assert results[0].nfields == 1 - assert results[0].fname(0) == b"pg_sleep" - assert results[0].get_value(0, 0) == b"" + assert results[0].fname(0) == b"f" + assert results[0].get_value(0, 0) == b"x" assert results[1].nfields == 1 assert results[1].fname(0) == b"foo" assert results[1].get_value(0, 0) == b"1" @@ -169,7 +173,7 @@ def test_send_prepared_binary_out(pgconn, fmt, out): def test_send_describe_prepared(pgconn): - pgconn.send_prepare(b"prep", b"select $1::int + $2::int as fld") + pgconn.send_prepare(b"prep", b"select $1::int8 + $2::int8 as fld") (res,) = execute_wait(pgconn) assert res.status == pq.ExecStatus.COMMAND_OK, res.error_message @@ -178,13 +182,14 @@ def test_send_describe_prepared(pgconn): assert res.nfields == 1 assert res.ntuples == 0 assert res.fname(0) == b"fld" - assert res.ftype(0) == 23 + assert res.ftype(0) == 20 pgconn.finish() with pytest.raises(psycopg.OperationalError): pgconn.send_describe_prepared(b"prep") +@pytest.mark.crdb("skip", reason="server-side cursor") def test_send_describe_portal(pgconn): res = pgconn.exec_( b""" diff --git a/tests/pq/test_copy.py b/tests/pq/test_copy.py index d45416d27..cec848b2a 100644 --- a/tests/pq/test_copy.py +++ b/tests/pq/test_copy.py @@ -3,6 +3,8 @@ import pytest import psycopg from psycopg import pq +pytestmark = pytest.mark.crdb("skip", reason="copy") + sample_values = "values (10::int, 20::int, 'hello'::text), (40, NULL, 'world')" sample_tabledef = "col1 int primary key, col2 int, data text" diff --git a/tests/pq/test_escaping.py b/tests/pq/test_escaping.py index 59259c9b1..ad88d8a03 100644 --- a/tests/pq/test_escaping.py +++ b/tests/pq/test_escaping.py @@ -3,6 +3,8 @@ import pytest import psycopg from psycopg import pq +from ..fix_crdb import crdb_scs_off + @pytest.mark.parametrize( "data, want", @@ -19,7 +21,7 @@ def test_escape_literal(pgconn, data, want): assert out == want -@pytest.mark.parametrize("scs", ["on", "off"]) +@pytest.mark.parametrize("scs", ["on", crdb_scs_off("off")]) def test_escape_literal_1char(pgconn, scs): res = pgconn.exec_(f"set standard_conforming_strings to {scs}".encode("ascii")) assert res.status == pq.ExecStatus.COMMAND_OK @@ -58,7 +60,7 @@ def test_escape_identifier(pgconn, data, want): assert out == want -@pytest.mark.parametrize("scs", ["on", "off"]) +@pytest.mark.parametrize("scs", ["on", crdb_scs_off("off")]) def test_escape_identifier_1char(pgconn, scs): res = pgconn.exec_(f"set standard_conforming_strings to {scs}".encode("ascii")) assert res.status == pq.ExecStatus.COMMAND_OK @@ -97,7 +99,7 @@ def test_escape_string(pgconn, data, want): assert out == want -@pytest.mark.parametrize("scs", ["on", "off"]) +@pytest.mark.parametrize("scs", ["on", crdb_scs_off("off")]) def test_escape_string_1char(pgconn, scs): esc = pq.Escaping(pgconn) res = pgconn.exec_(f"set standard_conforming_strings to {scs}".encode("ascii")) diff --git a/tests/pq/test_exec.py b/tests/pq/test_exec.py index 136879317..3ae4eeb16 100644 --- a/tests/pq/test_exec.py +++ b/tests/pq/test_exec.py @@ -126,6 +126,7 @@ def test_exec_prepared_binary_out(pgconn, fmt, out): assert res.get_value(0, 0) == out +@pytest.mark.crdb("skip", reason="server-side cursor") def test_describe_portal(pgconn): res = pgconn.exec_( b""" diff --git a/tests/pq/test_misc.py b/tests/pq/test_misc.py index ca809a856..eef4d2dac 100644 --- a/tests/pq/test_misc.py +++ b/tests/pq/test_misc.py @@ -22,6 +22,7 @@ def test_error_message(pgconn): assert "NULL" in pq.error_message(pgconn) +@pytest.mark.crdb("skip", reason="encoding") def test_error_message_encoding(pgconn): res = pgconn.exec_(b"set client_encoding to latin9") assert res.status == pq.ExecStatus.COMMAND_OK diff --git a/tests/pq/test_pgconn.py b/tests/pq/test_pgconn.py index 4545ed59a..6f339617b 100644 --- a/tests/pq/test_pgconn.py +++ b/tests/pq/test_pgconn.py @@ -51,6 +51,7 @@ def test_connect_async(dsn): conn.connect_poll() +@pytest.mark.crdb("skip", reason="connects to any db name") def test_connect_async_bad(dsn): parsed_dsn = {e.keyword: e.val for e in pq.Conninfo.parse(dsn.encode()) if e.val} parsed_dsn[b"dbname"] = b"psycopg_test_not_for_real" @@ -130,6 +131,7 @@ def test_info(dsn, pgconn): pgconn.info +@pytest.mark.crdb("skip", reason="pg_terminate_backend") def test_reset(pgconn): assert pgconn.status == pq.ConnStatus.OK pgconn.exec_(b"select pg_terminate_backend(pg_backend_pid())") @@ -144,6 +146,7 @@ def test_reset(pgconn): assert pgconn.status == pq.ConnStatus.BAD +@pytest.mark.crdb("skip", reason="pg_terminate_backend") def test_reset_async(pgconn): assert pgconn.status == pq.ConnStatus.OK pgconn.exec_(b"select pg_terminate_backend(pg_backend_pid())") @@ -269,6 +272,7 @@ def test_parameter_status(dsn, monkeypatch): pgconn.parameter_status(b"application_name") +@pytest.mark.crdb("skip", reason="encoding") def test_encoding(pgconn): res = pgconn.exec_(b"set client_encoding to latin1") assert res.status == pq.ExecStatus.COMMAND_OK @@ -398,6 +402,7 @@ def test_cancel_free(pgconn): cancel.free() +@pytest.mark.crdb("skip", reason="notify") def test_notify(pgconn): assert pgconn.notifies() is None @@ -425,6 +430,7 @@ def test_notify(pgconn): assert pgconn.notifies() is None +@pytest.mark.crdb("skip", reason="do") def test_notice_nohandler(pgconn): pgconn.exec_(b"set client_min_messages to notice") res = pgconn.exec_( @@ -433,6 +439,7 @@ def test_notice_nohandler(pgconn): assert res.status == pq.ExecStatus.COMMAND_OK +@pytest.mark.crdb("skip", reason="do") def test_notice(pgconn): msgs = [] @@ -450,6 +457,7 @@ def test_notice(pgconn): assert msgs and msgs[0] == b"hello notice" +@pytest.mark.crdb("skip", reason="do") def test_notice_error(pgconn, caplog): caplog.set_level(logging.WARNING, logger="psycopg") @@ -492,13 +500,13 @@ def test_trace(pgconn, tmp_path): with tracef.open("w") as f: pgconn.trace(f.fileno()) pgconn.set_trace_flags(pq.Trace.SUPPRESS_TIMESTAMPS | pq.Trace.REGRESS_MODE) - pgconn.exec_(b"select 1") + pgconn.exec_(b"select 1::int4 as foo") pgconn.untrace() - pgconn.exec_(b"select 2") + pgconn.exec_(b"select 2::int4 as foo") traces = [line.split("\t") for line in tracef.read_text().splitlines()] assert traces == [ - ["F", "13", "Query", ' "select 1"'], - ["B", "33", "RowDescription", ' 1 "?column?" NNNN 0 NNNN 4 -1 0'], + ["F", "26", "Query", ' "select 1::int4 as foo"'], + ["B", "28", "RowDescription", ' 1 "foo" NNNN 0 NNNN 4 -1 0'], ["B", "11", "DataRow", " 1 1 '1'"], ["B", "13", "CommandComplete", ' "SELECT 1"'], ["B", "5", "ReadyForQuery", " I"], @@ -530,9 +538,10 @@ def test_encrypt_password_badalgo(pgconn): @pytest.mark.libpq(">= 10") +@pytest.mark.crdb("skip", reason="password_encryption") def test_encrypt_password_query(pgconn): res = pgconn.exec_(b"set password_encryption to 'md5'") - assert res.status == pq.ExecStatus.COMMAND_OK + assert res.status == pq.ExecStatus.COMMAND_OK, pgconn.error_message.decode() enc = pgconn.encrypt_password(b"psycopg2", b"ashesh") assert enc == b"md594839d658c28a357126f105b9cb14cfc" diff --git a/tests/pq/test_pgresult.py b/tests/pq/test_pgresult.py index 40ca33688..3ad818d09 100644 --- a/tests/pq/test_pgresult.py +++ b/tests/pq/test_pgresult.py @@ -52,7 +52,11 @@ def test_error_message(pgconn): def test_error_field(pgconn): res = pgconn.exec_(b"select wat") - assert res.error_field(pq.DiagnosticField.SEVERITY_NONLOCALIZED) == b"ERROR" + # https://github.com/cockroachdb/cockroach/issues/81794 + assert ( + res.error_field(pq.DiagnosticField.SEVERITY_NONLOCALIZED) + or res.error_field(pq.DiagnosticField.SEVERITY) + ) == b"ERROR" assert res.error_field(pq.DiagnosticField.SQLSTATE) == b"42703" assert b"wat" in res.error_field(pq.DiagnosticField.MESSAGE_PRIMARY) res.clear() @@ -86,6 +90,7 @@ def test_fname(pgconn): assert res.fname(0) is None +@pytest.mark.crdb("skip", reason="ftable") def test_ftable_and_col(pgconn): res = pgconn.exec_( b""" @@ -122,7 +127,7 @@ def test_fformat(pgconn, fmt): def test_ftype(pgconn): - res = pgconn.exec_(b"select 1::int, 1::numeric, 1::text") + res = pgconn.exec_(b"select 1::int4, 1::numeric, 1::text") assert res.status == pq.ExecStatus.TUPLES_OK, res.error_message assert res.ftype(0) == 23 assert res.ftype(1) == 1700 @@ -142,7 +147,7 @@ def test_fmod(pgconn): def test_fsize(pgconn): - res = pgconn.exec_(b"select 1::int, 1::bigint, 1::text") + res = pgconn.exec_(b"select 1::int4, 1::bigint, 1::text") assert res.status == pq.ExecStatus.TUPLES_OK, res.error_message assert res.fsize(0) == 4 assert res.fsize(1) == 8 @@ -162,7 +167,7 @@ def test_get_value(pgconn): def test_nparams_types(pgconn): - res = pgconn.prepare(b"", b"select $1::int, $2::text") + res = pgconn.prepare(b"", b"select $1::int4, $2::text") assert res.status == pq.ExecStatus.COMMAND_OK, res.error_message res = pgconn.describe_prepared(b"") @@ -180,7 +185,7 @@ def test_nparams_types(pgconn): def test_command_status(pgconn): res = pgconn.exec_(b"select 1") assert res.command_status == b"SELECT 1" - res = pgconn.exec_(b"set timezone to utf8") + res = pgconn.exec_(b"set timezone to utc") assert res.command_status == b"SET" res.clear() assert res.command_status is None diff --git a/tests/test_sql.py b/tests/test_sql.py index 991e43500..0f3cb23be 100644 --- a/tests/test_sql.py +++ b/tests/test_sql.py @@ -14,9 +14,7 @@ from psycopg.types import TypeInfo from psycopg.types.string import StrDumper from .utils import eur -from .fix_crdb import crdb_encoding - -crdb_skip_scs = pytest.mark.crdb("skip", reason="standard_conforming_strings=off") +from .fix_crdb import crdb_encoding, crdb_scs_off @pytest.mark.parametrize( @@ -33,7 +31,7 @@ def test_quote(obj, quoted): assert sql.quote(obj) == quoted -@pytest.mark.parametrize("scs", ["on", pytest.param("off", marks=crdb_skip_scs)]) +@pytest.mark.parametrize("scs", ["on", crdb_scs_off("off")]) def test_quote_roundtrip(conn, scs): messages = [] conn.add_notice_handler(lambda msg: messages.append(msg.message_primary)) @@ -49,8 +47,8 @@ def test_quote_roundtrip(conn, scs): assert not messages, f"error with {want!r}" -@crdb_skip_scs -def test_quote_stable_despite_deranged_libpq(conn): +@pytest.mark.parametrize("dummy", [crdb_scs_off("off")]) +def test_quote_stable_despite_deranged_libpq(conn, dummy): # Verify the libpq behaviour of PQescapeString using the last setting seen. # Check that we are not affected by it. good_str = " E'\\\\'" -- 2.47.2