Content-Length: 1065439 | pFad | http://github.com/tonybelloni/postgres/commit/687f096ea9da82d267f1809a5f3fdfa027092045

35 Make PL/Python handle domain-type conversions correctly. · tonybelloni/postgres@687f096 · GitHub
Skip to content

Commit 687f096

Browse files
committed
Make PL/Python handle domain-type conversions correctly.
Fix PL/Python so that it can handle domains over composite, and so that it enforces domain constraints correctly in other cases that were not always done properly before. Notably, it didn't do arrays of domains right (oversight in commit c12d570), and it failed to enforce domain constraints when returning a composite type containing a domain field, and if a transform function is being used for a domain's base type then it failed to enforce domain constraints on the result. Also, in many places it missed checking domain constraints on null values, because the plpy_typeio code simply wasn't called for Py_None. Rather than try to band-aid these problems, I made a significant refactoring of the plpy_typeio logic. The existing design of recursing for array and composite members is extended to also treat domains as containers requiring recursion, and the APIs for the module are cleaned up and simplified. The patch also modifies plpy_typeio to rely on the typcache more than it did before (which was pretty much not at all). This reduces the need for repetitive lookups, and lets us get rid of an ad-hoc scheme for detecting changes in composite types. I added a couple of small features to typcache to help with that. Although some of this is fixing bugs that long predate v11, I don't think we should risk a back-patch: it's a significant amount of code churn, and there've been no complaints from the field about the bugs. Tom Lane, reviewed by Anthony Bykov Discussion: https://postgr.es/m/24449.1509393613@sss.pgh.pa.us
1 parent 575cead commit 687f096

17 files changed

+1378
-929
lines changed

contrib/hstore_plpython/expected/hstore_plpython.out

+19-1
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,30 @@ AS $$
6868
val = [{'a': 1, 'b': 'boo', 'c': None}, {'d': 2}]
6969
return val
7070
$$;
71-
SELECT test2arr();
71+
SELECT test2arr();
7272
test2arr
7373
--------------------------------------------------------------
7474
{"\"a\"=>\"1\", \"b\"=>\"boo\", \"c\"=>NULL","\"d\"=>\"2\""}
7575
(1 row)
7676

77+
-- test python -> domain over hstore
78+
CREATE DOMAIN hstore_foo AS hstore CHECK(VALUE ? 'foo');
79+
CREATE FUNCTION test2dom(fn text) RETURNS hstore_foo
80+
LANGUAGE plpythonu
81+
TRANSFORM FOR TYPE hstore
82+
AS $$
83+
return {'a': 1, fn: 'boo', 'c': None}
84+
$$;
85+
SELECT test2dom('foo');
86+
test2dom
87+
-----------------------------------
88+
"a"=>"1", "c"=>NULL, "foo"=>"boo"
89+
(1 row)
90+
91+
SELECT test2dom('bar'); -- fail
92+
ERROR: value for domain hstore_foo violates check constraint "hstore_foo_check"
93+
CONTEXT: while creating return value
94+
PL/Python function "test2dom"
7795
-- test as part of prepare/execute
7896
CREATE FUNCTION test3() RETURNS void
7997
LANGUAGE plpythonu

contrib/hstore_plpython/sql/hstore_plpython.sql

+15-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,21 @@ val = [{'a': 1, 'b': 'boo', 'c': None}, {'d': 2}]
6060
return val
6161
$$;
6262

63-
SELECT test2arr();
63+
SELECT test2arr();
64+
65+
66+
-- test python -> domain over hstore
67+
CREATE DOMAIN hstore_foo AS hstore CHECK(VALUE ? 'foo');
68+
69+
CREATE FUNCTION test2dom(fn text) RETURNS hstore_foo
70+
LANGUAGE plpythonu
71+
TRANSFORM FOR TYPE hstore
72+
AS $$
73+
return {'a': 1, fn: 'boo', 'c': None}
74+
$$;
75+
76+
SELECT test2dom('foo');
77+
SELECT test2dom('bar'); -- fail
6478

6579

6680
-- test as part of prepare/execute

src/backend/utils/cache/typcache.c

+7
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,7 @@ lookup_type_cache(Oid type_id, int flags)
377377
typentry->typstorage = typtup->typstorage;
378378
typentry->typtype = typtup->typtype;
379379
typentry->typrelid = typtup->typrelid;
380+
typentry->typelem = typtup->typelem;
380381

381382
/* If it's a domain, immediately thread it into the domain cache list */
382383
if (typentry->typtype == TYPTYPE_DOMAIN)
@@ -791,6 +792,12 @@ load_typcache_tupdesc(TypeCacheEntry *typentry)
791792
Assert(typentry->tupDesc->tdrefcount > 0);
792793
typentry->tupDesc->tdrefcount++;
793794

795+
/*
796+
* In future, we could take some pains to not increment the seqno if the
797+
* tupdesc didn't really change; but for now it's not worth it.
798+
*/
799+
typentry->tupDescSeqNo++;
800+
794801
relation_close(rel, AccessShareLock);
795802
}
796803

src/include/utils/typcache.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ typedef struct TypeCacheEntry
4040
char typstorage;
4141
char typtype;
4242
Oid typrelid;
43+
Oid typelem;
4344

4445
/*
4546
* Information obtained from opfamily entries
@@ -75,9 +76,11 @@ typedef struct TypeCacheEntry
7576
/*
7677
* Tuple descriptor if it's a composite type (row type). NULL if not
7778
* composite or information hasn't yet been requested. (NOTE: this is a
78-
* reference-counted tupledesc.)
79+
* reference-counted tupledesc.) To simplify caching dependent info,
80+
* tupDescSeqNo is incremented each time tupDesc is rebuilt in a session.
7981
*/
8082
TupleDesc tupDesc;
83+
int64 tupDescSeqNo;
8184

8285
/*
8386
* Fields computed when TYPECACHE_RANGE_INFO is requested. Zeroes if not

src/pl/plpython/expected/plpython_types.out

+128
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,76 @@ SELECT * FROM test_type_conversion_array_domain_check_violation();
765765
ERROR: value for domain ordered_pair_domain violates check constraint "ordered_pair_domain_check"
766766
CONTEXT: while creating return value
767767
PL/Python function "test_type_conversion_array_domain_check_violation"
768+
--
769+
-- Arrays of domains
770+
--
771+
CREATE FUNCTION test_read_uint2_array(x uint2[]) RETURNS uint2 AS $$
772+
plpy.info(x, type(x))
773+
return x[0]
774+
$$ LANGUAGE plpythonu;
775+
select test_read_uint2_array(array[1::uint2]);
776+
INFO: ([1], <type 'list'>)
777+
test_read_uint2_array
778+
-----------------------
779+
1
780+
(1 row)
781+
782+
CREATE FUNCTION test_build_uint2_array(x int2) RETURNS uint2[] AS $$
783+
return [x, x]
784+
$$ LANGUAGE plpythonu;
785+
select test_build_uint2_array(1::int2);
786+
test_build_uint2_array
787+
------------------------
788+
{1,1}
789+
(1 row)
790+
791+
select test_build_uint2_array(-1::int2); -- fail
792+
ERROR: value for domain uint2 violates check constraint "uint2_check"
793+
CONTEXT: while creating return value
794+
PL/Python function "test_build_uint2_array"
795+
--
796+
-- ideally this would work, but for now it doesn't, because the return value
797+
-- is [[2,4], [2,4]] which our conversion code thinks should become a 2-D
798+
-- integer array, not an array of arrays.
799+
--
800+
CREATE FUNCTION test_type_conversion_domain_array(x integer[])
801+
RETURNS ordered_pair_domain[] AS $$
802+
return [x, x]
803+
$$ LANGUAGE plpythonu;
804+
select test_type_conversion_domain_array(array[2,4]);
805+
ERROR: return value of function with array return type is not a Python sequence
806+
CONTEXT: while creating return value
807+
PL/Python function "test_type_conversion_domain_array"
808+
select test_type_conversion_domain_array(array[4,2]); -- fail
809+
ERROR: return value of function with array return type is not a Python sequence
810+
CONTEXT: while creating return value
811+
PL/Python function "test_type_conversion_domain_array"
812+
CREATE FUNCTION test_type_conversion_domain_array2(x ordered_pair_domain)
813+
RETURNS integer AS $$
814+
plpy.info(x, type(x))
815+
return x[1]
816+
$$ LANGUAGE plpythonu;
817+
select test_type_conversion_domain_array2(array[2,4]);
818+
INFO: ([2, 4], <type 'list'>)
819+
test_type_conversion_domain_array2
820+
------------------------------------
821+
4
822+
(1 row)
823+
824+
select test_type_conversion_domain_array2(array[4,2]); -- fail
825+
ERROR: value for domain ordered_pair_domain violates check constraint "ordered_pair_domain_check"
826+
CREATE FUNCTION test_type_conversion_array_domain_array(x ordered_pair_domain[])
827+
RETURNS ordered_pair_domain AS $$
828+
plpy.info(x, type(x))
829+
return x[0]
830+
$$ LANGUAGE plpythonu;
831+
select test_type_conversion_array_domain_array(array[array[2,4]::ordered_pair_domain]);
832+
INFO: ([[2, 4]], <type 'list'>)
833+
test_type_conversion_array_domain_array
834+
-----------------------------------------
835+
{2,4}
836+
(1 row)
837+
768838
---
769839
--- Composite types
770840
---
@@ -820,6 +890,64 @@ SELECT test_composite_type_input(row(1, 2));
820890
3
821891
(1 row)
822892

893+
--
894+
-- Domains within composite
895+
--
896+
CREATE TYPE nnint_container AS (f1 int, f2 nnint);
897+
CREATE FUNCTION nnint_test(x int, y int) RETURNS nnint_container AS $$
898+
return {'f1': x, 'f2': y}
899+
$$ LANGUAGE plpythonu;
900+
SELECT nnint_test(null, 3);
901+
nnint_test
902+
------------
903+
(,3)
904+
(1 row)
905+
906+
SELECT nnint_test(3, null); -- fail
907+
ERROR: value for domain nnint violates check constraint "nnint_check"
908+
CONTEXT: while creating return value
909+
PL/Python function "nnint_test"
910+
--
911+
-- Domains of composite
912+
--
913+
CREATE DOMAIN ordered_named_pair AS named_pair_2 CHECK((VALUE).i <= (VALUE).j);
914+
CREATE FUNCTION read_ordered_named_pair(p ordered_named_pair) RETURNS integer AS $$
915+
return p['i'] + p['j']
916+
$$ LANGUAGE plpythonu;
917+
SELECT read_ordered_named_pair(row(1, 2));
918+
read_ordered_named_pair
919+
-------------------------
920+
3
921+
(1 row)
922+
923+
SELECT read_ordered_named_pair(row(2, 1)); -- fail
924+
ERROR: value for domain ordered_named_pair violates check constraint "ordered_named_pair_check"
925+
CREATE FUNCTION build_ordered_named_pair(i int, j int) RETURNS ordered_named_pair AS $$
926+
return {'i': i, 'j': j}
927+
$$ LANGUAGE plpythonu;
928+
SELECT build_ordered_named_pair(1,2);
929+
build_ordered_named_pair
930+
--------------------------
931+
(1,2)
932+
(1 row)
933+
934+
SELECT build_ordered_named_pair(2,1); -- fail
935+
ERROR: value for domain ordered_named_pair violates check constraint "ordered_named_pair_check"
936+
CONTEXT: while creating return value
937+
PL/Python function "build_ordered_named_pair"
938+
CREATE FUNCTION build_ordered_named_pairs(i int, j int) RETURNS ordered_named_pair[] AS $$
939+
return [{'i': i, 'j': j}, {'i': i, 'j': j+1}]
940+
$$ LANGUAGE plpythonu;
941+
SELECT build_ordered_named_pairs(1,2);
942+
build_ordered_named_pairs
943+
---------------------------
944+
{"(1,2)","(1,3)"}
945+
(1 row)
946+
947+
SELECT build_ordered_named_pairs(2,1); -- fail
948+
ERROR: value for domain ordered_named_pair violates check constraint "ordered_named_pair_check"
949+
CONTEXT: while creating return value
950+
PL/Python function "build_ordered_named_pairs"
823951
--
824952
-- Prepared statements
825953
--

src/pl/plpython/expected/plpython_types_3.out

+128
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,76 @@ SELECT * FROM test_type_conversion_array_domain_check_violation();
765765
ERROR: value for domain ordered_pair_domain violates check constraint "ordered_pair_domain_check"
766766
CONTEXT: while creating return value
767767
PL/Python function "test_type_conversion_array_domain_check_violation"
768+
--
769+
-- Arrays of domains
770+
--
771+
CREATE FUNCTION test_read_uint2_array(x uint2[]) RETURNS uint2 AS $$
772+
plpy.info(x, type(x))
773+
return x[0]
774+
$$ LANGUAGE plpythonu;
775+
select test_read_uint2_array(array[1::uint2]);
776+
INFO: ([1], <class 'list'>)
777+
test_read_uint2_array
778+
-----------------------
779+
1
780+
(1 row)
781+
782+
CREATE FUNCTION test_build_uint2_array(x int2) RETURNS uint2[] AS $$
783+
return [x, x]
784+
$$ LANGUAGE plpythonu;
785+
select test_build_uint2_array(1::int2);
786+
test_build_uint2_array
787+
------------------------
788+
{1,1}
789+
(1 row)
790+
791+
select test_build_uint2_array(-1::int2); -- fail
792+
ERROR: value for domain uint2 violates check constraint "uint2_check"
793+
CONTEXT: while creating return value
794+
PL/Python function "test_build_uint2_array"
795+
--
796+
-- ideally this would work, but for now it doesn't, because the return value
797+
-- is [[2,4], [2,4]] which our conversion code thinks should become a 2-D
798+
-- integer array, not an array of arrays.
799+
--
800+
CREATE FUNCTION test_type_conversion_domain_array(x integer[])
801+
RETURNS ordered_pair_domain[] AS $$
802+
return [x, x]
803+
$$ LANGUAGE plpythonu;
804+
select test_type_conversion_domain_array(array[2,4]);
805+
ERROR: return value of function with array return type is not a Python sequence
806+
CONTEXT: while creating return value
807+
PL/Python function "test_type_conversion_domain_array"
808+
select test_type_conversion_domain_array(array[4,2]); -- fail
809+
ERROR: return value of function with array return type is not a Python sequence
810+
CONTEXT: while creating return value
811+
PL/Python function "test_type_conversion_domain_array"
812+
CREATE FUNCTION test_type_conversion_domain_array2(x ordered_pair_domain)
813+
RETURNS integer AS $$
814+
plpy.info(x, type(x))
815+
return x[1]
816+
$$ LANGUAGE plpythonu;
817+
select test_type_conversion_domain_array2(array[2,4]);
818+
INFO: ([2, 4], <class 'list'>)
819+
test_type_conversion_domain_array2
820+
------------------------------------
821+
4
822+
(1 row)
823+
824+
select test_type_conversion_domain_array2(array[4,2]); -- fail
825+
ERROR: value for domain ordered_pair_domain violates check constraint "ordered_pair_domain_check"
826+
CREATE FUNCTION test_type_conversion_array_domain_array(x ordered_pair_domain[])
827+
RETURNS ordered_pair_domain AS $$
828+
plpy.info(x, type(x))
829+
return x[0]
830+
$$ LANGUAGE plpythonu;
831+
select test_type_conversion_array_domain_array(array[array[2,4]::ordered_pair_domain]);
832+
INFO: ([[2, 4]], <class 'list'>)
833+
test_type_conversion_array_domain_array
834+
-----------------------------------------
835+
{2,4}
836+
(1 row)
837+
768838
---
769839
--- Composite types
770840
---
@@ -820,6 +890,64 @@ SELECT test_composite_type_input(row(1, 2));
820890
3
821891
(1 row)
822892

893+
--
894+
-- Domains within composite
895+
--
896+
CREATE TYPE nnint_container AS (f1 int, f2 nnint);
897+
CREATE FUNCTION nnint_test(x int, y int) RETURNS nnint_container AS $$
898+
return {'f1': x, 'f2': y}
899+
$$ LANGUAGE plpythonu;
900+
SELECT nnint_test(null, 3);
901+
nnint_test
902+
------------
903+
(,3)
904+
(1 row)
905+
906+
SELECT nnint_test(3, null); -- fail
907+
ERROR: value for domain nnint violates check constraint "nnint_check"
908+
CONTEXT: while creating return value
909+
PL/Python function "nnint_test"
910+
--
911+
-- Domains of composite
912+
--
913+
CREATE DOMAIN ordered_named_pair AS named_pair_2 CHECK((VALUE).i <= (VALUE).j);
914+
CREATE FUNCTION read_ordered_named_pair(p ordered_named_pair) RETURNS integer AS $$
915+
return p['i'] + p['j']
916+
$$ LANGUAGE plpythonu;
917+
SELECT read_ordered_named_pair(row(1, 2));
918+
read_ordered_named_pair
919+
-------------------------
920+
3
921+
(1 row)
922+
923+
SELECT read_ordered_named_pair(row(2, 1)); -- fail
924+
ERROR: value for domain ordered_named_pair violates check constraint "ordered_named_pair_check"
925+
CREATE FUNCTION build_ordered_named_pair(i int, j int) RETURNS ordered_named_pair AS $$
926+
return {'i': i, 'j': j}
927+
$$ LANGUAGE plpythonu;
928+
SELECT build_ordered_named_pair(1,2);
929+
build_ordered_named_pair
930+
--------------------------
931+
(1,2)
932+
(1 row)
933+
934+
SELECT build_ordered_named_pair(2,1); -- fail
935+
ERROR: value for domain ordered_named_pair violates check constraint "ordered_named_pair_check"
936+
CONTEXT: while creating return value
937+
PL/Python function "build_ordered_named_pair"
938+
CREATE FUNCTION build_ordered_named_pairs(i int, j int) RETURNS ordered_named_pair[] AS $$
939+
return [{'i': i, 'j': j}, {'i': i, 'j': j+1}]
940+
$$ LANGUAGE plpythonu;
941+
SELECT build_ordered_named_pairs(1,2);
942+
build_ordered_named_pairs
943+
---------------------------
944+
{"(1,2)","(1,3)"}
945+
(1 row)
946+
947+
SELECT build_ordered_named_pairs(2,1); -- fail
948+
ERROR: value for domain ordered_named_pair violates check constraint "ordered_named_pair_check"
949+
CONTEXT: while creating return value
950+
PL/Python function "build_ordered_named_pairs"
823951
--
824952
-- Prepared statements
825953
--

0 commit comments

Comments
 (0)








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://github.com/tonybelloni/postgres/commit/687f096ea9da82d267f1809a5f3fdfa027092045

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy