diff --git a/src/pl/plpython/Makefile b/src/pl/plpython/Makefile index 77d66657e1d08d0589b4b33cee66f258e37e327a..11cd5a8811ef83a9880819beb1b92ee68273e730 100644 --- a/src/pl/plpython/Makefile +++ b/src/pl/plpython/Makefile @@ -1,4 +1,4 @@ -# $PostgreSQL: pgsql/src/pl/plpython/Makefile,v 1.33 2009/08/12 16:37:25 petere Exp $ +# $PostgreSQL: pgsql/src/pl/plpython/Makefile,v 1.34 2009/08/14 13:42:16 petere Exp $ subdir = src/pl/plpython top_builddir = ../../.. @@ -70,6 +70,7 @@ REGRESS = \ plpython_setof \ plpython_record \ plpython_trigger \ + plpython_types \ plpython_error \ plpython_unicode \ plpython_drop diff --git a/src/pl/plpython/expected/plpython_types.out b/src/pl/plpython/expected/plpython_types.out new file mode 100644 index 0000000000000000000000000000000000000000..fd4efe959644d30a15728bcb9c2c648dc484a446 --- /dev/null +++ b/src/pl/plpython/expected/plpython_types.out @@ -0,0 +1,378 @@ +-- +-- Test data type behavior +-- +-- +-- Base/common types +-- +CREATE FUNCTION test_type_conversion_bool(x bool) RETURNS bool AS $$ +plpy.info(x, type(x)) +return x +$$ LANGUAGE plpythonu; +SELECT * FROM test_type_conversion_bool(true); +INFO: (True, <type 'bool'>) +CONTEXT: PL/Python function "test_type_conversion_bool" + test_type_conversion_bool +--------------------------- + t +(1 row) + +SELECT * FROM test_type_conversion_bool(false); +INFO: (False, <type 'bool'>) +CONTEXT: PL/Python function "test_type_conversion_bool" + test_type_conversion_bool +--------------------------- + f +(1 row) + +SELECT * FROM test_type_conversion_bool(null); +INFO: (None, <type 'NoneType'>) +CONTEXT: PL/Python function "test_type_conversion_bool" + test_type_conversion_bool +--------------------------- + +(1 row) + +CREATE FUNCTION test_type_conversion_char(x char) RETURNS char AS $$ +plpy.info(x, type(x)) +return x +$$ LANGUAGE plpythonu; +SELECT * FROM test_type_conversion_char('a'); +INFO: ('a', <type 'str'>) +CONTEXT: PL/Python function "test_type_conversion_char" + test_type_conversion_char +--------------------------- + a +(1 row) + +SELECT * FROM test_type_conversion_char(null); +INFO: (None, <type 'NoneType'>) +CONTEXT: PL/Python function "test_type_conversion_char" + test_type_conversion_char +--------------------------- + +(1 row) + +CREATE FUNCTION test_type_conversion_int2(x int2) RETURNS int2 AS $$ +plpy.info(x, type(x)) +return x +$$ LANGUAGE plpythonu; +SELECT * FROM test_type_conversion_int2(100::int2); +INFO: (100, <type 'int'>) +CONTEXT: PL/Python function "test_type_conversion_int2" + test_type_conversion_int2 +--------------------------- + 100 +(1 row) + +SELECT * FROM test_type_conversion_int2(-100::int2); +INFO: (-100, <type 'int'>) +CONTEXT: PL/Python function "test_type_conversion_int2" + test_type_conversion_int2 +--------------------------- + -100 +(1 row) + +SELECT * FROM test_type_conversion_int2(null); +INFO: (None, <type 'NoneType'>) +CONTEXT: PL/Python function "test_type_conversion_int2" + test_type_conversion_int2 +--------------------------- + +(1 row) + +CREATE FUNCTION test_type_conversion_int4(x int4) RETURNS int4 AS $$ +plpy.info(x, type(x)) +return x +$$ LANGUAGE plpythonu; +SELECT * FROM test_type_conversion_int4(100); +INFO: (100, <type 'int'>) +CONTEXT: PL/Python function "test_type_conversion_int4" + test_type_conversion_int4 +--------------------------- + 100 +(1 row) + +SELECT * FROM test_type_conversion_int4(-100); +INFO: (-100, <type 'int'>) +CONTEXT: PL/Python function "test_type_conversion_int4" + test_type_conversion_int4 +--------------------------- + -100 +(1 row) + +SELECT * FROM test_type_conversion_int4(null); +INFO: (None, <type 'NoneType'>) +CONTEXT: PL/Python function "test_type_conversion_int4" + test_type_conversion_int4 +--------------------------- + +(1 row) + +CREATE FUNCTION test_type_conversion_int8(x int8) RETURNS int8 AS $$ +plpy.info(x, type(x)) +return x +$$ LANGUAGE plpythonu; +SELECT * FROM test_type_conversion_int8(100); +INFO: (100L, <type 'long'>) +CONTEXT: PL/Python function "test_type_conversion_int8" + test_type_conversion_int8 +--------------------------- + 100 +(1 row) + +SELECT * FROM test_type_conversion_int8(-100); +INFO: (-100L, <type 'long'>) +CONTEXT: PL/Python function "test_type_conversion_int8" + test_type_conversion_int8 +--------------------------- + -100 +(1 row) + +SELECT * FROM test_type_conversion_int8(5000000000); +INFO: (5000000000L, <type 'long'>) +CONTEXT: PL/Python function "test_type_conversion_int8" + test_type_conversion_int8 +--------------------------- + 5000000000 +(1 row) + +SELECT * FROM test_type_conversion_int8(null); +INFO: (None, <type 'NoneType'>) +CONTEXT: PL/Python function "test_type_conversion_int8" + test_type_conversion_int8 +--------------------------- + +(1 row) + +CREATE FUNCTION test_type_conversion_numeric(x numeric) RETURNS numeric AS $$ +plpy.info(x, type(x)) +return x +$$ LANGUAGE plpythonu; +/* The current implementation converts numeric to float. */ +SELECT * FROM test_type_conversion_numeric(100); +INFO: (100.0, <type 'float'>) +CONTEXT: PL/Python function "test_type_conversion_numeric" + test_type_conversion_numeric +------------------------------ + 100.0 +(1 row) + +SELECT * FROM test_type_conversion_numeric(-100); +INFO: (-100.0, <type 'float'>) +CONTEXT: PL/Python function "test_type_conversion_numeric" + test_type_conversion_numeric +------------------------------ + -100.0 +(1 row) + +SELECT * FROM test_type_conversion_numeric(5000000000.5); +INFO: (5000000000.5, <type 'float'>) +CONTEXT: PL/Python function "test_type_conversion_numeric" + test_type_conversion_numeric +------------------------------ + 5000000000.5 +(1 row) + +SELECT * FROM test_type_conversion_numeric(79228162514264337593543950336); +INFO: (7.9228162514264338e+28, <type 'float'>) +CONTEXT: PL/Python function "test_type_conversion_numeric" + test_type_conversion_numeric +------------------------------- + 79228162514300000000000000000 +(1 row) + +SELECT * FROM test_type_conversion_numeric(null); +INFO: (None, <type 'NoneType'>) +CONTEXT: PL/Python function "test_type_conversion_numeric" + test_type_conversion_numeric +------------------------------ + +(1 row) + +CREATE FUNCTION test_type_conversion_float4(x float4) RETURNS float4 AS $$ +plpy.info(x, type(x)) +return x +$$ LANGUAGE plpythonu; +SELECT * FROM test_type_conversion_float4(100); +INFO: (100.0, <type 'float'>) +CONTEXT: PL/Python function "test_type_conversion_float4" + test_type_conversion_float4 +----------------------------- + 100 +(1 row) + +SELECT * FROM test_type_conversion_float4(-100); +INFO: (-100.0, <type 'float'>) +CONTEXT: PL/Python function "test_type_conversion_float4" + test_type_conversion_float4 +----------------------------- + -100 +(1 row) + +SELECT * FROM test_type_conversion_float4(5000.5); +INFO: (5000.5, <type 'float'>) +CONTEXT: PL/Python function "test_type_conversion_float4" + test_type_conversion_float4 +----------------------------- + 5000.5 +(1 row) + +SELECT * FROM test_type_conversion_float4(null); +INFO: (None, <type 'NoneType'>) +CONTEXT: PL/Python function "test_type_conversion_float4" + test_type_conversion_float4 +----------------------------- + +(1 row) + +CREATE FUNCTION test_type_conversion_float8(x float8) RETURNS float8 AS $$ +plpy.info(x, type(x)) +return x +$$ LANGUAGE plpythonu; +SELECT * FROM test_type_conversion_float8(100); +INFO: (100.0, <type 'float'>) +CONTEXT: PL/Python function "test_type_conversion_float8" + test_type_conversion_float8 +----------------------------- + 100 +(1 row) + +SELECT * FROM test_type_conversion_float8(-100); +INFO: (-100.0, <type 'float'>) +CONTEXT: PL/Python function "test_type_conversion_float8" + test_type_conversion_float8 +----------------------------- + -100 +(1 row) + +SELECT * FROM test_type_conversion_float8(5000000000.5); +INFO: (5000000000.5, <type 'float'>) +CONTEXT: PL/Python function "test_type_conversion_float8" + test_type_conversion_float8 +----------------------------- + 5000000000.5 +(1 row) + +SELECT * FROM test_type_conversion_float8(null); +INFO: (None, <type 'NoneType'>) +CONTEXT: PL/Python function "test_type_conversion_float8" + test_type_conversion_float8 +----------------------------- + +(1 row) + +CREATE FUNCTION test_type_conversion_text(x text) RETURNS text AS $$ +plpy.info(x, type(x)) +return x +$$ LANGUAGE plpythonu; +SELECT * FROM test_type_conversion_text('hello world'); +INFO: ('hello world', <type 'str'>) +CONTEXT: PL/Python function "test_type_conversion_text" + test_type_conversion_text +--------------------------- + hello world +(1 row) + +SELECT * FROM test_type_conversion_text(null); +INFO: (None, <type 'NoneType'>) +CONTEXT: PL/Python function "test_type_conversion_text" + test_type_conversion_text +--------------------------- + +(1 row) + +CREATE FUNCTION test_type_conversion_bytea(x bytea) RETURNS bytea AS $$ +plpy.info(x, type(x)) +return x +$$ LANGUAGE plpythonu; +SELECT * FROM test_type_conversion_bytea('hello world'); +INFO: ('\\x68656c6c6f20776f726c64', <type 'str'>) +CONTEXT: PL/Python function "test_type_conversion_bytea" + test_type_conversion_bytea +---------------------------- + \x68656c6c6f20776f726c64 +(1 row) + +SELECT * FROM test_type_conversion_bytea(null); +INFO: (None, <type 'NoneType'>) +CONTEXT: PL/Python function "test_type_conversion_bytea" + test_type_conversion_bytea +---------------------------- + +(1 row) + +CREATE FUNCTION test_type_marshal() RETURNS bytea AS $$ +import marshal +return marshal.dumps('hello world') +$$ LANGUAGE plpythonu; +CREATE FUNCTION test_type_unmarshal(x bytea) RETURNS text AS $$ +import marshal +try: + return marshal.loads(x) +except ValueError, e: + return 'FAILED: ' + str(e) +$$ LANGUAGE plpythonu; +/* This will currently fail because the bytea datum is presented to + Python as a string in bytea-encoding, which Python doesn't understand. */ +SELECT test_type_unmarshal(x) FROM test_type_marshal() x; + test_type_unmarshal +-------------------------- + FAILED: bad marshal data +(1 row) + +-- +-- Domains +-- +CREATE DOMAIN uint2 AS int2 CHECK (VALUE >= 0); +CREATE FUNCTION test_type_conversion_uint2(x uint2, y int) RETURNS uint2 AS $$ +plpy.info(x, type(x)) +return y +$$ LANGUAGE plpythonu; +SELECT * FROM test_type_conversion_uint2(100::uint2, 50); +INFO: (100, <type 'int'>) +CONTEXT: PL/Python function "test_type_conversion_uint2" + test_type_conversion_uint2 +---------------------------- + 50 +(1 row) + +SELECT * FROM test_type_conversion_uint2(100::uint2, -50); +INFO: (100, <type 'int'>) +CONTEXT: PL/Python function "test_type_conversion_uint2" +ERROR: value for domain uint2 violates check constraint "uint2_check" +CONTEXT: PL/Python function "test_type_conversion_uint2" +SELECT * FROM test_type_conversion_uint2(null, 1); +INFO: (None, <type 'NoneType'>) +CONTEXT: PL/Python function "test_type_conversion_uint2" + test_type_conversion_uint2 +---------------------------- + 1 +(1 row) + +CREATE DOMAIN bytea10 AS bytea CHECK (octet_length(VALUE) = 10 AND VALUE IS NOT NULL); +CREATE FUNCTION test_type_conversion_bytea10(x bytea10, y bytea) RETURNS bytea10 AS $$ +plpy.info(x, type(x)) +return y +$$ LANGUAGE plpythonu; +SELECT * FROM test_type_conversion_bytea10('hello wold', 'hello wold'); +INFO: ('\\x68656c6c6f20776f6c64', <type 'str'>) +CONTEXT: PL/Python function "test_type_conversion_bytea10" + test_type_conversion_bytea10 +------------------------------ + \x68656c6c6f20776f6c64 +(1 row) + +SELECT * FROM test_type_conversion_bytea10('hello world', 'hello wold'); +ERROR: value for domain bytea10 violates check constraint "bytea10_check" +SELECT * FROM test_type_conversion_bytea10('hello word', 'hello world'); +INFO: ('\\x68656c6c6f20776f7264', <type 'str'>) +CONTEXT: PL/Python function "test_type_conversion_bytea10" +ERROR: value for domain bytea10 violates check constraint "bytea10_check" +CONTEXT: PL/Python function "test_type_conversion_bytea10" +SELECT * FROM test_type_conversion_bytea10(null, 'hello word'); +ERROR: value for domain bytea10 violates check constraint "bytea10_check" +SELECT * FROM test_type_conversion_bytea10('hello word', null); +INFO: ('\\x68656c6c6f20776f7264', <type 'str'>) +CONTEXT: PL/Python function "test_type_conversion_bytea10" +ERROR: value for domain bytea10 violates check constraint "bytea10_check" +CONTEXT: PL/Python function "test_type_conversion_bytea10" diff --git a/src/pl/plpython/sql/plpython_types.sql b/src/pl/plpython/sql/plpython_types.sql new file mode 100644 index 0000000000000000000000000000000000000000..d0579962a9487e558f735be8a3d7dcae1fa1552e --- /dev/null +++ b/src/pl/plpython/sql/plpython_types.sql @@ -0,0 +1,157 @@ +-- +-- Test data type behavior +-- + +-- +-- Base/common types +-- + +CREATE FUNCTION test_type_conversion_bool(x bool) RETURNS bool AS $$ +plpy.info(x, type(x)) +return x +$$ LANGUAGE plpythonu; + +SELECT * FROM test_type_conversion_bool(true); +SELECT * FROM test_type_conversion_bool(false); +SELECT * FROM test_type_conversion_bool(null); + + +CREATE FUNCTION test_type_conversion_char(x char) RETURNS char AS $$ +plpy.info(x, type(x)) +return x +$$ LANGUAGE plpythonu; + +SELECT * FROM test_type_conversion_char('a'); +SELECT * FROM test_type_conversion_char(null); + + +CREATE FUNCTION test_type_conversion_int2(x int2) RETURNS int2 AS $$ +plpy.info(x, type(x)) +return x +$$ LANGUAGE plpythonu; + +SELECT * FROM test_type_conversion_int2(100::int2); +SELECT * FROM test_type_conversion_int2(-100::int2); +SELECT * FROM test_type_conversion_int2(null); + + +CREATE FUNCTION test_type_conversion_int4(x int4) RETURNS int4 AS $$ +plpy.info(x, type(x)) +return x +$$ LANGUAGE plpythonu; + +SELECT * FROM test_type_conversion_int4(100); +SELECT * FROM test_type_conversion_int4(-100); +SELECT * FROM test_type_conversion_int4(null); + + +CREATE FUNCTION test_type_conversion_int8(x int8) RETURNS int8 AS $$ +plpy.info(x, type(x)) +return x +$$ LANGUAGE plpythonu; + +SELECT * FROM test_type_conversion_int8(100); +SELECT * FROM test_type_conversion_int8(-100); +SELECT * FROM test_type_conversion_int8(5000000000); +SELECT * FROM test_type_conversion_int8(null); + + +CREATE FUNCTION test_type_conversion_numeric(x numeric) RETURNS numeric AS $$ +plpy.info(x, type(x)) +return x +$$ LANGUAGE plpythonu; + +/* The current implementation converts numeric to float. */ +SELECT * FROM test_type_conversion_numeric(100); +SELECT * FROM test_type_conversion_numeric(-100); +SELECT * FROM test_type_conversion_numeric(5000000000.5); +SELECT * FROM test_type_conversion_numeric(79228162514264337593543950336); +SELECT * FROM test_type_conversion_numeric(null); + + +CREATE FUNCTION test_type_conversion_float4(x float4) RETURNS float4 AS $$ +plpy.info(x, type(x)) +return x +$$ LANGUAGE plpythonu; + +SELECT * FROM test_type_conversion_float4(100); +SELECT * FROM test_type_conversion_float4(-100); +SELECT * FROM test_type_conversion_float4(5000.5); +SELECT * FROM test_type_conversion_float4(null); + + +CREATE FUNCTION test_type_conversion_float8(x float8) RETURNS float8 AS $$ +plpy.info(x, type(x)) +return x +$$ LANGUAGE plpythonu; + +SELECT * FROM test_type_conversion_float8(100); +SELECT * FROM test_type_conversion_float8(-100); +SELECT * FROM test_type_conversion_float8(5000000000.5); +SELECT * FROM test_type_conversion_float8(null); + + +CREATE FUNCTION test_type_conversion_text(x text) RETURNS text AS $$ +plpy.info(x, type(x)) +return x +$$ LANGUAGE plpythonu; + +SELECT * FROM test_type_conversion_text('hello world'); +SELECT * FROM test_type_conversion_text(null); + + +CREATE FUNCTION test_type_conversion_bytea(x bytea) RETURNS bytea AS $$ +plpy.info(x, type(x)) +return x +$$ LANGUAGE plpythonu; + +SELECT * FROM test_type_conversion_bytea('hello world'); +SELECT * FROM test_type_conversion_bytea(null); + + +CREATE FUNCTION test_type_marshal() RETURNS bytea AS $$ +import marshal +return marshal.dumps('hello world') +$$ LANGUAGE plpythonu; + +CREATE FUNCTION test_type_unmarshal(x bytea) RETURNS text AS $$ +import marshal +try: + return marshal.loads(x) +except ValueError, e: + return 'FAILED: ' + str(e) +$$ LANGUAGE plpythonu; + +/* This will currently fail because the bytea datum is presented to + Python as a string in bytea-encoding, which Python doesn't understand. */ +SELECT test_type_unmarshal(x) FROM test_type_marshal() x; + + +-- +-- Domains +-- + +CREATE DOMAIN uint2 AS int2 CHECK (VALUE >= 0); + +CREATE FUNCTION test_type_conversion_uint2(x uint2, y int) RETURNS uint2 AS $$ +plpy.info(x, type(x)) +return y +$$ LANGUAGE plpythonu; + +SELECT * FROM test_type_conversion_uint2(100::uint2, 50); +SELECT * FROM test_type_conversion_uint2(100::uint2, -50); +SELECT * FROM test_type_conversion_uint2(null, 1); + + +CREATE DOMAIN bytea10 AS bytea CHECK (octet_length(VALUE) = 10 AND VALUE IS NOT NULL); + +CREATE FUNCTION test_type_conversion_bytea10(x bytea10, y bytea) RETURNS bytea10 AS $$ +plpy.info(x, type(x)) +return y +$$ LANGUAGE plpythonu; + +SELECT * FROM test_type_conversion_bytea10('hello wold', 'hello wold'); +SELECT * FROM test_type_conversion_bytea10('hello world', 'hello wold'); +SELECT * FROM test_type_conversion_bytea10('hello word', 'hello world'); +SELECT * FROM test_type_conversion_bytea10(null, 'hello word'); +SELECT * FROM test_type_conversion_bytea10('hello word', null);