diff -U3 /tmp/cirrus-ci-build/src/pl/plpython/expected/plpython_types.out /tmp/cirrus-ci-build/build/testrun/plpython-running/regress/results/plpython_types.out --- /tmp/cirrus-ci-build/src/pl/plpython/expected/plpython_types.out 2024-04-02 00:56:35.796880000 +0000 +++ /tmp/cirrus-ci-build/build/testrun/plpython-running/regress/results/plpython_types.out 2024-04-02 00:59:54.114433000 +0000 @@ -737,333 +737,10 @@ CREATE FUNCTION test_type_conversion_mdarray_malformed() RETURNS int[] AS $$ return [[1,2,3],[4,5]] $$ LANGUAGE plpython3u; -SELECT * FROM test_type_conversion_mdarray_malformed(); -ERROR: multidimensional arrays must have array expressions with matching dimensions -CONTEXT: while creating return value -PL/Python function "test_type_conversion_mdarray_malformed" -CREATE FUNCTION test_type_conversion_mdarray_malformed2() RETURNS text[] AS $$ -return [[1,2,3], "abc"] -$$ LANGUAGE plpython3u; -SELECT * FROM test_type_conversion_mdarray_malformed2(); -ERROR: multidimensional arrays must have array expressions with matching dimensions -CONTEXT: while creating return value -PL/Python function "test_type_conversion_mdarray_malformed2" -CREATE FUNCTION test_type_conversion_mdarray_malformed3() RETURNS text[] AS $$ -return ["abc", [1,2,3]] -$$ LANGUAGE plpython3u; -SELECT * FROM test_type_conversion_mdarray_malformed3(); -ERROR: multidimensional arrays must have array expressions with matching dimensions -CONTEXT: while creating return value -PL/Python function "test_type_conversion_mdarray_malformed3" -CREATE FUNCTION test_type_conversion_mdarray_toodeep() RETURNS int[] AS $$ -return [[[[[[[1]]]]]]] -$$ LANGUAGE plpython3u; -SELECT * FROM test_type_conversion_mdarray_toodeep(); -ERROR: number of array dimensions exceeds the maximum allowed (6) -CONTEXT: while creating return value -PL/Python function "test_type_conversion_mdarray_toodeep" -CREATE FUNCTION test_type_conversion_array_record() RETURNS type_record[] AS $$ -return [{'first': 'one', 'second': 42}, {'first': 'two', 'second': 11}] -$$ LANGUAGE plpython3u; -SELECT * FROM test_type_conversion_array_record(); - test_type_conversion_array_record ------------------------------------ - {"(one,42)","(two,11)"} -(1 row) - -CREATE FUNCTION test_type_conversion_array_string() RETURNS text[] AS $$ -return 'abc' -$$ LANGUAGE plpython3u; -SELECT * FROM test_type_conversion_array_string(); - test_type_conversion_array_string ------------------------------------ - {a,b,c} -(1 row) - -CREATE FUNCTION test_type_conversion_array_tuple() RETURNS text[] AS $$ -return ('abc', 'def') -$$ LANGUAGE plpython3u; -SELECT * FROM test_type_conversion_array_tuple(); - test_type_conversion_array_tuple ----------------------------------- - {abc,def} -(1 row) - -CREATE FUNCTION test_type_conversion_array_error() RETURNS int[] AS $$ -return 5 -$$ LANGUAGE plpython3u; -SELECT * FROM test_type_conversion_array_error(); -ERROR: return value of function with array return type is not a Python sequence -CONTEXT: while creating return value -PL/Python function "test_type_conversion_array_error" --- --- Domains over arrays --- -CREATE DOMAIN ordered_pair_domain AS integer[] CHECK (array_length(VALUE,1)=2 AND VALUE[1] < VALUE[2]); -CREATE FUNCTION test_type_conversion_array_domain(x ordered_pair_domain) RETURNS ordered_pair_domain AS $$ -plpy.info(x, type(x)) -return x -$$ LANGUAGE plpython3u; -SELECT * FROM test_type_conversion_array_domain(ARRAY[0, 100]::ordered_pair_domain); -INFO: ([0, 100], ) - test_type_conversion_array_domain ------------------------------------ - {0,100} -(1 row) - -SELECT * FROM test_type_conversion_array_domain(NULL::ordered_pair_domain); -INFO: (None, ) - test_type_conversion_array_domain ------------------------------------ - -(1 row) - -CREATE FUNCTION test_type_conversion_array_domain_check_violation() RETURNS ordered_pair_domain AS $$ -return [2,1] -$$ LANGUAGE plpython3u; -SELECT * FROM test_type_conversion_array_domain_check_violation(); -ERROR: value for domain ordered_pair_domain violates check constraint "ordered_pair_domain_check" -CONTEXT: while creating return value -PL/Python function "test_type_conversion_array_domain_check_violation" --- --- Arrays of domains --- -CREATE FUNCTION test_read_uint2_array(x uint2[]) RETURNS uint2 AS $$ -plpy.info(x, type(x)) -return x[0] -$$ LANGUAGE plpython3u; -select test_read_uint2_array(array[1::uint2]); -INFO: ([1], ) - test_read_uint2_array ------------------------ - 1 -(1 row) - -CREATE FUNCTION test_build_uint2_array(x int2) RETURNS uint2[] AS $$ -return [x, x] -$$ LANGUAGE plpython3u; -select test_build_uint2_array(1::int2); - test_build_uint2_array ------------------------- - {1,1} -(1 row) - -select test_build_uint2_array(-1::int2); -- fail -ERROR: value for domain uint2 violates check constraint "uint2_check" -CONTEXT: while creating return value -PL/Python function "test_build_uint2_array" --- --- ideally this would work, but for now it doesn't, because the return value --- is [[2,4], [2,4]] which our conversion code thinks should become a 2-D --- integer array, not an array of arrays. --- -CREATE FUNCTION test_type_conversion_domain_array(x integer[]) - RETURNS ordered_pair_domain[] AS $$ -return [x, x] -$$ LANGUAGE plpython3u; -select test_type_conversion_domain_array(array[2,4]); -ERROR: return value of function with array return type is not a Python sequence -CONTEXT: while creating return value -PL/Python function "test_type_conversion_domain_array" -select test_type_conversion_domain_array(array[4,2]); -- fail -ERROR: return value of function with array return type is not a Python sequence -CONTEXT: while creating return value -PL/Python function "test_type_conversion_domain_array" -CREATE FUNCTION test_type_conversion_domain_array2(x ordered_pair_domain) - RETURNS integer AS $$ -plpy.info(x, type(x)) -return x[1] -$$ LANGUAGE plpython3u; -select test_type_conversion_domain_array2(array[2,4]); -INFO: ([2, 4], ) - test_type_conversion_domain_array2 ------------------------------------- - 4 -(1 row) - -select test_type_conversion_domain_array2(array[4,2]); -- fail -ERROR: value for domain ordered_pair_domain violates check constraint "ordered_pair_domain_check" -CREATE FUNCTION test_type_conversion_array_domain_array(x ordered_pair_domain[]) - RETURNS ordered_pair_domain AS $$ -plpy.info(x, type(x)) -return x[0] -$$ LANGUAGE plpython3u; -select test_type_conversion_array_domain_array(array[array[2,4]::ordered_pair_domain]); -INFO: ([[2, 4]], ) - test_type_conversion_array_domain_array ------------------------------------------ - {2,4} -(1 row) - ---- ---- Composite types ---- -CREATE TABLE employee ( - name text, - basesalary integer, - bonus integer -); -INSERT INTO employee VALUES ('John', 100, 10), ('Mary', 200, 10); -CREATE OR REPLACE FUNCTION test_composite_table_input(e employee) RETURNS integer AS $$ -return e['basesalary'] + e['bonus'] -$$ LANGUAGE plpython3u; -SELECT name, test_composite_table_input(employee.*) FROM employee; - name | test_composite_table_input -------+---------------------------- - John | 110 - Mary | 210 -(2 rows) - -ALTER TABLE employee DROP bonus; -SELECT name, test_composite_table_input(employee.*) FROM employee; -ERROR: KeyError: 'bonus' -CONTEXT: Traceback (most recent call last): - PL/Python function "test_composite_table_input", line 2, in - return e['basesalary'] + e['bonus'] -PL/Python function "test_composite_table_input" -ALTER TABLE employee ADD bonus integer; -UPDATE employee SET bonus = 10; -SELECT name, test_composite_table_input(employee.*) FROM employee; - name | test_composite_table_input -------+---------------------------- - John | 110 - Mary | 210 -(2 rows) - -CREATE TYPE named_pair AS ( - i integer, - j integer -); -CREATE OR REPLACE FUNCTION test_composite_type_input(p named_pair) RETURNS integer AS $$ -return sum(p.values()) -$$ LANGUAGE plpython3u; -SELECT test_composite_type_input(row(1, 2)); - test_composite_type_input ---------------------------- - 3 -(1 row) - -ALTER TYPE named_pair RENAME TO named_pair_2; -SELECT test_composite_type_input(row(1, 2)); - test_composite_type_input ---------------------------- - 3 -(1 row) - --- --- Domains within composite --- -CREATE TYPE nnint_container AS (f1 int, f2 nnint); -CREATE FUNCTION nnint_test(x int, y int) RETURNS nnint_container AS $$ -return {'f1': x, 'f2': y} -$$ LANGUAGE plpython3u; -SELECT nnint_test(null, 3); - nnint_test ------------- - (,3) -(1 row) - -SELECT nnint_test(3, null); -- fail -ERROR: value for domain nnint violates check constraint "nnint_check" -CONTEXT: while creating return value -PL/Python function "nnint_test" --- --- Domains of composite --- -CREATE DOMAIN ordered_named_pair AS named_pair_2 CHECK((VALUE).i <= (VALUE).j); -CREATE FUNCTION read_ordered_named_pair(p ordered_named_pair) RETURNS integer AS $$ -return p['i'] + p['j'] -$$ LANGUAGE plpython3u; -SELECT read_ordered_named_pair(row(1, 2)); - read_ordered_named_pair -------------------------- - 3 -(1 row) - -SELECT read_ordered_named_pair(row(2, 1)); -- fail -ERROR: value for domain ordered_named_pair violates check constraint "ordered_named_pair_check" -CREATE FUNCTION build_ordered_named_pair(i int, j int) RETURNS ordered_named_pair AS $$ -return {'i': i, 'j': j} -$$ LANGUAGE plpython3u; -SELECT build_ordered_named_pair(1,2); - build_ordered_named_pair --------------------------- - (1,2) -(1 row) - -SELECT build_ordered_named_pair(2,1); -- fail -ERROR: value for domain ordered_named_pair violates check constraint "ordered_named_pair_check" -CONTEXT: while creating return value -PL/Python function "build_ordered_named_pair" -CREATE FUNCTION build_ordered_named_pairs(i int, j int) RETURNS ordered_named_pair[] AS $$ -return [{'i': i, 'j': j}, {'i': i, 'j': j+1}] -$$ LANGUAGE plpython3u; -SELECT build_ordered_named_pairs(1,2); - build_ordered_named_pairs ---------------------------- - {"(1,2)","(1,3)"} -(1 row) - -SELECT build_ordered_named_pairs(2,1); -- fail -ERROR: value for domain ordered_named_pair violates check constraint "ordered_named_pair_check" -CONTEXT: while creating return value -PL/Python function "build_ordered_named_pairs" --- --- Prepared statements --- -CREATE OR REPLACE FUNCTION test_prep_bool_input() RETURNS int -LANGUAGE plpython3u -AS $$ -plan = plpy.prepare("SELECT CASE WHEN $1 THEN 1 ELSE 0 END AS val", ['boolean']) -rv = plpy.execute(plan, ['fa'], 5) # 'fa' is true in Python -return rv[0]['val'] -$$; -SELECT test_prep_bool_input(); -- 1 - test_prep_bool_input ----------------------- - 1 -(1 row) - -CREATE OR REPLACE FUNCTION test_prep_bool_output() RETURNS bool -LANGUAGE plpython3u -AS $$ -plan = plpy.prepare("SELECT $1 = 1 AS val", ['int']) -rv = plpy.execute(plan, [0], 5) -plpy.info(rv[0]) -return rv[0]['val'] -$$; -SELECT test_prep_bool_output(); -- false -INFO: {'val': False} - test_prep_bool_output ------------------------ - f -(1 row) - -CREATE OR REPLACE FUNCTION test_prep_bytea_input(bb bytea) RETURNS int -LANGUAGE plpython3u -AS $$ -plan = plpy.prepare("SELECT octet_length($1) AS val", ['bytea']) -rv = plpy.execute(plan, [bb], 5) -return rv[0]['val'] -$$; -SELECT test_prep_bytea_input(E'a\\000b'); -- 3 (embedded null formerly truncated value) - test_prep_bytea_input ------------------------ - 3 -(1 row) - -CREATE OR REPLACE FUNCTION test_prep_bytea_output() RETURNS bytea -LANGUAGE plpython3u -AS $$ -plan = plpy.prepare("SELECT decode('aa00bb', 'hex') AS val") -rv = plpy.execute(plan, [], 5) -plpy.info(rv[0]) -return rv[0]['val'] -$$; -SELECT test_prep_bytea_output(); -INFO: {'val': b'\xaa\x00\xbb'} - test_prep_bytea_output ------------------------- - \xaa00bb -(1 row) - +WARNING: terminating connection because of crash of another server process +DETAIL: The postmaster has commanded this server process to roll back the current transaction and exit, because another server process exited abnormally and possibly corrupted shared memory. +HINT: In a moment you should be able to reconnect to the database and repeat your command. +server closed the connection unexpectedly + This probably means the server terminated abnormally + before or while processing the request. +connection to server was lost diff -U3 /tmp/cirrus-ci-build/src/pl/plpython/expected/plpython_error.out /tmp/cirrus-ci-build/build/testrun/plpython-running/regress/results/plpython_error.out --- /tmp/cirrus-ci-build/src/pl/plpython/expected/plpython_error.out 2024-04-02 00:56:35.795242000 +0000 +++ /tmp/cirrus-ci-build/build/testrun/plpython-running/regress/results/plpython_error.out 2024-04-02 00:59:54.125885000 +0000 @@ -1,460 +1,2 @@ --- test error handling, i forgot to restore Warn_restart in --- the trigger handler once. the errors and subsequent core dump were --- interesting. -/* Flat out Python syntax error - */ -CREATE FUNCTION python_syntax_error() RETURNS text - AS -'.syntaxerror' - LANGUAGE plpython3u; -ERROR: could not compile PL/Python function "python_syntax_error" -DETAIL: SyntaxError: invalid syntax (, line 2) -/* With check_function_bodies = false the function should get defined - * and the error reported when called - */ -SET check_function_bodies = false; -CREATE FUNCTION python_syntax_error() RETURNS text - AS -'.syntaxerror' - LANGUAGE plpython3u; -SELECT python_syntax_error(); -ERROR: could not compile PL/Python function "python_syntax_error" -DETAIL: SyntaxError: invalid syntax (, line 2) -/* Run the function twice to check if the hashtable entry gets cleaned up */ -SELECT python_syntax_error(); -ERROR: could not compile PL/Python function "python_syntax_error" -DETAIL: SyntaxError: invalid syntax (, line 2) -RESET check_function_bodies; -/* Flat out syntax error - */ -CREATE FUNCTION sql_syntax_error() RETURNS text - AS -'plpy.execute("syntax error")' - LANGUAGE plpython3u; -SELECT sql_syntax_error(); -ERROR: spiexceptions.SyntaxError: syntax error at or near "syntax" -LINE 1: syntax error - ^ -QUERY: syntax error -CONTEXT: Traceback (most recent call last): - PL/Python function "sql_syntax_error", line 1, in - plpy.execute("syntax error") -PL/Python function "sql_syntax_error" -/* check the handling of uncaught python exceptions - */ -CREATE FUNCTION exception_index_invalid(text) RETURNS text - AS -'return args[1]' - LANGUAGE plpython3u; -SELECT exception_index_invalid('test'); -ERROR: IndexError: list index out of range -CONTEXT: Traceback (most recent call last): - PL/Python function "exception_index_invalid", line 1, in - return args[1] -PL/Python function "exception_index_invalid" -/* check handling of nested exceptions - */ -CREATE FUNCTION exception_index_invalid_nested() RETURNS text - AS -'rv = plpy.execute("SELECT test5(''foo'')") -return rv[0]' - LANGUAGE plpython3u; -SELECT exception_index_invalid_nested(); -ERROR: spiexceptions.UndefinedFunction: function test5(unknown) does not exist -LINE 1: SELECT test5('foo') - ^ -HINT: No function matches the given name and argument types. You might need to add explicit type casts. -QUERY: SELECT test5('foo') -CONTEXT: Traceback (most recent call last): - PL/Python function "exception_index_invalid_nested", line 1, in - rv = plpy.execute("SELECT test5('foo')") -PL/Python function "exception_index_invalid_nested" -/* a typo - */ -CREATE FUNCTION invalid_type_uncaught(a text) RETURNS text - AS -'if "plan" not in SD: - q = "SELECT fname FROM users WHERE lname = $1" - SD["plan"] = plpy.prepare(q, [ "test" ]) -rv = plpy.execute(SD["plan"], [ a ]) -if len(rv): - return rv[0]["fname"] -return None -' - LANGUAGE plpython3u; -SELECT invalid_type_uncaught('rick'); -ERROR: spiexceptions.UndefinedObject: type "test" does not exist -CONTEXT: Traceback (most recent call last): - PL/Python function "invalid_type_uncaught", line 3, in - SD["plan"] = plpy.prepare(q, [ "test" ]) -PL/Python function "invalid_type_uncaught" -/* for what it's worth catch the exception generated by - * the typo, and return None - */ -CREATE FUNCTION invalid_type_caught(a text) RETURNS text - AS -'if "plan" not in SD: - q = "SELECT fname FROM users WHERE lname = $1" - try: - SD["plan"] = plpy.prepare(q, [ "test" ]) - except plpy.SPIError as ex: - plpy.notice(str(ex)) - return None -rv = plpy.execute(SD["plan"], [ a ]) -if len(rv): - return rv[0]["fname"] -return None -' - LANGUAGE plpython3u; -SELECT invalid_type_caught('rick'); -NOTICE: type "test" does not exist - invalid_type_caught ---------------------- - -(1 row) - -/* for what it's worth catch the exception generated by - * the typo, and reraise it as a plain error - */ -CREATE FUNCTION invalid_type_reraised(a text) RETURNS text - AS -'if "plan" not in SD: - q = "SELECT fname FROM users WHERE lname = $1" - try: - SD["plan"] = plpy.prepare(q, [ "test" ]) - except plpy.SPIError as ex: - plpy.error(str(ex)) -rv = plpy.execute(SD["plan"], [ a ]) -if len(rv): - return rv[0]["fname"] -return None -' - LANGUAGE plpython3u; -SELECT invalid_type_reraised('rick'); -ERROR: plpy.Error: type "test" does not exist -CONTEXT: Traceback (most recent call last): - PL/Python function "invalid_type_reraised", line 6, in - plpy.error(str(ex)) -PL/Python function "invalid_type_reraised" -/* no typo no messing about - */ -CREATE FUNCTION valid_type(a text) RETURNS text - AS -'if "plan" not in SD: - SD["plan"] = plpy.prepare("SELECT fname FROM users WHERE lname = $1", [ "text" ]) -rv = plpy.execute(SD["plan"], [ a ]) -if len(rv): - return rv[0]["fname"] -return None -' - LANGUAGE plpython3u; -SELECT valid_type('rick'); - valid_type ------------- - -(1 row) - -/* error in nested functions to get a traceback -*/ -CREATE FUNCTION nested_error() RETURNS text - AS -'def fun1(): - plpy.error("boom") - -def fun2(): - fun1() - -def fun3(): - fun2() - -fun3() -return "not reached" -' - LANGUAGE plpython3u; -SELECT nested_error(); -ERROR: plpy.Error: boom -CONTEXT: Traceback (most recent call last): - PL/Python function "nested_error", line 10, in - fun3() - PL/Python function "nested_error", line 8, in fun3 - fun2() - PL/Python function "nested_error", line 5, in fun2 - fun1() - PL/Python function "nested_error", line 2, in fun1 - plpy.error("boom") -PL/Python function "nested_error" -/* raising plpy.Error is just like calling plpy.error -*/ -CREATE FUNCTION nested_error_raise() RETURNS text - AS -'def fun1(): - raise plpy.Error("boom") - -def fun2(): - fun1() - -def fun3(): - fun2() - -fun3() -return "not reached" -' - LANGUAGE plpython3u; -SELECT nested_error_raise(); -ERROR: plpy.Error: boom -CONTEXT: Traceback (most recent call last): - PL/Python function "nested_error_raise", line 10, in - fun3() - PL/Python function "nested_error_raise", line 8, in fun3 - fun2() - PL/Python function "nested_error_raise", line 5, in fun2 - fun1() - PL/Python function "nested_error_raise", line 2, in fun1 - raise plpy.Error("boom") -PL/Python function "nested_error_raise" -/* using plpy.warning should not produce a traceback -*/ -CREATE FUNCTION nested_warning() RETURNS text - AS -'def fun1(): - plpy.warning("boom") - -def fun2(): - fun1() - -def fun3(): - fun2() - -fun3() -return "you''ve been warned" -' - LANGUAGE plpython3u; -SELECT nested_warning(); -WARNING: boom - nested_warning --------------------- - you've been warned -(1 row) - -/* AttributeError at toplevel used to give segfaults with the traceback -*/ -CREATE FUNCTION toplevel_attribute_error() RETURNS void AS -$$ -plpy.nonexistent -$$ LANGUAGE plpython3u; -SELECT toplevel_attribute_error(); -ERROR: AttributeError: 'module' object has no attribute 'nonexistent' -CONTEXT: Traceback (most recent call last): - PL/Python function "toplevel_attribute_error", line 2, in - plpy.nonexistent -PL/Python function "toplevel_attribute_error" -/* Calling PL/Python functions from SQL and vice versa should not lose context. - */ -CREATE OR REPLACE FUNCTION python_traceback() RETURNS void AS $$ -def first(): - second() - -def second(): - third() - -def third(): - plpy.execute("select sql_error()") - -first() -$$ LANGUAGE plpython3u; -CREATE OR REPLACE FUNCTION sql_error() RETURNS void AS $$ -begin - select 1/0; -end -$$ LANGUAGE plpgsql; -CREATE OR REPLACE FUNCTION python_from_sql_error() RETURNS void AS $$ -begin - select python_traceback(); -end -$$ LANGUAGE plpgsql; -CREATE OR REPLACE FUNCTION sql_from_python_error() RETURNS void AS $$ -plpy.execute("select sql_error()") -$$ LANGUAGE plpython3u; -SELECT python_traceback(); -ERROR: spiexceptions.DivisionByZero: division by zero -CONTEXT: Traceback (most recent call last): - PL/Python function "python_traceback", line 11, in - first() - PL/Python function "python_traceback", line 3, in first - second() - PL/Python function "python_traceback", line 6, in second - third() - PL/Python function "python_traceback", line 9, in third - plpy.execute("select sql_error()") -PL/Python function "python_traceback" -SELECT sql_error(); -ERROR: division by zero -CONTEXT: SQL statement "select 1/0" -PL/pgSQL function sql_error() line 3 at SQL statement -SELECT python_from_sql_error(); -ERROR: spiexceptions.DivisionByZero: division by zero -CONTEXT: Traceback (most recent call last): - PL/Python function "python_traceback", line 11, in - first() - PL/Python function "python_traceback", line 3, in first - second() - PL/Python function "python_traceback", line 6, in second - third() - PL/Python function "python_traceback", line 9, in third - plpy.execute("select sql_error()") -PL/Python function "python_traceback" -SQL statement "select python_traceback()" -PL/pgSQL function python_from_sql_error() line 3 at SQL statement -SELECT sql_from_python_error(); -ERROR: spiexceptions.DivisionByZero: division by zero -CONTEXT: Traceback (most recent call last): - PL/Python function "sql_from_python_error", line 2, in - plpy.execute("select sql_error()") -PL/Python function "sql_from_python_error" -/* check catching specific types of exceptions - */ -CREATE TABLE specific ( - i integer PRIMARY KEY -); -CREATE FUNCTION specific_exception(i integer) RETURNS void AS -$$ -from plpy import spiexceptions -try: - plpy.execute("insert into specific values (%s)" % (i or "NULL")); -except spiexceptions.NotNullViolation as e: - plpy.notice("Violated the NOT NULL constraint, sqlstate %s" % e.sqlstate) -except spiexceptions.UniqueViolation as e: - plpy.notice("Violated the UNIQUE constraint, sqlstate %s" % e.sqlstate) -$$ LANGUAGE plpython3u; -SELECT specific_exception(2); - specific_exception --------------------- - -(1 row) - -SELECT specific_exception(NULL); -NOTICE: Violated the NOT NULL constraint, sqlstate 23502 - specific_exception --------------------- - -(1 row) - -SELECT specific_exception(2); -NOTICE: Violated the UNIQUE constraint, sqlstate 23505 - specific_exception --------------------- - -(1 row) - -/* SPI errors in PL/Python functions should preserve the SQLSTATE value - */ -CREATE FUNCTION python_unique_violation() RETURNS void AS $$ -plpy.execute("insert into specific values (1)") -plpy.execute("insert into specific values (1)") -$$ LANGUAGE plpython3u; -CREATE FUNCTION catch_python_unique_violation() RETURNS text AS $$ -begin - begin - perform python_unique_violation(); - exception when unique_violation then - return 'ok'; - end; - return 'not reached'; -end; -$$ language plpgsql; -SELECT catch_python_unique_violation(); - catch_python_unique_violation -------------------------------- - ok -(1 row) - -/* manually starting subtransactions - a bad idea - */ -CREATE FUNCTION manual_subxact() RETURNS void AS $$ -plpy.execute("savepoint save") -plpy.execute("create table foo(x integer)") -plpy.execute("rollback to save") -$$ LANGUAGE plpython3u; -SELECT manual_subxact(); -ERROR: plpy.SPIError: SPI_execute failed: SPI_ERROR_TRANSACTION -CONTEXT: Traceback (most recent call last): - PL/Python function "manual_subxact", line 2, in - plpy.execute("savepoint save") -PL/Python function "manual_subxact" -/* same for prepared plans - */ -CREATE FUNCTION manual_subxact_prepared() RETURNS void AS $$ -save = plpy.prepare("savepoint save") -rollback = plpy.prepare("rollback to save") -plpy.execute(save) -plpy.execute("create table foo(x integer)") -plpy.execute(rollback) -$$ LANGUAGE plpython3u; -SELECT manual_subxact_prepared(); -ERROR: plpy.SPIError: SPI_execute_plan failed: SPI_ERROR_TRANSACTION -CONTEXT: Traceback (most recent call last): - PL/Python function "manual_subxact_prepared", line 4, in - plpy.execute(save) -PL/Python function "manual_subxact_prepared" -/* raising plpy.spiexception.* from python code should preserve sqlstate - */ -CREATE FUNCTION plpy_raise_spiexception() RETURNS void AS $$ -raise plpy.spiexceptions.DivisionByZero() -$$ LANGUAGE plpython3u; -DO $$ -BEGIN - SELECT plpy_raise_spiexception(); -EXCEPTION WHEN division_by_zero THEN - -- NOOP -END -$$ LANGUAGE plpgsql; -/* setting a custom sqlstate should be handled - */ -CREATE FUNCTION plpy_raise_spiexception_override() RETURNS void AS $$ -exc = plpy.spiexceptions.DivisionByZero() -exc.sqlstate = 'SILLY' -raise exc -$$ LANGUAGE plpython3u; -DO $$ -BEGIN - SELECT plpy_raise_spiexception_override(); -EXCEPTION WHEN SQLSTATE 'SILLY' THEN - -- NOOP -END -$$ LANGUAGE plpgsql; -/* test the context stack trace for nested execution levels - */ -CREATE FUNCTION notice_innerfunc() RETURNS int AS $$ -plpy.execute("DO LANGUAGE plpython3u $x$ plpy.notice('inside DO') $x$") -return 1 -$$ LANGUAGE plpython3u; -CREATE FUNCTION notice_outerfunc() RETURNS int AS $$ -plpy.execute("SELECT notice_innerfunc()") -return 1 -$$ LANGUAGE plpython3u; -\set SHOW_CONTEXT always -SELECT notice_outerfunc(); -NOTICE: inside DO -CONTEXT: PL/Python anonymous code block -SQL statement "DO LANGUAGE plpython3u $x$ plpy.notice('inside DO') $x$" -PL/Python function "notice_innerfunc" -SQL statement "SELECT notice_innerfunc()" -PL/Python function "notice_outerfunc" - notice_outerfunc ------------------- - 1 -(1 row) - -/* test error logged with an underlying exception that includes a detail - * string (bug #18070). - */ -CREATE FUNCTION python_error_detail() RETURNS SETOF text AS $$ - plan = plpy.prepare("SELECT to_date('xy', 'DD') d") - for row in plpy.cursor(plan): - yield row['d'] -$$ LANGUAGE plpython3u; -SELECT python_error_detail(); -ERROR: error fetching next item from iterator -DETAIL: spiexceptions.InvalidDatetimeFormat: invalid value "xy" for "DD" -CONTEXT: Traceback (most recent call last): -PL/Python function "python_error_detail" +psql: error: connection to server on socket "/tmp/.s.PGSQL.5432" failed: No such file or directory + Is the server running locally and accepting connections on that socket? diff -U3 /tmp/cirrus-ci-build/src/pl/plpython/expected/plpython_ereport.out /tmp/cirrus-ci-build/build/testrun/plpython-running/regress/results/plpython_ereport.out --- /tmp/cirrus-ci-build/src/pl/plpython/expected/plpython_ereport.out 2024-04-02 00:56:35.795085000 +0000 +++ /tmp/cirrus-ci-build/build/testrun/plpython-running/regress/results/plpython_ereport.out 2024-04-02 00:59:54.141353000 +0000 @@ -1,214 +1,2 @@ -CREATE FUNCTION elog_test() RETURNS void -AS $$ -plpy.debug('debug', detail='some detail') -plpy.log('log', detail='some detail') -plpy.info('info', detail='some detail') -plpy.info() -plpy.info('the question', detail=42); -plpy.info('This is message text.', - detail='This is detail text', - hint='This is hint text.', - sqlstate='XX000', - schema_name='any info about schema', - table_name='any info about table', - column_name='any info about column', - datatype_name='any info about datatype', - constraint_name='any info about constraint') -plpy.notice('notice', detail='some detail') -plpy.warning('warning', detail='some detail') -plpy.error('stop on error', detail='some detail', hint='some hint') -$$ LANGUAGE plpython3u; -SELECT elog_test(); -INFO: info -DETAIL: some detail -INFO: () -INFO: the question -DETAIL: 42 -INFO: This is message text. -DETAIL: This is detail text -HINT: This is hint text. -NOTICE: notice -DETAIL: some detail -WARNING: warning -DETAIL: some detail -ERROR: plpy.Error: stop on error -DETAIL: some detail -HINT: some hint -CONTEXT: Traceback (most recent call last): - PL/Python function "elog_test", line 18, in - plpy.error('stop on error', detail='some detail', hint='some hint') -PL/Python function "elog_test" -DO $$ plpy.info('other types', detail=(10, 20)) $$ LANGUAGE plpython3u; -INFO: other types -DETAIL: (10, 20) -DO $$ -import time; -from datetime import date -plpy.info('other types', detail=date(2016, 2, 26)) -$$ LANGUAGE plpython3u; -INFO: other types -DETAIL: 2016-02-26 -DO $$ -basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana'] -plpy.info('other types', detail=basket) -$$ LANGUAGE plpython3u; -INFO: other types -DETAIL: ['apple', 'orange', 'apple', 'pear', 'orange', 'banana'] --- should fail -DO $$ plpy.info('wrong sqlstate', sqlstate='54444A') $$ LANGUAGE plpython3u; -ERROR: ValueError: invalid SQLSTATE code -CONTEXT: Traceback (most recent call last): - PL/Python anonymous code block, line 1, in - plpy.info('wrong sqlstate', sqlstate='54444A') -PL/Python anonymous code block -DO $$ plpy.info('unsupported argument', blabla='fooboo') $$ LANGUAGE plpython3u; -ERROR: TypeError: 'blabla' is an invalid keyword argument for this function -CONTEXT: Traceback (most recent call last): - PL/Python anonymous code block, line 1, in - plpy.info('unsupported argument', blabla='fooboo') -PL/Python anonymous code block -DO $$ plpy.info('first message', message='second message') $$ LANGUAGE plpython3u; -ERROR: TypeError: argument 'message' given by name and position -CONTEXT: Traceback (most recent call last): - PL/Python anonymous code block, line 1, in - plpy.info('first message', message='second message') -PL/Python anonymous code block -DO $$ plpy.info('first message', 'second message', message='third message') $$ LANGUAGE plpython3u; -ERROR: TypeError: argument 'message' given by name and position -CONTEXT: Traceback (most recent call last): - PL/Python anonymous code block, line 1, in - plpy.info('first message', 'second message', message='third message') -PL/Python anonymous code block --- raise exception in python, handle exception in plpgsql -CREATE OR REPLACE FUNCTION raise_exception(_message text, _detail text DEFAULT NULL, _hint text DEFAULT NULL, - _sqlstate text DEFAULT NULL, - _schema_name text DEFAULT NULL, - _table_name text DEFAULT NULL, - _column_name text DEFAULT NULL, - _datatype_name text DEFAULT NULL, - _constraint_name text DEFAULT NULL) -RETURNS void AS $$ -kwargs = { - "message": _message, "detail": _detail, "hint": _hint, - "sqlstate": _sqlstate, "schema_name": _schema_name, "table_name": _table_name, - "column_name": _column_name, "datatype_name": _datatype_name, - "constraint_name": _constraint_name -} -# ignore None values -plpy.error(**dict((k, v) for k, v in iter(kwargs.items()) if v)) -$$ LANGUAGE plpython3u; -SELECT raise_exception('hello', 'world'); -ERROR: plpy.Error: hello -DETAIL: world -CONTEXT: Traceback (most recent call last): - PL/Python function "raise_exception", line 9, in - plpy.error(**dict((k, v) for k, v in iter(kwargs.items()) if v)) -PL/Python function "raise_exception" -SELECT raise_exception('message text', 'detail text', _sqlstate => 'YY333'); -ERROR: plpy.Error: message text -DETAIL: detail text -CONTEXT: Traceback (most recent call last): - PL/Python function "raise_exception", line 9, in - plpy.error(**dict((k, v) for k, v in iter(kwargs.items()) if v)) -PL/Python function "raise_exception" -SELECT raise_exception(_message => 'message text', - _detail => 'detail text', - _hint => 'hint text', - _sqlstate => 'XX555', - _schema_name => 'schema text', - _table_name => 'table text', - _column_name => 'column text', - _datatype_name => 'datatype text', - _constraint_name => 'constraint text'); -ERROR: plpy.Error: message text -DETAIL: detail text -HINT: hint text -CONTEXT: Traceback (most recent call last): - PL/Python function "raise_exception", line 9, in - plpy.error(**dict((k, v) for k, v in iter(kwargs.items()) if v)) -PL/Python function "raise_exception" -SELECT raise_exception(_message => 'message text', - _hint => 'hint text', - _schema_name => 'schema text', - _column_name => 'column text', - _constraint_name => 'constraint text'); -ERROR: plpy.Error: message text -HINT: hint text -CONTEXT: Traceback (most recent call last): - PL/Python function "raise_exception", line 9, in - plpy.error(**dict((k, v) for k, v in iter(kwargs.items()) if v)) -PL/Python function "raise_exception" -DO $$ -DECLARE - __message text; - __detail text; - __hint text; - __sqlstate text; - __schema_name text; - __table_name text; - __column_name text; - __datatype_name text; - __constraint_name text; -BEGIN - BEGIN - PERFORM raise_exception(_message => 'message text', - _detail => 'detail text', - _hint => 'hint text', - _sqlstate => 'XX555', - _schema_name => 'schema text', - _table_name => 'table text', - _column_name => 'column text', - _datatype_name => 'datatype text', - _constraint_name => 'constraint text'); - EXCEPTION WHEN SQLSTATE 'XX555' THEN - GET STACKED DIAGNOSTICS __message = MESSAGE_TEXT, - __detail = PG_EXCEPTION_DETAIL, - __hint = PG_EXCEPTION_HINT, - __sqlstate = RETURNED_SQLSTATE, - __schema_name = SCHEMA_NAME, - __table_name = TABLE_NAME, - __column_name = COLUMN_NAME, - __datatype_name = PG_DATATYPE_NAME, - __constraint_name = CONSTRAINT_NAME; - RAISE NOTICE 'handled exception' - USING DETAIL = format('message:(%s), detail:(%s), hint: (%s), sqlstate: (%s), ' - 'schema_name:(%s), table_name:(%s), column_name:(%s), datatype_name:(%s), constraint_name:(%s)', - __message, __detail, __hint, __sqlstate, __schema_name, - __table_name, __column_name, __datatype_name, __constraint_name); - END; -END; -$$; -NOTICE: handled exception -DETAIL: message:(plpy.Error: message text), detail:(detail text), hint: (hint text), sqlstate: (XX555), schema_name:(schema text), table_name:(table text), column_name:(column text), datatype_name:(datatype text), constraint_name:(constraint text) -DO $$ -try: - plpy.execute("select raise_exception(_message => 'my message', _sqlstate => 'XX987', _hint => 'some hint', _table_name => 'users_tab', _datatype_name => 'user_type')") -except Exception as e: - plpy.info(e.spidata) - raise e -$$ LANGUAGE plpython3u; -INFO: (119577128, None, 'some hint', None, 0, None, 'users_tab', None, 'user_type', None) -ERROR: plpy.SPIError: plpy.Error: my message -HINT: some hint -CONTEXT: Traceback (most recent call last): - PL/Python anonymous code block, line 6, in - raise e - PL/Python anonymous code block, line 3, in __plpython_inline_block - plpy.execute("select raise_exception(_message => 'my message', _sqlstate => 'XX987', _hint => 'some hint', _table_name => 'users_tab', _datatype_name => 'user_type')") -PL/Python anonymous code block -DO $$ -try: - plpy.error(message = 'my message', sqlstate = 'XX987', hint = 'some hint', table_name = 'users_tab', datatype_name = 'user_type') -except Exception as e: - plpy.info('sqlstate: %s, hint: %s, table_name: %s, datatype_name: %s' % (e.sqlstate, e.hint, e.table_name, e.datatype_name)) - raise e -$$ LANGUAGE plpython3u; -INFO: sqlstate: XX987, hint: some hint, table_name: users_tab, datatype_name: user_type -ERROR: plpy.Error: my message -HINT: some hint -CONTEXT: Traceback (most recent call last): - PL/Python anonymous code block, line 6, in - raise e - PL/Python anonymous code block, line 3, in __plpython_inline_block - plpy.error(message = 'my message', sqlstate = 'XX987', hint = 'some hint', table_name = 'users_tab', datatype_name = 'user_type') -PL/Python anonymous code block +psql: error: connection to server on socket "/tmp/.s.PGSQL.5432" failed: No such file or directory + Is the server running locally and accepting connections on that socket? diff -U3 /tmp/cirrus-ci-build/src/pl/plpython/expected/plpython_unicode.out /tmp/cirrus-ci-build/build/testrun/plpython-running/regress/results/plpython_unicode.out --- /tmp/cirrus-ci-build/src/pl/plpython/expected/plpython_unicode.out 2024-04-02 00:56:35.796938000 +0000 +++ /tmp/cirrus-ci-build/build/testrun/plpython-running/regress/results/plpython_unicode.out 2024-04-02 00:59:54.152742000 +0000 @@ -1,56 +1,2 @@ --- --- Unicode handling --- --- Note: this test case is known to fail if the database encoding is --- EUC_CN, EUC_JP, EUC_KR, or EUC_TW, for lack of any equivalent to --- U+00A0 (no-break space) in those encodings. However, testing with --- plain ASCII data would be rather useless, so we must live with that. --- -SET client_encoding TO UTF8; -CREATE TABLE unicode_test ( - testvalue text NOT NULL -); -CREATE FUNCTION unicode_return() RETURNS text AS E' -return "\\xA0" -' LANGUAGE plpython3u; -CREATE FUNCTION unicode_trigger() RETURNS trigger AS E' -TD["new"]["testvalue"] = "\\xA0" -return "MODIFY" -' LANGUAGE plpython3u; -CREATE TRIGGER unicode_test_bi BEFORE INSERT ON unicode_test - FOR EACH ROW EXECUTE PROCEDURE unicode_trigger(); -CREATE FUNCTION unicode_plan1() RETURNS text AS E' -plan = plpy.prepare("SELECT $1 AS testvalue", ["text"]) -rv = plpy.execute(plan, ["\\xA0"], 1) -return rv[0]["testvalue"] -' LANGUAGE plpython3u; -CREATE FUNCTION unicode_plan2() RETURNS text AS E' -plan = plpy.prepare("SELECT $1 || $2 AS testvalue", ["text", "text"]) -rv = plpy.execute(plan, ["foo", "bar"], 1) -return rv[0]["testvalue"] -' LANGUAGE plpython3u; -SELECT unicode_return(); - unicode_return ----------------- -   -(1 row) - -INSERT INTO unicode_test (testvalue) VALUES ('test'); -SELECT * FROM unicode_test; - testvalue ------------ -   -(1 row) - -SELECT unicode_plan1(); - unicode_plan1 ---------------- -   -(1 row) - -SELECT unicode_plan2(); - unicode_plan2 ---------------- - foobar -(1 row) - +psql: error: connection to server on socket "/tmp/.s.PGSQL.5432" failed: No such file or directory + Is the server running locally and accepting connections on that socket? diff -U3 /tmp/cirrus-ci-build/src/pl/plpython/expected/plpython_quote.out /tmp/cirrus-ci-build/build/testrun/plpython-running/regress/results/plpython_quote.out --- /tmp/cirrus-ci-build/src/pl/plpython/expected/plpython_quote.out 2024-04-02 00:56:35.795696000 +0000 +++ /tmp/cirrus-ci-build/build/testrun/plpython-running/regress/results/plpython_quote.out 2024-04-02 00:59:54.158822000 +0000 @@ -1,56 +1,2 @@ --- test quoting functions -CREATE FUNCTION quote(t text, how text) RETURNS text AS $$ - if how == "literal": - return plpy.quote_literal(t) - elif how == "nullable": - return plpy.quote_nullable(t) - elif how == "ident": - return plpy.quote_ident(t) - else: - raise plpy.Error("unrecognized quote type %s" % how) -$$ LANGUAGE plpython3u; -SELECT quote(t, 'literal') FROM (VALUES - ('abc'), - ('a''bc'), - ('''abc'''), - (''), - (''''), - ('xyzv')) AS v(t); - quote ------------ - 'abc' - 'a''bc' - '''abc''' - '' - '''' - 'xyzv' -(6 rows) - -SELECT quote(t, 'nullable') FROM (VALUES - ('abc'), - ('a''bc'), - ('''abc'''), - (''), - (''''), - (NULL)) AS v(t); - quote ------------ - 'abc' - 'a''bc' - '''abc''' - '' - '''' - NULL -(6 rows) - -SELECT quote(t, 'ident') FROM (VALUES - ('abc'), - ('a b c'), - ('a " ''abc''')) AS v(t); - quote --------------- - abc - "a b c" - "a "" 'abc'" -(3 rows) - +psql: error: connection to server on socket "/tmp/.s.PGSQL.5432" failed: No such file or directory + Is the server running locally and accepting connections on that socket? diff -U3 /tmp/cirrus-ci-build/src/pl/plpython/expected/plpython_composite.out /tmp/cirrus-ci-build/build/testrun/plpython-running/regress/results/plpython_composite.out --- /tmp/cirrus-ci-build/src/pl/plpython/expected/plpython_composite.out 2024-04-02 00:56:35.794885000 +0000 +++ /tmp/cirrus-ci-build/build/testrun/plpython-running/regress/results/plpython_composite.out 2024-04-02 00:59:54.181251000 +0000 @@ -1,594 +1,2 @@ -CREATE FUNCTION multiout_simple(OUT i integer, OUT j integer) AS $$ -return (1, 2) -$$ LANGUAGE plpython3u; -SELECT multiout_simple(); - multiout_simple ------------------ - (1,2) -(1 row) - -SELECT * FROM multiout_simple(); - i | j ----+--- - 1 | 2 -(1 row) - -SELECT i, j + 2 FROM multiout_simple(); - i | ?column? ----+---------- - 1 | 4 -(1 row) - -SELECT (multiout_simple()).j + 3; - ?column? ----------- - 5 -(1 row) - -CREATE FUNCTION multiout_simple_setof(n integer = 1, OUT integer, OUT integer) RETURNS SETOF record AS $$ -return [(1, 2)] * n -$$ LANGUAGE plpython3u; -SELECT multiout_simple_setof(); - multiout_simple_setof ------------------------ - (1,2) -(1 row) - -SELECT * FROM multiout_simple_setof(); - column1 | column2 ----------+--------- - 1 | 2 -(1 row) - -SELECT * FROM multiout_simple_setof(3); - column1 | column2 ----------+--------- - 1 | 2 - 1 | 2 - 1 | 2 -(3 rows) - -CREATE FUNCTION multiout_record_as(typ text, - first text, OUT first text, - second integer, OUT second integer, - retnull boolean) RETURNS record AS $$ -if retnull: - return None -if typ == 'dict': - return { 'first': first, 'second': second, 'additionalfield': 'must not cause trouble' } -elif typ == 'tuple': - return ( first, second ) -elif typ == 'list': - return [ first, second ] -elif typ == 'obj': - class type_record: pass - type_record.first = first - type_record.second = second - return type_record -elif typ == 'str': - return "('%s',%r)" % (first, second) -$$ LANGUAGE plpython3u; -SELECT * FROM multiout_record_as('dict', 'foo', 1, 'f'); - first | second --------+-------- - foo | 1 -(1 row) - -SELECT multiout_record_as('dict', 'foo', 1, 'f'); - multiout_record_as --------------------- - (foo,1) -(1 row) - -SELECT * FROM multiout_record_as('dict', null, null, false); - first | second --------+-------- - | -(1 row) - -SELECT * FROM multiout_record_as('dict', 'one', null, false); - first | second --------+-------- - one | -(1 row) - -SELECT * FROM multiout_record_as('dict', null, 2, false); - first | second --------+-------- - | 2 -(1 row) - -SELECT * FROM multiout_record_as('dict', 'three', 3, false); - first | second --------+-------- - three | 3 -(1 row) - -SELECT * FROM multiout_record_as('dict', null, null, true); - first | second --------+-------- - | -(1 row) - -SELECT * FROM multiout_record_as('tuple', null, null, false); - first | second --------+-------- - | -(1 row) - -SELECT * FROM multiout_record_as('tuple', 'one', null, false); - first | second --------+-------- - one | -(1 row) - -SELECT * FROM multiout_record_as('tuple', null, 2, false); - first | second --------+-------- - | 2 -(1 row) - -SELECT * FROM multiout_record_as('tuple', 'three', 3, false); - first | second --------+-------- - three | 3 -(1 row) - -SELECT * FROM multiout_record_as('tuple', null, null, true); - first | second --------+-------- - | -(1 row) - -SELECT * FROM multiout_record_as('list', null, null, false); - first | second --------+-------- - | -(1 row) - -SELECT * FROM multiout_record_as('list', 'one', null, false); - first | second --------+-------- - one | -(1 row) - -SELECT * FROM multiout_record_as('list', null, 2, false); - first | second --------+-------- - | 2 -(1 row) - -SELECT * FROM multiout_record_as('list', 'three', 3, false); - first | second --------+-------- - three | 3 -(1 row) - -SELECT * FROM multiout_record_as('list', null, null, true); - first | second --------+-------- - | -(1 row) - -SELECT * FROM multiout_record_as('obj', null, null, false); - first | second --------+-------- - | -(1 row) - -SELECT * FROM multiout_record_as('obj', 'one', null, false); - first | second --------+-------- - one | -(1 row) - -SELECT * FROM multiout_record_as('obj', null, 2, false); - first | second --------+-------- - | 2 -(1 row) - -SELECT * FROM multiout_record_as('obj', 'three', 3, false); - first | second --------+-------- - three | 3 -(1 row) - -SELECT * FROM multiout_record_as('obj', null, null, true); - first | second --------+-------- - | -(1 row) - -SELECT * FROM multiout_record_as('str', 'one', 1, false); - first | second --------+-------- - 'one' | 1 -(1 row) - -SELECT * FROM multiout_record_as('str', 'one', 2, false); - first | second --------+-------- - 'one' | 2 -(1 row) - -SELECT *, s IS NULL AS snull FROM multiout_record_as('tuple', 'xxx', NULL, 'f') AS f(f, s); - f | s | snull ------+---+------- - xxx | | t -(1 row) - -SELECT *, f IS NULL AS fnull, s IS NULL AS snull FROM multiout_record_as('tuple', 'xxx', 1, 't') AS f(f, s); - f | s | fnull | snull ----+---+-------+------- - | | t | t -(1 row) - -SELECT * FROM multiout_record_as('obj', NULL, 10, 'f'); - first | second --------+-------- - | 10 -(1 row) - -CREATE FUNCTION multiout_setof(n integer, - OUT power_of_2 integer, - OUT length integer) RETURNS SETOF record AS $$ -for i in range(n): - power = 2 ** i - length = plpy.execute("select length('%d')" % power)[0]['length'] - yield power, length -$$ LANGUAGE plpython3u; -SELECT * FROM multiout_setof(3); - power_of_2 | length -------------+-------- - 1 | 1 - 2 | 1 - 4 | 1 -(3 rows) - -SELECT multiout_setof(5); - multiout_setof ----------------- - (1,1) - (2,1) - (4,1) - (8,1) - (16,2) -(5 rows) - -CREATE FUNCTION multiout_return_table() RETURNS TABLE (x integer, y text) AS $$ -return [{'x': 4, 'y' :'four'}, - {'x': 7, 'y' :'seven'}, - {'x': 0, 'y' :'zero'}] -$$ LANGUAGE plpython3u; -SELECT * FROM multiout_return_table(); - x | y ----+------- - 4 | four - 7 | seven - 0 | zero -(3 rows) - -CREATE FUNCTION multiout_array(OUT integer[], OUT text) RETURNS SETOF record AS $$ -yield [[1], 'a'] -yield [[1,2], 'b'] -yield [[1,2,3], None] -$$ LANGUAGE plpython3u; -SELECT * FROM multiout_array(); - column1 | column2 ----------+--------- - {1} | a - {1,2} | b - {1,2,3} | -(3 rows) - -CREATE FUNCTION singleout_composite(OUT type_record) AS $$ -return {'first': 1, 'second': 2} -$$ LANGUAGE plpython3u; -CREATE FUNCTION multiout_composite(OUT type_record) RETURNS SETOF type_record AS $$ -return [{'first': 1, 'second': 2}, - {'first': 3, 'second': 4 }] -$$ LANGUAGE plpython3u; -SELECT * FROM singleout_composite(); - first | second --------+-------- - 1 | 2 -(1 row) - -SELECT * FROM multiout_composite(); - first | second --------+-------- - 1 | 2 - 3 | 4 -(2 rows) - --- composite OUT parameters in functions returning RECORD not supported yet -CREATE FUNCTION multiout_composite(INOUT n integer, OUT type_record) AS $$ -return (n, (n * 2, n * 3)) -$$ LANGUAGE plpython3u; -CREATE FUNCTION multiout_table_type_setof(typ text, returnnull boolean, INOUT n integer, OUT table_record) RETURNS SETOF record AS $$ -if returnnull: - d = None -elif typ == 'dict': - d = {'first': n * 2, 'second': n * 3, 'extra': 'not important'} -elif typ == 'tuple': - d = (n * 2, n * 3) -elif typ == 'list': - d = [ n * 2, n * 3 ] -elif typ == 'obj': - class d: pass - d.first = n * 2 - d.second = n * 3 -elif typ == 'str': - d = "(%r,%r)" % (n * 2, n * 3) -for i in range(n): - yield (i, d) -$$ LANGUAGE plpython3u; -SELECT * FROM multiout_composite(2); - n | column2 ----+--------- - 2 | (4,6) -(1 row) - -SELECT * FROM multiout_table_type_setof('dict', 'f', 3); - n | column2 ----+--------- - 0 | (6,9) - 1 | (6,9) - 2 | (6,9) -(3 rows) - -SELECT * FROM multiout_table_type_setof('dict', 'f', 7); - n | column2 ----+--------- - 0 | (14,21) - 1 | (14,21) - 2 | (14,21) - 3 | (14,21) - 4 | (14,21) - 5 | (14,21) - 6 | (14,21) -(7 rows) - -SELECT * FROM multiout_table_type_setof('tuple', 'f', 2); - n | column2 ----+--------- - 0 | (4,6) - 1 | (4,6) -(2 rows) - -SELECT * FROM multiout_table_type_setof('tuple', 'f', 3); - n | column2 ----+--------- - 0 | (6,9) - 1 | (6,9) - 2 | (6,9) -(3 rows) - -SELECT * FROM multiout_table_type_setof('list', 'f', 2); - n | column2 ----+--------- - 0 | (4,6) - 1 | (4,6) -(2 rows) - -SELECT * FROM multiout_table_type_setof('list', 'f', 3); - n | column2 ----+--------- - 0 | (6,9) - 1 | (6,9) - 2 | (6,9) -(3 rows) - -SELECT * FROM multiout_table_type_setof('obj', 'f', 4); - n | column2 ----+--------- - 0 | (8,12) - 1 | (8,12) - 2 | (8,12) - 3 | (8,12) -(4 rows) - -SELECT * FROM multiout_table_type_setof('obj', 'f', 5); - n | column2 ----+--------- - 0 | (10,15) - 1 | (10,15) - 2 | (10,15) - 3 | (10,15) - 4 | (10,15) -(5 rows) - -SELECT * FROM multiout_table_type_setof('str', 'f', 6); - n | column2 ----+--------- - 0 | (12,18) - 1 | (12,18) - 2 | (12,18) - 3 | (12,18) - 4 | (12,18) - 5 | (12,18) -(6 rows) - -SELECT * FROM multiout_table_type_setof('str', 'f', 7); - n | column2 ----+--------- - 0 | (14,21) - 1 | (14,21) - 2 | (14,21) - 3 | (14,21) - 4 | (14,21) - 5 | (14,21) - 6 | (14,21) -(7 rows) - -SELECT * FROM multiout_table_type_setof('dict', 't', 3); - n | column2 ----+--------- - 0 | - 1 | - 2 | -(3 rows) - --- check what happens if a type changes under us -CREATE TABLE changing ( - i integer, - j integer -); -CREATE FUNCTION changing_test(OUT n integer, OUT changing) RETURNS SETOF record AS $$ -return [(1, {'i': 1, 'j': 2}), - (1, (3, 4))] -$$ LANGUAGE plpython3u; -SELECT * FROM changing_test(); - n | column2 ----+--------- - 1 | (1,2) - 1 | (3,4) -(2 rows) - -ALTER TABLE changing DROP COLUMN j; -SELECT * FROM changing_test(); -ERROR: length of returned sequence did not match number of columns in row -CONTEXT: while creating return value -PL/Python function "changing_test" -SELECT * FROM changing_test(); -ERROR: length of returned sequence did not match number of columns in row -CONTEXT: while creating return value -PL/Python function "changing_test" -ALTER TABLE changing ADD COLUMN j integer; -SELECT * FROM changing_test(); - n | column2 ----+--------- - 1 | (1,2) - 1 | (3,4) -(2 rows) - --- tables of composite types -CREATE FUNCTION composite_types_table(OUT tab table_record[], OUT typ type_record[] ) RETURNS SETOF record AS $$ -yield {'tab': [('first', 1), ('second', 2)], - 'typ': [{'first': 'third', 'second': 3}, - {'first': 'fourth', 'second': 4}]} -yield {'tab': [('first', 1), ('second', 2)], - 'typ': [{'first': 'third', 'second': 3}, - {'first': 'fourth', 'second': 4}]} -yield {'tab': [('first', 1), ('second', 2)], - 'typ': [{'first': 'third', 'second': 3}, - {'first': 'fourth', 'second': 4}]} -$$ LANGUAGE plpython3u; -SELECT * FROM composite_types_table(); - tab | typ -----------------------------+---------------------------- - {"(first,1)","(second,2)"} | {"(third,3)","(fourth,4)"} - {"(first,1)","(second,2)"} | {"(third,3)","(fourth,4)"} - {"(first,1)","(second,2)"} | {"(third,3)","(fourth,4)"} -(3 rows) - --- check what happens if the output record descriptor changes -CREATE FUNCTION return_record(t text) RETURNS record AS $$ -return {'t': t, 'val': 10} -$$ LANGUAGE plpython3u; -SELECT * FROM return_record('abc') AS r(t text, val integer); - t | val ------+----- - abc | 10 -(1 row) - -SELECT * FROM return_record('abc') AS r(t text, val bigint); - t | val ------+----- - abc | 10 -(1 row) - -SELECT * FROM return_record('abc') AS r(t text, val integer); - t | val ------+----- - abc | 10 -(1 row) - -SELECT * FROM return_record('abc') AS r(t varchar(30), val integer); - t | val ------+----- - abc | 10 -(1 row) - -SELECT * FROM return_record('abc') AS r(t varchar(100), val integer); - t | val ------+----- - abc | 10 -(1 row) - -SELECT * FROM return_record('999') AS r(val text, t integer); - val | t ------+----- - 10 | 999 -(1 row) - -CREATE FUNCTION return_record_2(t text) RETURNS record AS $$ -return {'v1':1,'v2':2,t:3} -$$ LANGUAGE plpython3u; -SELECT * FROM return_record_2('v3') AS (v3 int, v2 int, v1 int); - v3 | v2 | v1 -----+----+---- - 3 | 2 | 1 -(1 row) - -SELECT * FROM return_record_2('v3') AS (v2 int, v3 int, v1 int); - v2 | v3 | v1 -----+----+---- - 2 | 3 | 1 -(1 row) - -SELECT * FROM return_record_2('v4') AS (v1 int, v4 int, v2 int); - v1 | v4 | v2 -----+----+---- - 1 | 3 | 2 -(1 row) - -SELECT * FROM return_record_2('v4') AS (v1 int, v4 int, v2 int); - v1 | v4 | v2 -----+----+---- - 1 | 3 | 2 -(1 row) - --- error -SELECT * FROM return_record_2('v4') AS (v1 int, v3 int, v2 int); -ERROR: key "v3" not found in mapping -HINT: To return null in a column, add the value None to the mapping with the key named after the column. -CONTEXT: while creating return value -PL/Python function "return_record_2" --- works -SELECT * FROM return_record_2('v3') AS (v1 int, v3 int, v2 int); - v1 | v3 | v2 -----+----+---- - 1 | 3 | 2 -(1 row) - -SELECT * FROM return_record_2('v3') AS (v1 int, v2 int, v3 int); - v1 | v2 | v3 -----+----+---- - 1 | 2 | 3 -(1 row) - --- multi-dimensional array of composite types. -CREATE FUNCTION composite_type_as_list() RETURNS type_record[] AS $$ - return [[('first', 1), ('second', 1)], [('first', 2), ('second', 2)], [('first', 3), ('second', 3)]]; -$$ LANGUAGE plpython3u; -SELECT * FROM composite_type_as_list(); - composite_type_as_list ------------------------------------------------------------------------------------- - {{"(first,1)","(second,1)"},{"(first,2)","(second,2)"},{"(first,3)","(second,3)"}} -(1 row) - --- Starting with PostgreSQL 10, a composite type in an array cannot be --- represented as a Python list, because it's ambiguous with multi-dimensional --- arrays. So this throws an error now. The error should contain a useful hint --- on the issue. -CREATE FUNCTION composite_type_as_list_broken() RETURNS type_record[] AS $$ - return [['first', 1]]; -$$ LANGUAGE plpython3u; -SELECT * FROM composite_type_as_list_broken(); -ERROR: malformed record literal: "first" -DETAIL: Missing left parenthesis. -HINT: To return a composite type in an array, return the composite type as a Python tuple, e.g., "[('foo',)]". -CONTEXT: while creating return value -PL/Python function "composite_type_as_list_broken" +psql: error: connection to server on socket "/tmp/.s.PGSQL.5432" failed: No such file or directory + Is the server running locally and accepting connections on that socket? diff -U3 /tmp/cirrus-ci-build/src/pl/plpython/expected/plpython_subtransaction.out /tmp/cirrus-ci-build/build/testrun/plpython-running/regress/results/plpython_subtransaction.out --- /tmp/cirrus-ci-build/src/pl/plpython/expected/plpython_subtransaction.out 2024-04-02 00:56:35.796211000 +0000 +++ /tmp/cirrus-ci-build/build/testrun/plpython-running/regress/results/plpython_subtransaction.out 2024-04-02 00:59:54.192763000 +0000 @@ -1,401 +1,2 @@ --- --- Test explicit subtransactions --- --- Test table to see if transactions get properly rolled back -CREATE TABLE subtransaction_tbl ( - i integer -); -CREATE FUNCTION subtransaction_ctx_test(what_error text = NULL) RETURNS text -AS $$ -with plpy.subtransaction(): - plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)") - plpy.execute("INSERT INTO subtransaction_tbl VALUES (2)") - if what_error == "SPI": - plpy.execute("INSERT INTO subtransaction_tbl VALUES ('oops')") - elif what_error == "Python": - raise Exception("Python exception") -$$ LANGUAGE plpython3u; -SELECT subtransaction_ctx_test(); - subtransaction_ctx_test -------------------------- - -(1 row) - -SELECT * FROM subtransaction_tbl; - i ---- - 1 - 2 -(2 rows) - -TRUNCATE subtransaction_tbl; -SELECT subtransaction_ctx_test('SPI'); -ERROR: spiexceptions.InvalidTextRepresentation: invalid input syntax for type integer: "oops" -LINE 1: INSERT INTO subtransaction_tbl VALUES ('oops') - ^ -QUERY: INSERT INTO subtransaction_tbl VALUES ('oops') -CONTEXT: Traceback (most recent call last): - PL/Python function "subtransaction_ctx_test", line 6, in - plpy.execute("INSERT INTO subtransaction_tbl VALUES ('oops')") -PL/Python function "subtransaction_ctx_test" -SELECT * FROM subtransaction_tbl; - i ---- -(0 rows) - -TRUNCATE subtransaction_tbl; -SELECT subtransaction_ctx_test('Python'); -ERROR: Exception: Python exception -CONTEXT: Traceback (most recent call last): - PL/Python function "subtransaction_ctx_test", line 8, in - raise Exception("Python exception") -PL/Python function "subtransaction_ctx_test" -SELECT * FROM subtransaction_tbl; - i ---- -(0 rows) - -TRUNCATE subtransaction_tbl; --- Nested subtransactions -CREATE FUNCTION subtransaction_nested_test(swallow boolean = 'f') RETURNS text -AS $$ -plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)") -with plpy.subtransaction(): - plpy.execute("INSERT INTO subtransaction_tbl VALUES (2)") - try: - with plpy.subtransaction(): - plpy.execute("INSERT INTO subtransaction_tbl VALUES (3)") - plpy.execute("error") - except plpy.SPIError as e: - if not swallow: - raise - plpy.notice("Swallowed %s(%r)" % (e.__class__.__name__, e.args[0])) -return "ok" -$$ LANGUAGE plpython3u; -SELECT subtransaction_nested_test(); -ERROR: spiexceptions.SyntaxError: syntax error at or near "error" -LINE 1: error - ^ -QUERY: error -CONTEXT: Traceback (most recent call last): - PL/Python function "subtransaction_nested_test", line 8, in - plpy.execute("error") -PL/Python function "subtransaction_nested_test" -SELECT * FROM subtransaction_tbl; - i ---- -(0 rows) - -TRUNCATE subtransaction_tbl; -SELECT subtransaction_nested_test('t'); -NOTICE: Swallowed SyntaxError('syntax error at or near "error"') - subtransaction_nested_test ----------------------------- - ok -(1 row) - -SELECT * FROM subtransaction_tbl; - i ---- - 1 - 2 -(2 rows) - -TRUNCATE subtransaction_tbl; --- Nested subtransactions that recursively call code dealing with --- subtransactions -CREATE FUNCTION subtransaction_deeply_nested_test() RETURNS text -AS $$ -plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)") -with plpy.subtransaction(): - plpy.execute("INSERT INTO subtransaction_tbl VALUES (2)") - plpy.execute("SELECT subtransaction_nested_test('t')") -return "ok" -$$ LANGUAGE plpython3u; -SELECT subtransaction_deeply_nested_test(); -NOTICE: Swallowed SyntaxError('syntax error at or near "error"') - subtransaction_deeply_nested_test ------------------------------------ - ok -(1 row) - -SELECT * FROM subtransaction_tbl; - i ---- - 1 - 2 - 1 - 2 -(4 rows) - -TRUNCATE subtransaction_tbl; --- Error conditions from not opening/closing subtransactions -CREATE FUNCTION subtransaction_exit_without_enter() RETURNS void -AS $$ -plpy.subtransaction().__exit__(None, None, None) -$$ LANGUAGE plpython3u; -CREATE FUNCTION subtransaction_enter_without_exit() RETURNS void -AS $$ -plpy.subtransaction().__enter__() -$$ LANGUAGE plpython3u; -CREATE FUNCTION subtransaction_exit_twice() RETURNS void -AS $$ -plpy.subtransaction().__enter__() -plpy.subtransaction().__exit__(None, None, None) -plpy.subtransaction().__exit__(None, None, None) -$$ LANGUAGE plpython3u; -CREATE FUNCTION subtransaction_enter_twice() RETURNS void -AS $$ -plpy.subtransaction().__enter__() -plpy.subtransaction().__enter__() -$$ LANGUAGE plpython3u; -CREATE FUNCTION subtransaction_exit_same_subtransaction_twice() RETURNS void -AS $$ -s = plpy.subtransaction() -s.__enter__() -s.__exit__(None, None, None) -s.__exit__(None, None, None) -$$ LANGUAGE plpython3u; -CREATE FUNCTION subtransaction_enter_same_subtransaction_twice() RETURNS void -AS $$ -s = plpy.subtransaction() -s.__enter__() -s.__enter__() -s.__exit__(None, None, None) -$$ LANGUAGE plpython3u; --- No warnings here, as the subtransaction gets indeed closed -CREATE FUNCTION subtransaction_enter_subtransaction_in_with() RETURNS void -AS $$ -with plpy.subtransaction() as s: - s.__enter__() -$$ LANGUAGE plpython3u; -CREATE FUNCTION subtransaction_exit_subtransaction_in_with() RETURNS void -AS $$ -try: - with plpy.subtransaction() as s: - s.__exit__(None, None, None) -except ValueError as e: - raise ValueError(e) -$$ LANGUAGE plpython3u; -SELECT subtransaction_exit_without_enter(); -ERROR: ValueError: this subtransaction has not been entered -CONTEXT: Traceback (most recent call last): - PL/Python function "subtransaction_exit_without_enter", line 2, in - plpy.subtransaction().__exit__(None, None, None) -PL/Python function "subtransaction_exit_without_enter" -SELECT subtransaction_enter_without_exit(); -WARNING: forcibly aborting a subtransaction that has not been exited - subtransaction_enter_without_exit ------------------------------------ - -(1 row) - -SELECT subtransaction_exit_twice(); -WARNING: forcibly aborting a subtransaction that has not been exited -ERROR: ValueError: this subtransaction has not been entered -CONTEXT: Traceback (most recent call last): - PL/Python function "subtransaction_exit_twice", line 3, in - plpy.subtransaction().__exit__(None, None, None) -PL/Python function "subtransaction_exit_twice" -SELECT subtransaction_enter_twice(); -WARNING: forcibly aborting a subtransaction that has not been exited -WARNING: forcibly aborting a subtransaction that has not been exited - subtransaction_enter_twice ----------------------------- - -(1 row) - -SELECT subtransaction_exit_same_subtransaction_twice(); -ERROR: ValueError: this subtransaction has already been exited -CONTEXT: Traceback (most recent call last): - PL/Python function "subtransaction_exit_same_subtransaction_twice", line 5, in - s.__exit__(None, None, None) -PL/Python function "subtransaction_exit_same_subtransaction_twice" -SELECT subtransaction_enter_same_subtransaction_twice(); -WARNING: forcibly aborting a subtransaction that has not been exited -ERROR: ValueError: this subtransaction has already been entered -CONTEXT: Traceback (most recent call last): - PL/Python function "subtransaction_enter_same_subtransaction_twice", line 4, in - s.__enter__() -PL/Python function "subtransaction_enter_same_subtransaction_twice" -SELECT subtransaction_enter_subtransaction_in_with(); -ERROR: ValueError: this subtransaction has already been entered -CONTEXT: Traceback (most recent call last): - PL/Python function "subtransaction_enter_subtransaction_in_with", line 3, in - s.__enter__() -PL/Python function "subtransaction_enter_subtransaction_in_with" -SELECT subtransaction_exit_subtransaction_in_with(); -ERROR: ValueError: this subtransaction has already been exited -CONTEXT: Traceback (most recent call last): - PL/Python function "subtransaction_exit_subtransaction_in_with", line 6, in - raise ValueError(e) -PL/Python function "subtransaction_exit_subtransaction_in_with" --- Make sure we don't get a "current transaction is aborted" error -SELECT 1 as test; - test ------- - 1 -(1 row) - --- Mix explicit subtransactions and normal SPI calls -CREATE FUNCTION subtransaction_mix_explicit_and_implicit() RETURNS void -AS $$ -p = plpy.prepare("INSERT INTO subtransaction_tbl VALUES ($1)", ["integer"]) -try: - with plpy.subtransaction(): - plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)") - plpy.execute(p, [2]) - plpy.execute(p, ["wrong"]) -except plpy.SPIError: - plpy.warning("Caught a SPI error from an explicit subtransaction") - -try: - plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)") - plpy.execute(p, [2]) - plpy.execute(p, ["wrong"]) -except plpy.SPIError: - plpy.warning("Caught a SPI error") -$$ LANGUAGE plpython3u; -SELECT subtransaction_mix_explicit_and_implicit(); -WARNING: Caught a SPI error from an explicit subtransaction -WARNING: Caught a SPI error - subtransaction_mix_explicit_and_implicit ------------------------------------------- - -(1 row) - -SELECT * FROM subtransaction_tbl; - i ---- - 1 - 2 -(2 rows) - -TRUNCATE subtransaction_tbl; --- Alternative method names for Python <2.6 -CREATE FUNCTION subtransaction_alternative_names() RETURNS void -AS $$ -s = plpy.subtransaction() -s.enter() -s.exit(None, None, None) -$$ LANGUAGE plpython3u; -SELECT subtransaction_alternative_names(); - subtransaction_alternative_names ----------------------------------- - -(1 row) - --- try/catch inside a subtransaction block -CREATE FUNCTION try_catch_inside_subtransaction() RETURNS void -AS $$ -with plpy.subtransaction(): - plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)") - try: - plpy.execute("INSERT INTO subtransaction_tbl VALUES ('a')") - except plpy.SPIError: - plpy.notice("caught") -$$ LANGUAGE plpython3u; -SELECT try_catch_inside_subtransaction(); -NOTICE: caught - try_catch_inside_subtransaction ---------------------------------- - -(1 row) - -SELECT * FROM subtransaction_tbl; - i ---- - 1 -(1 row) - -TRUNCATE subtransaction_tbl; -ALTER TABLE subtransaction_tbl ADD PRIMARY KEY (i); -CREATE FUNCTION pk_violation_inside_subtransaction() RETURNS void -AS $$ -with plpy.subtransaction(): - plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)") - try: - plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)") - except plpy.SPIError: - plpy.notice("caught") -$$ LANGUAGE plpython3u; -SELECT pk_violation_inside_subtransaction(); -NOTICE: caught - pk_violation_inside_subtransaction ------------------------------------- - -(1 row) - -SELECT * FROM subtransaction_tbl; - i ---- - 1 -(1 row) - -DROP TABLE subtransaction_tbl; --- cursor/subtransactions interactions -CREATE FUNCTION cursor_in_subxact() RETURNS int AS $$ -with plpy.subtransaction(): - cur = plpy.cursor("select * from generate_series(1, 20) as gen(i)") - cur.fetch(10) -fetched = cur.fetch(10); -return int(fetched[5]["i"]) -$$ LANGUAGE plpython3u; -CREATE FUNCTION cursor_aborted_subxact() RETURNS int AS $$ -try: - with plpy.subtransaction(): - cur = plpy.cursor("select * from generate_series(1, 20) as gen(i)") - cur.fetch(10); - plpy.execute("select no_such_function()") -except plpy.SPIError: - fetched = cur.fetch(10) - return int(fetched[5]["i"]) -return 0 # not reached -$$ LANGUAGE plpython3u; -CREATE FUNCTION cursor_plan_aborted_subxact() RETURNS int AS $$ -try: - with plpy.subtransaction(): - plpy.execute('create temporary table tmp(i) ' - 'as select generate_series(1, 10)') - plan = plpy.prepare("select i from tmp") - cur = plpy.cursor(plan) - plpy.execute("select no_such_function()") -except plpy.SPIError: - fetched = cur.fetch(5) - return fetched[2]["i"] -return 0 # not reached -$$ LANGUAGE plpython3u; -CREATE FUNCTION cursor_close_aborted_subxact() RETURNS boolean AS $$ -try: - with plpy.subtransaction(): - cur = plpy.cursor('select 1') - plpy.execute("select no_such_function()") -except plpy.SPIError: - cur.close() - return True -return False # not reached -$$ LANGUAGE plpython3u; -SELECT cursor_in_subxact(); - cursor_in_subxact -------------------- - 16 -(1 row) - -SELECT cursor_aborted_subxact(); -ERROR: ValueError: iterating a cursor in an aborted subtransaction -CONTEXT: Traceback (most recent call last): - PL/Python function "cursor_aborted_subxact", line 8, in - fetched = cur.fetch(10) -PL/Python function "cursor_aborted_subxact" -SELECT cursor_plan_aborted_subxact(); -ERROR: ValueError: iterating a cursor in an aborted subtransaction -CONTEXT: Traceback (most recent call last): - PL/Python function "cursor_plan_aborted_subxact", line 10, in - fetched = cur.fetch(5) -PL/Python function "cursor_plan_aborted_subxact" -SELECT cursor_close_aborted_subxact(); -ERROR: ValueError: closing a cursor in an aborted subtransaction -CONTEXT: Traceback (most recent call last): - PL/Python function "cursor_close_aborted_subxact", line 7, in - cur.close() -PL/Python function "cursor_close_aborted_subxact" +psql: error: connection to server on socket "/tmp/.s.PGSQL.5432" failed: No such file or directory + Is the server running locally and accepting connections on that socket? diff -U3 /tmp/cirrus-ci-build/src/pl/plpython/expected/plpython_transaction.out /tmp/cirrus-ci-build/build/testrun/plpython-running/regress/results/plpython_transaction.out --- /tmp/cirrus-ci-build/src/pl/plpython/expected/plpython_transaction.out 2024-04-02 00:56:35.796311000 +0000 +++ /tmp/cirrus-ci-build/build/testrun/plpython-running/regress/results/plpython_transaction.out 2024-04-02 00:59:54.215696000 +0000 @@ -1,250 +1,2 @@ -CREATE TABLE test1 (a int, b text); -CREATE PROCEDURE transaction_test1() -LANGUAGE plpython3u -AS $$ -for i in range(0, 10): - plpy.execute("INSERT INTO test1 (a) VALUES (%d)" % i) - if i % 2 == 0: - plpy.commit() - else: - plpy.rollback() -$$; -CALL transaction_test1(); -SELECT * FROM test1; - a | b ----+--- - 0 | - 2 | - 4 | - 6 | - 8 | -(5 rows) - -TRUNCATE test1; -DO -LANGUAGE plpython3u -$$ -for i in range(0, 10): - plpy.execute("INSERT INTO test1 (a) VALUES (%d)" % i) - if i % 2 == 0: - plpy.commit() - else: - plpy.rollback() -$$; -SELECT * FROM test1; - a | b ----+--- - 0 | - 2 | - 4 | - 6 | - 8 | -(5 rows) - -TRUNCATE test1; --- not allowed in a function -CREATE FUNCTION transaction_test2() RETURNS int -LANGUAGE plpython3u -AS $$ -for i in range(0, 10): - plpy.execute("INSERT INTO test1 (a) VALUES (%d)" % i) - if i % 2 == 0: - plpy.commit() - else: - plpy.rollback() -return 1 -$$; -SELECT transaction_test2(); -ERROR: spiexceptions.InvalidTransactionTermination: invalid transaction termination -CONTEXT: Traceback (most recent call last): - PL/Python function "transaction_test2", line 5, in - plpy.commit() -PL/Python function "transaction_test2" -SELECT * FROM test1; - a | b ----+--- -(0 rows) - --- also not allowed if procedure is called from a function -CREATE FUNCTION transaction_test3() RETURNS int -LANGUAGE plpython3u -AS $$ -plpy.execute("CALL transaction_test1()") -return 1 -$$; -SELECT transaction_test3(); -ERROR: spiexceptions.InvalidTransactionTermination: spiexceptions.InvalidTransactionTermination: invalid transaction termination -CONTEXT: Traceback (most recent call last): - PL/Python function "transaction_test3", line 2, in - plpy.execute("CALL transaction_test1()") -PL/Python function "transaction_test3" -SELECT * FROM test1; - a | b ----+--- -(0 rows) - --- DO block inside function -CREATE FUNCTION transaction_test4() RETURNS int -LANGUAGE plpython3u -AS $$ -plpy.execute("DO LANGUAGE plpython3u $x$ plpy.commit() $x$") -return 1 -$$; -SELECT transaction_test4(); -ERROR: spiexceptions.InvalidTransactionTermination: spiexceptions.InvalidTransactionTermination: invalid transaction termination -CONTEXT: Traceback (most recent call last): - PL/Python function "transaction_test4", line 2, in - plpy.execute("DO LANGUAGE plpython3u $x$ plpy.commit() $x$") -PL/Python function "transaction_test4" --- commit inside subtransaction (prohibited) -DO LANGUAGE plpython3u $$ -s = plpy.subtransaction() -s.enter() -plpy.commit() -$$; -WARNING: forcibly aborting a subtransaction that has not been exited -ERROR: spiexceptions.InvalidTransactionTermination: cannot commit while a subtransaction is active -CONTEXT: Traceback (most recent call last): - PL/Python anonymous code block, line 4, in - plpy.commit() -PL/Python anonymous code block --- commit inside cursor loop -CREATE TABLE test2 (x int); -INSERT INTO test2 VALUES (0), (1), (2), (3), (4); -TRUNCATE test1; -DO LANGUAGE plpython3u $$ -for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"): - plpy.execute("INSERT INTO test1 (a) VALUES (%s)" % row['x']) - plpy.commit() -$$; -SELECT * FROM test1; - a | b ----+--- - 0 | - 1 | - 2 | - 3 | - 4 | -(5 rows) - --- check that this doesn't leak a holdable portal -SELECT * FROM pg_cursors; - name | statement | is_holdable | is_binary | is_scrollable | creation_time -------+-----------+-------------+-----------+---------------+--------------- -(0 rows) - --- error in cursor loop with commit -TRUNCATE test1; -DO LANGUAGE plpython3u $$ -for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"): - plpy.execute("INSERT INTO test1 (a) VALUES (12/(%s-2))" % row['x']) - plpy.commit() -$$; -ERROR: spiexceptions.DivisionByZero: division by zero -CONTEXT: Traceback (most recent call last): - PL/Python anonymous code block, line 3, in - plpy.execute("INSERT INTO test1 (a) VALUES (12/(%s-2))" % row['x']) -PL/Python anonymous code block -SELECT * FROM test1; - a | b ------+--- - -6 | - -12 | -(2 rows) - -SELECT * FROM pg_cursors; - name | statement | is_holdable | is_binary | is_scrollable | creation_time -------+-----------+-------------+-----------+---------------+--------------- -(0 rows) - --- rollback inside cursor loop -TRUNCATE test1; -DO LANGUAGE plpython3u $$ -for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"): - plpy.execute("INSERT INTO test1 (a) VALUES (%s)" % row['x']) - plpy.rollback() -$$; -SELECT * FROM test1; - a | b ----+--- -(0 rows) - -SELECT * FROM pg_cursors; - name | statement | is_holdable | is_binary | is_scrollable | creation_time -------+-----------+-------------+-----------+---------------+--------------- -(0 rows) - --- first commit then rollback inside cursor loop -TRUNCATE test1; -DO LANGUAGE plpython3u $$ -for row in plpy.cursor("SELECT * FROM test2 ORDER BY x"): - plpy.execute("INSERT INTO test1 (a) VALUES (%s)" % row['x']) - if row['x'] % 2 == 0: - plpy.commit() - else: - plpy.rollback() -$$; -SELECT * FROM test1; - a | b ----+--- - 0 | - 2 | - 4 | -(3 rows) - -SELECT * FROM pg_cursors; - name | statement | is_holdable | is_binary | is_scrollable | creation_time -------+-----------+-------------+-----------+---------------+--------------- -(0 rows) - --- check handling of an error during COMMIT -CREATE TABLE testpk (id int PRIMARY KEY); -CREATE TABLE testfk(f1 int REFERENCES testpk DEFERRABLE INITIALLY DEFERRED); -DO LANGUAGE plpython3u $$ -# this insert will fail during commit: -plpy.execute("INSERT INTO testfk VALUES (0)") -plpy.commit() -plpy.warning('should not get here') -$$; -ERROR: spiexceptions.ForeignKeyViolation: insert or update on table "testfk" violates foreign key constraint "testfk_f1_fkey" -DETAIL: Key (f1)=(0) is not present in table "testpk". -CONTEXT: Traceback (most recent call last): - PL/Python anonymous code block, line 4, in - plpy.commit() -PL/Python anonymous code block -SELECT * FROM testpk; - id ----- -(0 rows) - -SELECT * FROM testfk; - f1 ----- -(0 rows) - -DO LANGUAGE plpython3u $$ -# this insert will fail during commit: -plpy.execute("INSERT INTO testfk VALUES (0)") -try: - plpy.commit() -except Exception as e: - plpy.info('sqlstate: %s' % (e.sqlstate)) -# these inserts should work: -plpy.execute("INSERT INTO testpk VALUES (1)") -plpy.execute("INSERT INTO testfk VALUES (1)") -$$; -INFO: sqlstate: 23503 -SELECT * FROM testpk; - id ----- - 1 -(1 row) - -SELECT * FROM testfk; - f1 ----- - 1 -(1 row) - -DROP TABLE test1; -DROP TABLE test2; +psql: error: connection to server on socket "/tmp/.s.PGSQL.5432" failed: No such file or directory + Is the server running locally and accepting connections on that socket? diff -U3 /tmp/cirrus-ci-build/src/pl/plpython/expected/plpython_drop.out /tmp/cirrus-ci-build/build/testrun/plpython-running/regress/results/plpython_drop.out --- /tmp/cirrus-ci-build/src/pl/plpython/expected/plpython_drop.out 2024-04-02 00:56:35.794967000 +0000 +++ /tmp/cirrus-ci-build/build/testrun/plpython-running/regress/results/plpython_drop.out 2024-04-02 00:59:54.226198000 +0000 @@ -1,5 +1,2 @@ --- --- For paranoia's sake, don't leave an untrusted language sitting around --- -SET client_min_messages = WARNING; -DROP EXTENSION plpython3u CASCADE; +psql: error: connection to server on socket "/tmp/.s.PGSQL.5432" failed: No such file or directory + Is the server running locally and accepting connections on that socket?