diff options
1143 files changed, 86723 insertions, 142377 deletions
diff --git a/.gitignore b/.gitignore index d42475b167..13c4bde5e6 100644 --- a/.gitignore +++ b/.gitignore @@ -40,4 +40,4 @@ lib*.pc /StormDB* /cscope* /.gitignore - +/tmp_install/ diff --git a/GNUmakefile.in b/GNUmakefile.in index 332ab7c36b..8bbd1cb83d 100644 --- a/GNUmakefile.in +++ b/GNUmakefile.in @@ -47,6 +47,7 @@ $(call recurse,distprep,doc-xc src config contrib) # it's not built by default $(call recurse,clean,doc-xc contrib src config) clean: + rm -rf tmp_install/ # Garbage from autoconf: @rm -rf autom4te.cache/ @@ -57,11 +58,10 @@ distclean maintainer-clean: $(MAKE) -C contrib $@ $(MAKE) -C config $@ $(MAKE) -C src $@ - rm -f config.cache config.log config.status GNUmakefile + rm -rf tmp_install/ # Garbage from autoconf: @rm -rf autom4te.cache/ - -check check-tests: all + rm -f config.cache config.log config.status GNUmakefile check check-tests installcheck installcheck-parallel installcheck-tests: $(MAKE) -C src/test/regress $@ diff --git a/config/python.m4 b/config/python.m4 index 7012c536d7..b95c8ed3b3 100644 --- a/config/python.m4 +++ b/config/python.m4 @@ -44,6 +44,9 @@ if a == b: print(a) else: print(a + ' ' + b)"` +if test "$PORTNAME" = win32 ; then + python_includespec=`echo $python_includespec | sed 's,[[\]],/,g'` +fi AC_MSG_RESULT([$python_includespec]) AC_SUBST(python_majorversion)[]dnl @@ -93,20 +96,5 @@ AC_MSG_RESULT([${python_libspec} ${python_additional_libs}]) AC_SUBST(python_libdir)[]dnl AC_SUBST(python_libspec)[]dnl AC_SUBST(python_additional_libs)[]dnl -AC_SUBST(python_enable_shared)[]dnl - -# threaded python is not supported on OpenBSD -AC_MSG_CHECKING(whether Python is compiled with thread support) -pythreads=`${PYTHON} -c "import sys; print(int('thread' in sys.builtin_module_names))"` -if test "$pythreads" = "1"; then - AC_MSG_RESULT(yes) - case $host_os in - openbsd*) - AC_MSG_ERROR([threaded Python not supported on this platform]) - ;; - esac -else - AC_MSG_RESULT(no) -fi ])# PGAC_CHECK_PYTHON_EMBED_SETUP @@ -642,7 +642,6 @@ TCL_SHLIB_LD_LIBS TCL_SHARED_BUILD TCL_LIB_SPEC TCL_LIBS -TCL_LIB_FILE TCL_INCLUDE_SPEC TCL_CONFIG_SH TCLSH @@ -663,7 +662,6 @@ HAVE_IPV6 LIBOBJS UUID_LIBS ZIC -python_enable_shared python_additional_libs python_libspec python_libdir @@ -7396,6 +7394,12 @@ perl_useshrplib=`$PERL -MConfig -e 'print $Config{useshrplib}'` test "$PORTNAME" = "win32" && perl_useshrplib=`echo $perl_useshrplib | sed 's,\\\\,/,g'` { $as_echo "$as_me:${as_lineno-$LINENO}: result: $perl_useshrplib" >&5 $as_echo "$perl_useshrplib" >&6; } + if test "$perl_useshrplib" != yes && test "$perl_useshrplib" != true; then + as_fn_error $? "cannot build PL/Perl because libperl is not a shared library +You might have to rebuild your Perl installation. Refer to the +documentation for details. Use --without-perl to disable building +PL/Perl." "$LINENO" 5 + fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for flags to link embedded Perl" >&5 $as_echo_n "checking for flags to link embedded Perl... " >&6; } @@ -7495,6 +7499,9 @@ if a == b: print(a) else: print(a + ' ' + b)"` +if test "$PORTNAME" = win32 ; then + python_includespec=`echo $python_includespec | sed 's,[\],/,g'` +fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $python_includespec" >&5 $as_echo "$python_includespec" >&6; } @@ -7531,24 +7538,41 @@ python_additional_libs=`${PYTHON} -c "import distutils.sysconfig; print(' '.join $as_echo "${python_libspec} ${python_additional_libs}" >&6; } -# threaded python is not supported on OpenBSD -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether Python is compiled with thread support" >&5 -$as_echo_n "checking whether Python is compiled with thread support... " >&6; } -pythreads=`${PYTHON} -c "import sys; print(int('thread' in sys.builtin_module_names))"` -if test "$pythreads" = "1"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - case $host_os in - openbsd*) - as_fn_error $? "threaded Python not supported on this platform" "$LINENO" 5 - ;; - esac -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi + # We need libpython as a shared library. With Python >=2.5, we + # check the Py_ENABLE_SHARED setting. On Debian, the setting is not + # correct before the jessie release (http://bugs.debian.org/695979). + # We also want to support older Python versions. So as a fallback + # we see if there is a file that is named like a shared library. + + if test "$python_enable_shared" != 1; then + if test "$PORTNAME" = darwin; then + # OS X does supply a .dylib even though Py_ENABLE_SHARED does + # not get set. The file detection logic below doesn't succeed + # on older OS X versions, so make it explicit. + python_enable_shared=1 + elif test "$PORTNAME" = win32; then + # Windows also needs an explicit override. + python_enable_shared=1 + else + # We don't know the platform shared library extension here yet, + # so we try some candidates. + for dlsuffix in .so .sl; do + if ls "$python_libdir"/libpython*${dlsuffix}* >/dev/null 2>&1; then + python_enable_shared=1 + break + fi + done + fi + fi + + if test "$python_enable_shared" != 1; then + as_fn_error $? "cannot build PL/Python because libpython is not a shared library +You might have to rebuild your Python installation. Refer to the +documentation for details. Use --without-python to disable building +PL/Python." "$LINENO" 5 + fi fi if test "$cross_compiling" = yes && test -z "$with_system_tzdata"; then @@ -14748,12 +14772,15 @@ fi . "$TCL_CONFIG_SH" eval TCL_INCLUDE_SPEC=\"$TCL_INCLUDE_SPEC\" -eval TCL_LIB_FILE=\"$TCL_LIB_FILE\" eval TCL_LIBS=\"$TCL_LIBS\" eval TCL_LIB_SPEC=\"$TCL_LIB_SPEC\" eval TCL_SHARED_BUILD=\"$TCL_SHARED_BUILD\" - # now that we have TCL_INCLUDE_SPEC, we can check for <tcl.h> + if test "$TCL_SHARED_BUILD" != 1; then + as_fn_error $? "cannot build PL/Tcl because Tcl is not a shared library +Use --without-tcl to disable building PL/Tcl." "$LINENO" 5 + fi + # now that we have TCL_INCLUDE_SPEC, we can check for <tcl.h> ac_save_CPPFLAGS=$CPPFLAGS CPPFLAGS="$TCL_INCLUDE_SPEC $CPPFLAGS" ac_fn_c_check_header_mongrel "$LINENO" "tcl.h" "ac_cv_header_tcl_h" "$ac_includes_default" diff --git a/configure.in b/configure.in index 6de315e2b5..e0941e52bc 100644 --- a/configure.in +++ b/configure.in @@ -889,12 +889,52 @@ if test "$with_perl" = yes; then AC_MSG_ERROR([Perl not found]) fi PGAC_CHECK_PERL_CONFIGS([archlibexp,privlibexp,useshrplib]) + if test "$perl_useshrplib" != yes && test "$perl_useshrplib" != true; then + AC_MSG_ERROR([cannot build PL/Perl because libperl is not a shared library +You might have to rebuild your Perl installation. Refer to the +documentation for details. Use --without-perl to disable building +PL/Perl.]) + fi PGAC_CHECK_PERL_EMBED_LDFLAGS fi if test "$with_python" = yes; then PGAC_PATH_PYTHON PGAC_CHECK_PYTHON_EMBED_SETUP + + # We need libpython as a shared library. With Python >=2.5, we + # check the Py_ENABLE_SHARED setting. On Debian, the setting is not + # correct before the jessie release (http://bugs.debian.org/695979). + # We also want to support older Python versions. So as a fallback + # we see if there is a file that is named like a shared library. + + if test "$python_enable_shared" != 1; then + if test "$PORTNAME" = darwin; then + # OS X does supply a .dylib even though Py_ENABLE_SHARED does + # not get set. The file detection logic below doesn't succeed + # on older OS X versions, so make it explicit. + python_enable_shared=1 + elif test "$PORTNAME" = win32; then + # Windows also needs an explicit override. + python_enable_shared=1 + else + # We don't know the platform shared library extension here yet, + # so we try some candidates. + for dlsuffix in .so .sl; do + if ls "$python_libdir"/libpython*${dlsuffix}* >/dev/null 2>&1; then + python_enable_shared=1 + break + fi + done + fi + fi + + if test "$python_enable_shared" != 1; then + AC_MSG_ERROR([cannot build PL/Python because libpython is not a shared library +You might have to rebuild your Python installation. Refer to the +documentation for details. Use --without-python to disable building +PL/Python.]) + fi fi if test "$cross_compiling" = yes && test -z "$with_system_tzdata"; then @@ -1942,8 +1982,12 @@ fi if test "$with_tcl" = yes; then PGAC_PATH_TCLCONFIGSH([$with_tclconfig]) PGAC_EVAL_TCLCONFIGSH([$TCL_CONFIG_SH], - [TCL_INCLUDE_SPEC,TCL_LIB_FILE,TCL_LIBS,TCL_LIB_SPEC,TCL_SHARED_BUILD]) + [TCL_INCLUDE_SPEC,TCL_LIBS,TCL_LIB_SPEC,TCL_SHARED_BUILD]) AC_SUBST(TCL_SHLIB_LD_LIBS)dnl don't want to double-evaluate that one + if test "$TCL_SHARED_BUILD" != 1; then + AC_MSG_ERROR([cannot build PL/Tcl because Tcl is not a shared library +Use --without-tcl to disable building PL/Tcl.]) + fi # now that we have TCL_INCLUDE_SPEC, we can check for <tcl.h> ac_save_CPPFLAGS=$CPPFLAGS CPPFLAGS="$TCL_INCLUDE_SPEC $CPPFLAGS" diff --git a/contrib/Makefile b/contrib/Makefile index b8230aadf7..0eedf440a6 100644 --- a/contrib/Makefile +++ b/contrib/Makefile @@ -33,21 +33,20 @@ SUBDIRS = \ pg_prewarm \ pg_standby \ pg_stat_statements \ - pg_test_fsync \ - pg_test_timing \ pg_trgm \ pgcrypto \ pgrowlocks \ pgstattuple \ pgxc_clean \ pgxc_ctl \ - pg_xlogdump \ postgres_fdw \ seg \ spi \ tablefunc \ tcn \ test_decoding \ + tsm_system_rows \ + tsm_system_time \ tsearch2 \ unaccent \ vacuumlo \ @@ -77,6 +76,18 @@ else ALWAYS_SUBDIRS += sepgsql endif +ifeq ($(with_perl),yes) +SUBDIRS += hstore_plperl +else +ALWAYS_SUBDIRS += hstore_plperl +endif + +ifeq ($(with_python),yes) +SUBDIRS += hstore_plpython ltree_plpython +else +ALWAYS_SUBDIRS += hstore_plpython ltree_plpython +endif + # Missing: # start-scripts \ (does not have a makefile) diff --git a/contrib/btree_gin/btree_gin.c b/contrib/btree_gin/btree_gin.c index 1a5bb3cdc6..f74e912ed7 100644 --- a/contrib/btree_gin/btree_gin.c +++ b/contrib/btree_gin/btree_gin.c @@ -5,7 +5,7 @@ #include <limits.h> -#include "access/skey.h" +#include "access/stratnum.h" #include "utils/builtins.h" #include "utils/bytea.h" #include "utils/cash.h" @@ -113,12 +113,12 @@ gin_btree_compare_prefix(FunctionCallInfo fcinfo) cmp; cmp = DatumGetInt32(DirectFunctionCall2Coll( - data->typecmp, - PG_GET_COLLATION(), - (data->strategy == BTLessStrategyNumber || - data->strategy == BTLessEqualStrategyNumber) - ? data->datum : a, - b)); + data->typecmp, + PG_GET_COLLATION(), + (data->strategy == BTLessStrategyNumber || + data->strategy == BTLessEqualStrategyNumber) + ? data->datum : a, + b)); switch (data->strategy) { @@ -186,14 +186,14 @@ Datum \ gin_extract_value_##type(PG_FUNCTION_ARGS) \ { \ return gin_btree_extract_value(fcinfo, is_varlena); \ -} \ +} \ PG_FUNCTION_INFO_V1(gin_extract_query_##type); \ Datum \ gin_extract_query_##type(PG_FUNCTION_ARGS) \ { \ return gin_btree_extract_query(fcinfo, \ is_varlena, leftmostvalue, typecmp); \ -} \ +} \ PG_FUNCTION_INFO_V1(gin_compare_prefix_##type); \ Datum \ gin_compare_prefix_##type(PG_FUNCTION_ARGS) \ @@ -209,6 +209,7 @@ leftmostvalue_int2(void) { return Int16GetDatum(SHRT_MIN); } + GIN_SUPPORT(int2, false, leftmostvalue_int2, btint2cmp) static Datum @@ -216,6 +217,7 @@ leftmostvalue_int4(void) { return Int32GetDatum(INT_MIN); } + GIN_SUPPORT(int4, false, leftmostvalue_int4, btint4cmp) static Datum @@ -226,6 +228,7 @@ leftmostvalue_int8(void) */ return Int64GetDatum(SEQ_MINVALUE); } + GIN_SUPPORT(int8, false, leftmostvalue_int8, btint8cmp) static Datum @@ -233,6 +236,7 @@ leftmostvalue_float4(void) { return Float4GetDatum(-get_float4_infinity()); } + GIN_SUPPORT(float4, false, leftmostvalue_float4, btfloat4cmp) static Datum @@ -240,6 +244,7 @@ leftmostvalue_float8(void) { return Float8GetDatum(-get_float8_infinity()); } + GIN_SUPPORT(float8, false, leftmostvalue_float8, btfloat8cmp) static Datum @@ -250,6 +255,7 @@ leftmostvalue_money(void) */ return Int64GetDatum(SEQ_MINVALUE); } + GIN_SUPPORT(money, false, leftmostvalue_money, cash_cmp) static Datum @@ -257,6 +263,7 @@ leftmostvalue_oid(void) { return ObjectIdGetDatum(0); } + GIN_SUPPORT(oid, false, leftmostvalue_oid, btoidcmp) static Datum @@ -264,6 +271,7 @@ leftmostvalue_timestamp(void) { return TimestampGetDatum(DT_NOBEGIN); } + GIN_SUPPORT(timestamp, false, leftmostvalue_timestamp, timestamp_cmp) GIN_SUPPORT(timestamptz, false, leftmostvalue_timestamp, timestamp_cmp) @@ -273,6 +281,7 @@ leftmostvalue_time(void) { return TimeADTGetDatum(0); } + GIN_SUPPORT(time, false, leftmostvalue_time, time_cmp) static Datum @@ -285,6 +294,7 @@ leftmostvalue_timetz(void) return TimeTzADTPGetDatum(v); } + GIN_SUPPORT(timetz, false, leftmostvalue_timetz, timetz_cmp) static Datum @@ -292,6 +302,7 @@ leftmostvalue_date(void) { return DateADTGetDatum(DATEVAL_NOBEGIN); } + GIN_SUPPORT(date, false, leftmostvalue_date, date_cmp) static Datum @@ -304,6 +315,7 @@ leftmostvalue_interval(void) v->month = 0; return IntervalPGetDatum(v); } + GIN_SUPPORT(interval, false, leftmostvalue_interval, interval_cmp) static Datum @@ -313,6 +325,7 @@ leftmostvalue_macaddr(void) return MacaddrPGetDatum(v); } + GIN_SUPPORT(macaddr, false, leftmostvalue_macaddr, macaddr_cmp) static Datum @@ -320,6 +333,7 @@ leftmostvalue_inet(void) { return DirectFunctionCall1(inet_in, CStringGetDatum("0.0.0.0/0")); } + GIN_SUPPORT(inet, true, leftmostvalue_inet, network_cmp) GIN_SUPPORT(cidr, true, leftmostvalue_inet, network_cmp) @@ -329,6 +343,7 @@ leftmostvalue_text(void) { return PointerGetDatum(cstring_to_text_with_len("", 0)); } + GIN_SUPPORT(text, true, leftmostvalue_text, bttextcmp) static Datum @@ -336,6 +351,7 @@ leftmostvalue_char(void) { return CharGetDatum(SCHAR_MIN); } + GIN_SUPPORT(char, false, leftmostvalue_char, btcharcmp) GIN_SUPPORT(bytea, true, leftmostvalue_text, byteacmp) @@ -348,6 +364,7 @@ leftmostvalue_bit(void) ObjectIdGetDatum(0), Int32GetDatum(-1)); } + GIN_SUPPORT(bit, true, leftmostvalue_bit, bitcmp) static Datum @@ -358,6 +375,7 @@ leftmostvalue_varbit(void) ObjectIdGetDatum(0), Int32GetDatum(-1)); } + GIN_SUPPORT(varbit, true, leftmostvalue_varbit, bitcmp) /* @@ -402,4 +420,5 @@ leftmostvalue_numeric(void) { return PointerGetDatum(NULL); } + GIN_SUPPORT(numeric, true, leftmostvalue_numeric, gin_numeric_cmp) diff --git a/contrib/btree_gist/btree_utils_num.c b/contrib/btree_gist/btree_utils_num.c index 5bfe659f91..99cb41f5f5 100644 --- a/contrib/btree_gist/btree_utils_num.c +++ b/contrib/btree_gist/btree_utils_num.c @@ -13,7 +13,7 @@ GISTENTRY * gbt_num_compress(GISTENTRY *entry, const gbtree_ninfo *tinfo) { - GISTENTRY *retval; + GISTENTRY *retval; if (entry->leafkey) { diff --git a/contrib/btree_gist/btree_utils_var.c b/contrib/btree_gist/btree_utils_var.c index 78e8662add..8105a3b035 100644 --- a/contrib/btree_gist/btree_utils_var.c +++ b/contrib/btree_gist/btree_utils_var.c @@ -71,7 +71,7 @@ gbt_var_key_readable(const GBT_VARKEY *k) * Create a leaf-entry to store in the index, from a single Datum. */ static GBT_VARKEY * -gbt_var_key_from_datum(const struct varlena *u) +gbt_var_key_from_datum(const struct varlena * u) { int32 lowersize = VARSIZE(u); GBT_VARKEY *r; diff --git a/contrib/citext/Makefile b/contrib/citext/Makefile index 267854b5de..61e04bce7a 100644 --- a/contrib/citext/Makefile +++ b/contrib/citext/Makefile @@ -3,7 +3,7 @@ MODULES = citext EXTENSION = citext -DATA = citext--1.0.sql citext--unpackaged--1.0.sql +DATA = citext--1.1.sql citext--1.0--1.1.sql citext--unpackaged--1.0.sql PGFILEDESC = "citext - case-insensitive character string data type" REGRESS = citext diff --git a/contrib/citext/citext--1.0--1.1.sql b/contrib/citext/citext--1.0--1.1.sql new file mode 100644 index 0000000000..e06627e025 --- /dev/null +++ b/contrib/citext/citext--1.0--1.1.sql @@ -0,0 +1,21 @@ +/* contrib/citext/citext--1.0--1.1.sql */ + +-- complain if script is sourced in psql, rather than via ALTER EXTENSION +\echo Use "ALTER EXTENSION citext UPDATE TO '1.1'" to load this file. \quit + +/* First we have to remove them from the extension */ +ALTER EXTENSION citext DROP FUNCTION regexp_matches( citext, citext ); +ALTER EXTENSION citext DROP FUNCTION regexp_matches( citext, citext, text ); + +/* Then we can drop them */ +DROP FUNCTION regexp_matches( citext, citext ); +DROP FUNCTION regexp_matches( citext, citext, text ); + +/* Now redefine */ +CREATE FUNCTION regexp_matches( citext, citext ) RETURNS SETOF TEXT[] AS $$ + SELECT pg_catalog.regexp_matches( $1::pg_catalog.text, $2::pg_catalog.text, 'i' ); +$$ LANGUAGE SQL IMMUTABLE STRICT ROWS 1; + +CREATE FUNCTION regexp_matches( citext, citext, text ) RETURNS SETOF TEXT[] AS $$ + SELECT pg_catalog.regexp_matches( $1::pg_catalog.text, $2::pg_catalog.text, CASE WHEN pg_catalog.strpos($3, 'c') = 0 THEN $3 || 'i' ELSE $3 END ); +$$ LANGUAGE SQL IMMUTABLE STRICT ROWS 10; diff --git a/contrib/citext/citext--1.0.sql b/contrib/citext/citext--1.1.sql index 7db2e8d0ae..9ea7c64709 100644 --- a/contrib/citext/citext--1.0.sql +++ b/contrib/citext/citext--1.1.sql @@ -1,4 +1,4 @@ -/* contrib/citext/citext--1.0.sql */ +/* contrib/citext/citext--1.1.sql */ -- complain if script is sourced in psql, rather than via CREATE EXTENSION \echo Use "CREATE EXTENSION citext" to load this file. \quit @@ -440,13 +440,13 @@ CREATE OPERATOR !~~* ( -- XXX TODO Ideally these would be implemented in C. -- -CREATE FUNCTION regexp_matches( citext, citext ) RETURNS TEXT[] AS $$ +CREATE FUNCTION regexp_matches( citext, citext ) RETURNS SETOF TEXT[] AS $$ SELECT pg_catalog.regexp_matches( $1::pg_catalog.text, $2::pg_catalog.text, 'i' ); -$$ LANGUAGE SQL IMMUTABLE STRICT; +$$ LANGUAGE SQL IMMUTABLE STRICT ROWS 1; -CREATE FUNCTION regexp_matches( citext, citext, text ) RETURNS TEXT[] AS $$ +CREATE FUNCTION regexp_matches( citext, citext, text ) RETURNS SETOF TEXT[] AS $$ SELECT pg_catalog.regexp_matches( $1::pg_catalog.text, $2::pg_catalog.text, CASE WHEN pg_catalog.strpos($3, 'c') = 0 THEN $3 || 'i' ELSE $3 END ); -$$ LANGUAGE SQL IMMUTABLE STRICT; +$$ LANGUAGE SQL IMMUTABLE STRICT ROWS 10; CREATE FUNCTION regexp_replace( citext, citext, text ) returns TEXT AS $$ SELECT pg_catalog.regexp_replace( $1::pg_catalog.text, $2::pg_catalog.text, $3, 'i'); diff --git a/contrib/citext/citext.control b/contrib/citext/citext.control index 3eb01a3360..ef90a97bc9 100644 --- a/contrib/citext/citext.control +++ b/contrib/citext/citext.control @@ -1,5 +1,5 @@ # citext extension comment = 'data type for case-insensitive character strings' -default_version = '1.0' +default_version = '1.1' module_pathname = '$libdir/citext' relocatable = true diff --git a/contrib/citext/expected/citext.out b/contrib/citext/expected/citext.out index 411b689b4b..373fe6da54 100644 --- a/contrib/citext/expected/citext.out +++ b/contrib/citext/expected/citext.out @@ -1846,11 +1846,18 @@ SELECT regexp_matches('foobarbequebaz'::citext, '(BAR)(BEQUE)'::citext, ''::cite (1 row) -- c forces case-sensitive -SELECT regexp_matches('foobarbequebaz'::citext, '(BAR)(BEQUE)'::citext, 'c'::citext) = ARRAY[ 'bar', 'beque' ] AS "null"; - null ------- - -(1 row) +SELECT regexp_matches('foobarbequebaz'::citext, '(BAR)(BEQUE)'::citext, 'c'::citext) = ARRAY[ 'bar', 'beque' ] AS "no rows"; + no rows +--------- +(0 rows) + +-- g allows multiple output rows +SELECT regexp_matches('foobarbequebazmorebarbequetoo'::citext, '(BAR)(BEQUE)'::citext, 'g'::citext) AS "two rows"; + two rows +------------- + {bar,beque} + {bar,beque} +(2 rows) SELECT regexp_replace('Thomas'::citext, '.[mN]a.', 'M') = 'ThM' AS t; t diff --git a/contrib/citext/expected/citext_1.out b/contrib/citext/expected/citext_1.out index da3862f49b..fcadd8d392 100644 --- a/contrib/citext/expected/citext_1.out +++ b/contrib/citext/expected/citext_1.out @@ -1846,11 +1846,18 @@ SELECT regexp_matches('foobarbequebaz'::citext, '(BAR)(BEQUE)'::citext, ''::cite (1 row) -- c forces case-sensitive -SELECT regexp_matches('foobarbequebaz'::citext, '(BAR)(BEQUE)'::citext, 'c'::citext) = ARRAY[ 'bar', 'beque' ] AS "null"; - null ------- - -(1 row) +SELECT regexp_matches('foobarbequebaz'::citext, '(BAR)(BEQUE)'::citext, 'c'::citext) = ARRAY[ 'bar', 'beque' ] AS "no rows"; + no rows +--------- +(0 rows) + +-- g allows multiple output rows +SELECT regexp_matches('foobarbequebazmorebarbequetoo'::citext, '(BAR)(BEQUE)'::citext, 'g'::citext) AS "two rows"; + two rows +------------- + {bar,beque} + {bar,beque} +(2 rows) SELECT regexp_replace('Thomas'::citext, '.[mN]a.', 'M') = 'ThM' AS t; t diff --git a/contrib/citext/sql/citext.sql b/contrib/citext/sql/citext.sql index 27678fab5d..950895baea 100644 --- a/contrib/citext/sql/citext.sql +++ b/contrib/citext/sql/citext.sql @@ -601,7 +601,9 @@ SELECT regexp_matches('foobarbequebaz'::citext, '(BAR)(BEQUE)', '') = ARRAY[ 'ba SELECT regexp_matches('foobarbequebaz', '(BAR)(BEQUE)'::citext, '') = ARRAY[ 'bar', 'beque' ] AS t; SELECT regexp_matches('foobarbequebaz'::citext, '(BAR)(BEQUE)'::citext, ''::citext) = ARRAY[ 'bar', 'beque' ] AS t; -- c forces case-sensitive -SELECT regexp_matches('foobarbequebaz'::citext, '(BAR)(BEQUE)'::citext, 'c'::citext) = ARRAY[ 'bar', 'beque' ] AS "null"; +SELECT regexp_matches('foobarbequebaz'::citext, '(BAR)(BEQUE)'::citext, 'c'::citext) = ARRAY[ 'bar', 'beque' ] AS "no rows"; +-- g allows multiple output rows +SELECT regexp_matches('foobarbequebazmorebarbequetoo'::citext, '(BAR)(BEQUE)'::citext, 'g'::citext) AS "two rows"; SELECT regexp_replace('Thomas'::citext, '.[mN]a.', 'M') = 'ThM' AS t; SELECT regexp_replace('Thomas'::citext, '.[MN]A.', 'M') = 'ThM' AS t; diff --git a/contrib/cube/cube.c b/contrib/cube/cube.c index b9ccad994a..113c66383a 100644 --- a/contrib/cube/cube.c +++ b/contrib/cube/cube.c @@ -12,7 +12,7 @@ #include <math.h> #include "access/gist.h" -#include "access/skey.h" +#include "access/stratnum.h" #include "utils/array.h" #include "utils/builtins.h" diff --git a/contrib/earthdistance/Makefile b/contrib/earthdistance/Makefile index 93dcbe3a31..cde1ae630f 100644 --- a/contrib/earthdistance/Makefile +++ b/contrib/earthdistance/Makefile @@ -7,7 +7,7 @@ DATA = earthdistance--1.0.sql earthdistance--unpackaged--1.0.sql PGFILEDESC = "earthdistance - calculate distances on the surface of the Earth" REGRESS = earthdistance -REGRESS_OPTS = --extra-install=contrib/cube +EXTRA_INSTALL = contrib/cube LDFLAGS_SL += $(filter -lm, $(LIBS)) diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c index 4368897581..499f24ff28 100644 --- a/contrib/file_fdw/file_fdw.c +++ b/contrib/file_fdw/file_fdw.c @@ -34,6 +34,7 @@ #include "optimizer/var.h" #include "utils/memutils.h" #include "utils/rel.h" +#include "utils/sampling.h" PG_MODULE_MAGIC; @@ -561,7 +562,8 @@ fileGetForeignPlan(PlannerInfo *root, scan_clauses, scan_relid, NIL, /* no expressions to evaluate */ - best_path->fdw_private); + best_path->fdw_private, + NIL /* no custom tlist */ ); } /* @@ -1005,7 +1007,7 @@ file_acquire_sample_rows(Relation onerel, int elevel, { int numrows = 0; double rowstoskip = -1; /* -1 means not set yet */ - double rstate; + ReservoirStateData rstate; TupleDesc tupDesc; Datum *values; bool *nulls; @@ -1043,7 +1045,7 @@ file_acquire_sample_rows(Relation onerel, int elevel, ALLOCSET_DEFAULT_MAXSIZE); /* Prepare for sampling rows */ - rstate = anl_init_selection_state(targrows); + reservoir_init_selection_state(&rstate, targrows); /* Set up callback to identify error line number. */ errcallback.callback = CopyFromErrorCallback; @@ -1087,7 +1089,7 @@ file_acquire_sample_rows(Relation onerel, int elevel, * not-yet-incremented value of totalrows as t. */ if (rowstoskip < 0) - rowstoskip = anl_get_next_S(*totalrows, targrows, &rstate); + rowstoskip = reservoir_get_next_S(&rstate, *totalrows, targrows); if (rowstoskip <= 0) { @@ -1095,7 +1097,7 @@ file_acquire_sample_rows(Relation onerel, int elevel, * Found a suitable tuple, so save it, replacing one old tuple * at random */ - int k = (int) (targrows * anl_random_fract()); + int k = (int) (targrows * sampler_random_fract(rstate.randstate)); Assert(k >= 0 && k < targrows); heap_freetuple(rows[k]); diff --git a/contrib/fuzzystrmatch/dmetaphone.c b/contrib/fuzzystrmatch/dmetaphone.c index 7c8457e734..147c8501ee 100644 --- a/contrib/fuzzystrmatch/dmetaphone.c +++ b/contrib/fuzzystrmatch/dmetaphone.c @@ -195,7 +195,7 @@ dmetaphone_alt(PG_FUNCTION_ARGS) * in a case like this. */ -#define META_FREE(x) ((void)true) /* pfree((x)) */ +#define META_FREE(x) ((void)true) /* pfree((x)) */ #else /* not defined DMETAPHONE_MAIN */ /* use the standard malloc library when not running in PostgreSQL */ diff --git a/contrib/hstore/hstore_gin.c b/contrib/hstore/hstore_gin.c index 68f9061db1..919181d375 100644 --- a/contrib/hstore/hstore_gin.c +++ b/contrib/hstore/hstore_gin.c @@ -4,7 +4,7 @@ #include "postgres.h" #include "access/gin.h" -#include "access/skey.h" +#include "access/stratnum.h" #include "catalog/pg_type.h" #include "hstore.h" diff --git a/contrib/hstore/hstore_gist.c b/contrib/hstore/hstore_gist.c index 06f3c9359b..0fb769de7d 100644 --- a/contrib/hstore/hstore_gist.c +++ b/contrib/hstore/hstore_gist.c @@ -4,7 +4,7 @@ #include "postgres.h" #include "access/gist.h" -#include "access/skey.h" +#include "access/stratnum.h" #include "catalog/pg_type.h" #include "utils/pg_crc.h" @@ -72,7 +72,7 @@ typedef struct static pg_crc32 crc32_sz(char *buf, int size) { - pg_crc32 crc; + pg_crc32 crc; INIT_TRADITIONAL_CRC32(crc); COMP_TRADITIONAL_CRC32(crc, buf, size); diff --git a/contrib/hstore_plperl/.gitignore b/contrib/hstore_plperl/.gitignore new file mode 100644 index 0000000000..5dcb3ff972 --- /dev/null +++ b/contrib/hstore_plperl/.gitignore @@ -0,0 +1,4 @@ +# Generated subdirectories +/log/ +/results/ +/tmp_check/ diff --git a/contrib/hstore_plperl/Makefile b/contrib/hstore_plperl/Makefile new file mode 100644 index 0000000000..19a8ab4493 --- /dev/null +++ b/contrib/hstore_plperl/Makefile @@ -0,0 +1,37 @@ +# contrib/hstore_plperl/Makefile + +MODULE_big = hstore_plperl +OBJS = hstore_plperl.o $(WIN32RES) +PGFILEDESC = "hstore_plperl - hstore transform for plperl" + +PG_CPPFLAGS = -I$(top_srcdir)/src/pl/plperl -I$(top_srcdir)/contrib/hstore + +EXTENSION = hstore_plperl hstore_plperlu +DATA = hstore_plperl--1.0.sql hstore_plperlu--1.0.sql + +REGRESS = hstore_plperl hstore_plperlu create_transform +EXTRA_INSTALL = contrib/hstore + +ifdef USE_PGXS +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = contrib/hstore_plperl +top_builddir = ../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif + +ifeq ($(PORTNAME), win32) +# these settings are the same as for plperl +override CPPFLAGS += -DPLPERL_HAVE_UID_GID -Wno-comment +# This means we need an in-tree build on Windows, not a pgxs build +SHLIB_LINK += ../hstore/libhstore.a $(wildcard ../../src/pl/plperl/libperl*.a) +endif + +# As with plperl we need to make sure that the CORE directory is included +# last, probably because it sometimes contains some header files with names +# that clash with some of ours, or with some that we include, notably on +# Windows. +override CPPFLAGS := $(CPPFLAGS) -I$(perl_archlibexp)/CORE diff --git a/contrib/hstore_plperl/expected/create_transform.out b/contrib/hstore_plperl/expected/create_transform.out new file mode 100644 index 0000000000..c588d33ab8 --- /dev/null +++ b/contrib/hstore_plperl/expected/create_transform.out @@ -0,0 +1,75 @@ +-- general regression test for transforms +DROP EXTENSION IF EXISTS hstore CASCADE; +NOTICE: extension "hstore" does not exist, skipping +DROP EXTENSION IF EXISTS plperl CASCADE; +NOTICE: extension "plperl" does not exist, skipping +DROP EXTENSION IF EXISTS hstore_plperl CASCADE; +NOTICE: extension "hstore_plperl" does not exist, skipping +CREATE EXTENSION hstore; +CREATE EXTENSION plperl; +CREATE FUNCTION hstore_to_plperl(val internal) RETURNS internal +LANGUAGE C STRICT IMMUTABLE +AS '$libdir/hstore_plperl'; +CREATE FUNCTION plperl_to_hstore(val internal) RETURNS hstore +LANGUAGE C STRICT IMMUTABLE +AS '$libdir/hstore_plperl'; +CREATE TRANSFORM FOR foo LANGUAGE plperl (FROM SQL WITH FUNCTION hstore_to_plperl(internal), TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- fail +ERROR: type "foo" does not exist +CREATE TRANSFORM FOR hstore LANGUAGE foo (FROM SQL WITH FUNCTION hstore_to_plperl(internal), TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- fail +ERROR: language "foo" does not exist +CREATE TRANSFORM FOR hstore LANGUAGE plperl (FROM SQL WITH FUNCTION hstore_out(hstore), TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- fail +ERROR: return data type of FROM SQL function must be "internal" +CREATE TRANSFORM FOR hstore LANGUAGE plperl (FROM SQL WITH FUNCTION internal_in(cstring), TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- fail +ERROR: first argument of transform function must be type "internal" +CREATE TRANSFORM FOR hstore LANGUAGE plperl (FROM SQL WITH FUNCTION hstore_to_plperl(internal), TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- ok +CREATE TRANSFORM FOR hstore LANGUAGE plperl (FROM SQL WITH FUNCTION hstore_to_plperl(internal), TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- fail +ERROR: transform for type hstore language "plperl" already exists +CREATE OR REPLACE TRANSFORM FOR hstore LANGUAGE plperl (FROM SQL WITH FUNCTION hstore_to_plperl(internal), TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- ok +CREATE OR REPLACE TRANSFORM FOR hstore LANGUAGE plperl (FROM SQL WITH FUNCTION hstore_to_plperl(internal)); -- ok +CREATE OR REPLACE TRANSFORM FOR hstore LANGUAGE plperl (TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- ok +COMMENT ON TRANSFORM FOR hstore LANGUAGE plperl IS 'test'; +DROP TRANSFORM IF EXISTS FOR fake_type LANGUAGE plperl; +NOTICE: type "fake_type" does not exist, skipping +DROP TRANSFORM IF EXISTS FOR hstore LANGUAGE fake_lang; +NOTICE: transform for type hstore language "fake_lang" does not exist, skipping +DROP TRANSFORM FOR foo LANGUAGE plperl; +ERROR: type "foo" does not exist +DROP TRANSFORM FOR hstore LANGUAGE foo; +ERROR: language "foo" does not exist +DROP TRANSFORM FOR hstore LANGUAGE plperl; +DROP TRANSFORM IF EXISTS FOR hstore LANGUAGE plperl; +NOTICE: transform for type hstore language "plperl" does not exist, skipping +DROP FUNCTION hstore_to_plperl(val internal); +DROP FUNCTION plperl_to_hstore(val internal); +CREATE EXTENSION hstore_plperl; +\dx+ hstore_plperl + Objects in extension "hstore_plperl" + Object Description +-------------------------------------- + function hstore_to_plperl(internal) + function plperl_to_hstore(internal) + transform for hstore language plperl +(3 rows) + +ALTER EXTENSION hstore_plperl DROP TRANSFORM FOR hstore LANGUAGE plperl; +\dx+ hstore_plperl +Objects in extension "hstore_plperl" + Object Description +------------------------------------- + function hstore_to_plperl(internal) + function plperl_to_hstore(internal) +(2 rows) + +ALTER EXTENSION hstore_plperl ADD TRANSFORM FOR hstore LANGUAGE plperl; +\dx+ hstore_plperl + Objects in extension "hstore_plperl" + Object Description +-------------------------------------- + function hstore_to_plperl(internal) + function plperl_to_hstore(internal) + transform for hstore language plperl +(3 rows) + +DROP EXTENSION hstore CASCADE; +NOTICE: drop cascades to extension hstore_plperl +DROP EXTENSION plperl CASCADE; diff --git a/contrib/hstore_plperl/expected/hstore_plperl.out b/contrib/hstore_plperl/expected/hstore_plperl.out new file mode 100644 index 0000000000..cf384eba64 --- /dev/null +++ b/contrib/hstore_plperl/expected/hstore_plperl.out @@ -0,0 +1,48 @@ +CREATE EXTENSION hstore; +CREATE EXTENSION plperl; +CREATE EXTENSION hstore_plperl; +SELECT transforms.udt_schema, transforms.udt_name, + routine_schema, routine_name, + group_name, transform_type +FROM information_schema.transforms JOIN information_schema.routines + USING (specific_catalog, specific_schema, specific_name) +ORDER BY 1, 2, 5, 6; + udt_schema | udt_name | routine_schema | routine_name | group_name | transform_type +------------+----------+----------------+------------------+------------+---------------- + public | hstore | public | hstore_to_plperl | plperl | FROM SQL + public | hstore | public | plperl_to_hstore | plperl | TO SQL +(2 rows) + +-- test perl -> hstore +CREATE FUNCTION test2() RETURNS hstore +LANGUAGE plperl +TRANSFORM FOR TYPE hstore +AS $$ +$val = {a => 1, b => 'boo', c => undef}; +return $val; +$$; +SELECT test2(); + test2 +--------------------------------- + "a"=>"1", "b"=>"boo", "c"=>NULL +(1 row) + +-- test perl -> hstore[] +CREATE FUNCTION test2arr() RETURNS hstore[] +LANGUAGE plperl +TRANSFORM FOR TYPE hstore +AS $$ +$val = [{a => 1, b => 'boo', c => undef}, {d => 2}]; +return $val; +$$; +SELECT test2arr(); + test2arr +-------------------------------------------------------------- + {"\"a\"=>\"1\", \"b\"=>\"boo\", \"c\"=>NULL","\"d\"=>\"2\""} +(1 row) + +DROP FUNCTION test2(); +DROP FUNCTION test2arr(); +DROP EXTENSION hstore_plperl; +DROP EXTENSION hstore; +DROP EXTENSION plperl; diff --git a/contrib/hstore_plperl/expected/hstore_plperlu.out b/contrib/hstore_plperl/expected/hstore_plperlu.out new file mode 100644 index 0000000000..8c689ad3ad --- /dev/null +++ b/contrib/hstore_plperl/expected/hstore_plperlu.out @@ -0,0 +1,180 @@ +CREATE EXTENSION hstore; +CREATE EXTENSION plperlu; +CREATE EXTENSION hstore_plperlu; +SELECT transforms.udt_schema, transforms.udt_name, + routine_schema, routine_name, + group_name, transform_type +FROM information_schema.transforms JOIN information_schema.routines + USING (specific_catalog, specific_schema, specific_name) +ORDER BY 1, 2, 5, 6; + udt_schema | udt_name | routine_schema | routine_name | group_name | transform_type +------------+----------+----------------+-------------------+------------+---------------- + public | hstore | public | hstore_to_plperlu | plperlu | FROM SQL + public | hstore | public | plperlu_to_hstore | plperlu | TO SQL +(2 rows) + +-- test hstore -> perl +CREATE FUNCTION test1(val hstore) RETURNS int +LANGUAGE plperlu +TRANSFORM FOR TYPE hstore +AS $$ +use Data::Dumper; +$Data::Dumper::Sortkeys = 1; +elog(INFO, Dumper($_[0])); +return scalar(keys %{$_[0]}); +$$; +SELECT test1('aa=>bb, cc=>NULL'::hstore); +INFO: $VAR1 = { + 'aa' => 'bb', + 'cc' => undef + }; + +CONTEXT: PL/Perl function "test1" + test1 +------- + 2 +(1 row) + +CREATE FUNCTION test1none(val hstore) RETURNS int +LANGUAGE plperlu +AS $$ +use Data::Dumper; +$Data::Dumper::Sortkeys = 1; +elog(INFO, Dumper($_[0])); +return scalar(keys %{$_[0]}); +$$; +SELECT test1none('aa=>bb, cc=>NULL'::hstore); +INFO: $VAR1 = '"aa"=>"bb", "cc"=>NULL'; + +CONTEXT: PL/Perl function "test1none" + test1none +----------- + 0 +(1 row) + +CREATE FUNCTION test1list(val hstore) RETURNS int +LANGUAGE plperlu +TRANSFORM FOR TYPE hstore +AS $$ +use Data::Dumper; +$Data::Dumper::Sortkeys = 1; +elog(INFO, Dumper($_[0])); +return scalar(keys %{$_[0]}); +$$; +SELECT test1list('aa=>bb, cc=>NULL'::hstore); +INFO: $VAR1 = { + 'aa' => 'bb', + 'cc' => undef + }; + +CONTEXT: PL/Perl function "test1list" + test1list +----------- + 2 +(1 row) + +-- test hstore[] -> perl +CREATE FUNCTION test1arr(val hstore[]) RETURNS int +LANGUAGE plperlu +TRANSFORM FOR TYPE hstore +AS $$ +use Data::Dumper; +$Data::Dumper::Sortkeys = 1; +elog(INFO, Dumper($_[0]->[0], $_[0]->[1])); +return scalar(keys %{$_[0]}); +$$; +SELECT test1arr(array['aa=>bb, cc=>NULL'::hstore, 'dd=>ee']); +INFO: $VAR1 = { + 'aa' => 'bb', + 'cc' => undef + }; +$VAR2 = { + 'dd' => 'ee' + }; + +CONTEXT: PL/Perl function "test1arr" + test1arr +---------- + 2 +(1 row) + +-- test as part of prepare/execute +CREATE FUNCTION test3() RETURNS void +LANGUAGE plperlu +TRANSFORM FOR TYPE hstore +AS $$ +use Data::Dumper; +$Data::Dumper::Sortkeys = 1; + +$rv = spi_exec_query(q{SELECT 'aa=>bb, cc=>NULL'::hstore AS col1}); +elog(INFO, Dumper($rv->{rows}[0]->{col1})); + +$val = {a => 1, b => 'boo', c => undef}; +$plan = spi_prepare(q{SELECT $1::text AS col1}, "hstore"); +$rv = spi_exec_prepared($plan, {}, $val); +elog(INFO, Dumper($rv->{rows}[0]->{col1})); +$$; +SELECT test3(); +INFO: $VAR1 = { + 'aa' => 'bb', + 'cc' => undef + }; + +CONTEXT: PL/Perl function "test3" +INFO: $VAR1 = '"a"=>"1", "b"=>"boo", "c"=>NULL'; + +CONTEXT: PL/Perl function "test3" + test3 +------- + +(1 row) + +-- test trigger +CREATE TABLE test1 (a int, b hstore); +INSERT INTO test1 VALUES (1, 'aa=>bb, cc=>NULL'); +SELECT * FROM test1; + a | b +---+------------------------ + 1 | "aa"=>"bb", "cc"=>NULL +(1 row) + +CREATE FUNCTION test4() RETURNS trigger +LANGUAGE plperlu +TRANSFORM FOR TYPE hstore +AS $$ +use Data::Dumper; +$Data::Dumper::Sortkeys = 1; +elog(INFO, Dumper($_TD->{new})); +if ($_TD->{new}{a} == 1) { + $_TD->{new}{b} = {a => 1, b => 'boo', c => undef}; +} + +return "MODIFY"; +$$; +CREATE TRIGGER test4 BEFORE UPDATE ON test1 FOR EACH ROW EXECUTE PROCEDURE test4(); +UPDATE test1 SET a = a; +INFO: $VAR1 = { + 'a' => '1', + 'b' => { + 'aa' => 'bb', + 'cc' => undef + } + }; + +CONTEXT: PL/Perl function "test4" +SELECT * FROM test1; + a | b +---+--------------------------------- + 1 | "a"=>"1", "b"=>"boo", "c"=>NULL +(1 row) + +DROP TABLE test1; +DROP FUNCTION test1(hstore); +DROP FUNCTION test1none(hstore); +DROP FUNCTION test1list(hstore); +DROP FUNCTION test1arr(hstore[]); +DROP FUNCTION test3(); +DROP FUNCTION test4(); +DROP EXTENSION hstore_plperlu; +DROP EXTENSION hstore; +DROP EXTENSION plperlu; diff --git a/contrib/hstore_plperl/hstore_plperl--1.0.sql b/contrib/hstore_plperl/hstore_plperl--1.0.sql new file mode 100644 index 0000000000..ea0ad7688d --- /dev/null +++ b/contrib/hstore_plperl/hstore_plperl--1.0.sql @@ -0,0 +1,17 @@ +-- make sure the prerequisite libraries are loaded +DO '' LANGUAGE plperl; +SELECT NULL::hstore; + + +CREATE FUNCTION hstore_to_plperl(val internal) RETURNS internal +LANGUAGE C STRICT IMMUTABLE +AS 'MODULE_PATHNAME'; + +CREATE FUNCTION plperl_to_hstore(val internal) RETURNS hstore +LANGUAGE C STRICT IMMUTABLE +AS 'MODULE_PATHNAME'; + +CREATE TRANSFORM FOR hstore LANGUAGE plperl ( + FROM SQL WITH FUNCTION hstore_to_plperl(internal), + TO SQL WITH FUNCTION plperl_to_hstore(internal) +); diff --git a/contrib/hstore_plperl/hstore_plperl.c b/contrib/hstore_plperl/hstore_plperl.c new file mode 100644 index 0000000000..fbbb4c8e76 --- /dev/null +++ b/contrib/hstore_plperl/hstore_plperl.c @@ -0,0 +1,88 @@ +#include "postgres.h" +#undef _ +#include "fmgr.h" +#include "plperl.h" +#include "plperl_helpers.h" +#include "hstore.h" + +PG_MODULE_MAGIC; + + +PG_FUNCTION_INFO_V1(hstore_to_plperl); + +Datum +hstore_to_plperl(PG_FUNCTION_ARGS) +{ + HStore *in = PG_GETARG_HS(0); + int i; + int count = HS_COUNT(in); + char *base = STRPTR(in); + HEntry *entries = ARRPTR(in); + HV *hv; + + hv = newHV(); + + for (i = 0; i < count; i++) + { + const char *key; + SV *value; + + key = pnstrdup(HS_KEY(entries, base, i), HS_KEYLEN(entries, i)); + value = HS_VALISNULL(entries, i) ? newSV(0) : cstr2sv(pnstrdup(HS_VAL(entries, base, i), HS_VALLEN(entries, i))); + + (void) hv_store(hv, key, strlen(key), value, 0); + } + + return PointerGetDatum(newRV((SV *) hv)); +} + + +PG_FUNCTION_INFO_V1(plperl_to_hstore); + +Datum +plperl_to_hstore(PG_FUNCTION_ARGS) +{ + HV *hv; + HE *he; + int32 buflen; + int32 i; + int32 pcount; + HStore *out; + Pairs *pairs; + + hv = (HV *) SvRV((SV *) PG_GETARG_POINTER(0)); + + pcount = hv_iterinit(hv); + + pairs = palloc(pcount * sizeof(Pairs)); + + i = 0; + while ((he = hv_iternext(hv))) + { + char *key = sv2cstr(HeSVKEY_force(he)); + SV *value = HeVAL(he); + + pairs[i].key = pstrdup(key); + pairs[i].keylen = hstoreCheckKeyLen(strlen(pairs[i].key)); + pairs[i].needfree = true; + + if (!SvOK(value)) + { + pairs[i].val = NULL; + pairs[i].vallen = 0; + pairs[i].isnull = true; + } + else + { + pairs[i].val = pstrdup(sv2cstr(value)); + pairs[i].vallen = hstoreCheckValLen(strlen(pairs[i].val)); + pairs[i].isnull = false; + } + + i++; + } + + pcount = hstoreUniquePairs(pairs, pcount, &buflen); + out = hstorePairs(pairs, pcount, buflen); + PG_RETURN_POINTER(out); +} diff --git a/contrib/hstore_plperl/hstore_plperl.control b/contrib/hstore_plperl/hstore_plperl.control new file mode 100644 index 0000000000..16277f68c1 --- /dev/null +++ b/contrib/hstore_plperl/hstore_plperl.control @@ -0,0 +1,6 @@ +# hstore_plperl extension +comment = 'transform between hstore and plperl' +default_version = '1.0' +module_pathname = '$libdir/hstore_plperl' +relocatable = true +requires = 'hstore,plperl' diff --git a/contrib/hstore_plperl/hstore_plperlu--1.0.sql b/contrib/hstore_plperl/hstore_plperlu--1.0.sql new file mode 100644 index 0000000000..46ad35c487 --- /dev/null +++ b/contrib/hstore_plperl/hstore_plperlu--1.0.sql @@ -0,0 +1,17 @@ +-- make sure the prerequisite libraries are loaded +DO '' LANGUAGE plperlu; +SELECT NULL::hstore; + + +CREATE FUNCTION hstore_to_plperlu(val internal) RETURNS internal +LANGUAGE C STRICT IMMUTABLE +AS 'MODULE_PATHNAME', 'hstore_to_plperl'; + +CREATE FUNCTION plperlu_to_hstore(val internal) RETURNS hstore +LANGUAGE C STRICT IMMUTABLE +AS 'MODULE_PATHNAME', 'plperl_to_hstore'; + +CREATE TRANSFORM FOR hstore LANGUAGE plperlu ( + FROM SQL WITH FUNCTION hstore_to_plperlu(internal), + TO SQL WITH FUNCTION plperlu_to_hstore(internal) +); diff --git a/contrib/hstore_plperl/hstore_plperlu.control b/contrib/hstore_plperl/hstore_plperlu.control new file mode 100644 index 0000000000..c8d43b41a5 --- /dev/null +++ b/contrib/hstore_plperl/hstore_plperlu.control @@ -0,0 +1,6 @@ +# hstore_plperlu extension +comment = 'transform between hstore and plperlu' +default_version = '1.0' +module_pathname = '$libdir/hstore_plperl' +relocatable = true +requires = 'hstore,plperlu' diff --git a/contrib/hstore_plperl/sql/create_transform.sql b/contrib/hstore_plperl/sql/create_transform.sql new file mode 100644 index 0000000000..d0a12ada9f --- /dev/null +++ b/contrib/hstore_plperl/sql/create_transform.sql @@ -0,0 +1,49 @@ +-- general regression test for transforms + +DROP EXTENSION IF EXISTS hstore CASCADE; +DROP EXTENSION IF EXISTS plperl CASCADE; +DROP EXTENSION IF EXISTS hstore_plperl CASCADE; + +CREATE EXTENSION hstore; +CREATE EXTENSION plperl; + +CREATE FUNCTION hstore_to_plperl(val internal) RETURNS internal +LANGUAGE C STRICT IMMUTABLE +AS '$libdir/hstore_plperl'; + +CREATE FUNCTION plperl_to_hstore(val internal) RETURNS hstore +LANGUAGE C STRICT IMMUTABLE +AS '$libdir/hstore_plperl'; + +CREATE TRANSFORM FOR foo LANGUAGE plperl (FROM SQL WITH FUNCTION hstore_to_plperl(internal), TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- fail +CREATE TRANSFORM FOR hstore LANGUAGE foo (FROM SQL WITH FUNCTION hstore_to_plperl(internal), TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- fail +CREATE TRANSFORM FOR hstore LANGUAGE plperl (FROM SQL WITH FUNCTION hstore_out(hstore), TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- fail +CREATE TRANSFORM FOR hstore LANGUAGE plperl (FROM SQL WITH FUNCTION internal_in(cstring), TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- fail + +CREATE TRANSFORM FOR hstore LANGUAGE plperl (FROM SQL WITH FUNCTION hstore_to_plperl(internal), TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- ok +CREATE TRANSFORM FOR hstore LANGUAGE plperl (FROM SQL WITH FUNCTION hstore_to_plperl(internal), TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- fail +CREATE OR REPLACE TRANSFORM FOR hstore LANGUAGE plperl (FROM SQL WITH FUNCTION hstore_to_plperl(internal), TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- ok +CREATE OR REPLACE TRANSFORM FOR hstore LANGUAGE plperl (FROM SQL WITH FUNCTION hstore_to_plperl(internal)); -- ok +CREATE OR REPLACE TRANSFORM FOR hstore LANGUAGE plperl (TO SQL WITH FUNCTION plperl_to_hstore(internal)); -- ok + +COMMENT ON TRANSFORM FOR hstore LANGUAGE plperl IS 'test'; + +DROP TRANSFORM IF EXISTS FOR fake_type LANGUAGE plperl; +DROP TRANSFORM IF EXISTS FOR hstore LANGUAGE fake_lang; +DROP TRANSFORM FOR foo LANGUAGE plperl; +DROP TRANSFORM FOR hstore LANGUAGE foo; +DROP TRANSFORM FOR hstore LANGUAGE plperl; +DROP TRANSFORM IF EXISTS FOR hstore LANGUAGE plperl; + +DROP FUNCTION hstore_to_plperl(val internal); +DROP FUNCTION plperl_to_hstore(val internal); + +CREATE EXTENSION hstore_plperl; +\dx+ hstore_plperl +ALTER EXTENSION hstore_plperl DROP TRANSFORM FOR hstore LANGUAGE plperl; +\dx+ hstore_plperl +ALTER EXTENSION hstore_plperl ADD TRANSFORM FOR hstore LANGUAGE plperl; +\dx+ hstore_plperl + +DROP EXTENSION hstore CASCADE; +DROP EXTENSION plperl CASCADE; diff --git a/contrib/hstore_plperl/sql/hstore_plperl.sql b/contrib/hstore_plperl/sql/hstore_plperl.sql new file mode 100644 index 0000000000..0f70f149f5 --- /dev/null +++ b/contrib/hstore_plperl/sql/hstore_plperl.sql @@ -0,0 +1,43 @@ +CREATE EXTENSION hstore; +CREATE EXTENSION plperl; +CREATE EXTENSION hstore_plperl; + +SELECT transforms.udt_schema, transforms.udt_name, + routine_schema, routine_name, + group_name, transform_type +FROM information_schema.transforms JOIN information_schema.routines + USING (specific_catalog, specific_schema, specific_name) +ORDER BY 1, 2, 5, 6; + + +-- test perl -> hstore +CREATE FUNCTION test2() RETURNS hstore +LANGUAGE plperl +TRANSFORM FOR TYPE hstore +AS $$ +$val = {a => 1, b => 'boo', c => undef}; +return $val; +$$; + +SELECT test2(); + + +-- test perl -> hstore[] +CREATE FUNCTION test2arr() RETURNS hstore[] +LANGUAGE plperl +TRANSFORM FOR TYPE hstore +AS $$ +$val = [{a => 1, b => 'boo', c => undef}, {d => 2}]; +return $val; +$$; + +SELECT test2arr(); + + +DROP FUNCTION test2(); +DROP FUNCTION test2arr(); + + +DROP EXTENSION hstore_plperl; +DROP EXTENSION hstore; +DROP EXTENSION plperl; diff --git a/contrib/hstore_plperl/sql/hstore_plperlu.sql b/contrib/hstore_plperl/sql/hstore_plperlu.sql new file mode 100644 index 0000000000..3cfb2fdd77 --- /dev/null +++ b/contrib/hstore_plperl/sql/hstore_plperlu.sql @@ -0,0 +1,121 @@ +CREATE EXTENSION hstore; +CREATE EXTENSION plperlu; +CREATE EXTENSION hstore_plperlu; + +SELECT transforms.udt_schema, transforms.udt_name, + routine_schema, routine_name, + group_name, transform_type +FROM information_schema.transforms JOIN information_schema.routines + USING (specific_catalog, specific_schema, specific_name) +ORDER BY 1, 2, 5, 6; + + +-- test hstore -> perl +CREATE FUNCTION test1(val hstore) RETURNS int +LANGUAGE plperlu +TRANSFORM FOR TYPE hstore +AS $$ +use Data::Dumper; +$Data::Dumper::Sortkeys = 1; +elog(INFO, Dumper($_[0])); +return scalar(keys %{$_[0]}); +$$; + +SELECT test1('aa=>bb, cc=>NULL'::hstore); + +CREATE FUNCTION test1none(val hstore) RETURNS int +LANGUAGE plperlu +AS $$ +use Data::Dumper; +$Data::Dumper::Sortkeys = 1; +elog(INFO, Dumper($_[0])); +return scalar(keys %{$_[0]}); +$$; + +SELECT test1none('aa=>bb, cc=>NULL'::hstore); + +CREATE FUNCTION test1list(val hstore) RETURNS int +LANGUAGE plperlu +TRANSFORM FOR TYPE hstore +AS $$ +use Data::Dumper; +$Data::Dumper::Sortkeys = 1; +elog(INFO, Dumper($_[0])); +return scalar(keys %{$_[0]}); +$$; + +SELECT test1list('aa=>bb, cc=>NULL'::hstore); + + +-- test hstore[] -> perl +CREATE FUNCTION test1arr(val hstore[]) RETURNS int +LANGUAGE plperlu +TRANSFORM FOR TYPE hstore +AS $$ +use Data::Dumper; +$Data::Dumper::Sortkeys = 1; +elog(INFO, Dumper($_[0]->[0], $_[0]->[1])); +return scalar(keys %{$_[0]}); +$$; + +SELECT test1arr(array['aa=>bb, cc=>NULL'::hstore, 'dd=>ee']); + + +-- test as part of prepare/execute +CREATE FUNCTION test3() RETURNS void +LANGUAGE plperlu +TRANSFORM FOR TYPE hstore +AS $$ +use Data::Dumper; +$Data::Dumper::Sortkeys = 1; + +$rv = spi_exec_query(q{SELECT 'aa=>bb, cc=>NULL'::hstore AS col1}); +elog(INFO, Dumper($rv->{rows}[0]->{col1})); + +$val = {a => 1, b => 'boo', c => undef}; +$plan = spi_prepare(q{SELECT $1::text AS col1}, "hstore"); +$rv = spi_exec_prepared($plan, {}, $val); +elog(INFO, Dumper($rv->{rows}[0]->{col1})); +$$; + +SELECT test3(); + + +-- test trigger +CREATE TABLE test1 (a int, b hstore); +INSERT INTO test1 VALUES (1, 'aa=>bb, cc=>NULL'); +SELECT * FROM test1; + +CREATE FUNCTION test4() RETURNS trigger +LANGUAGE plperlu +TRANSFORM FOR TYPE hstore +AS $$ +use Data::Dumper; +$Data::Dumper::Sortkeys = 1; +elog(INFO, Dumper($_TD->{new})); +if ($_TD->{new}{a} == 1) { + $_TD->{new}{b} = {a => 1, b => 'boo', c => undef}; +} + +return "MODIFY"; +$$; + +CREATE TRIGGER test4 BEFORE UPDATE ON test1 FOR EACH ROW EXECUTE PROCEDURE test4(); + +UPDATE test1 SET a = a; +SELECT * FROM test1; + + +DROP TABLE test1; + +DROP FUNCTION test1(hstore); +DROP FUNCTION test1none(hstore); +DROP FUNCTION test1list(hstore); +DROP FUNCTION test1arr(hstore[]); +DROP FUNCTION test3(); +DROP FUNCTION test4(); + + +DROP EXTENSION hstore_plperlu; +DROP EXTENSION hstore; +DROP EXTENSION plperlu; diff --git a/contrib/hstore_plpython/.gitignore b/contrib/hstore_plpython/.gitignore new file mode 100644 index 0000000000..ce6fab94a0 --- /dev/null +++ b/contrib/hstore_plpython/.gitignore @@ -0,0 +1,6 @@ +# Generated subdirectories +/expected/python3/ +/log/ +/results/ +/sql/python3/ +/tmp_check/ diff --git a/contrib/hstore_plpython/Makefile b/contrib/hstore_plpython/Makefile new file mode 100644 index 0000000000..6ee434bafa --- /dev/null +++ b/contrib/hstore_plpython/Makefile @@ -0,0 +1,37 @@ +# contrib/hstore_plpython/Makefile + +MODULE_big = hstore_plpython$(python_majorversion) +OBJS = hstore_plpython.o $(WIN32RES) +PGFILEDESC = "hstore_plpython - hstore transform for plpython" + +PG_CPPFLAGS = -I$(top_srcdir)/src/pl/plpython $(python_includespec) -I$(top_srcdir)/contrib/hstore + +EXTENSION = hstore_plpythonu hstore_plpython2u hstore_plpython3u +DATA = hstore_plpythonu--1.0.sql hstore_plpython2u--1.0.sql hstore_plpython3u--1.0.sql + +REGRESS = hstore_plpython +REGRESS_PLPYTHON3_MANGLE := $(REGRESS) + +ifdef USE_PGXS +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = contrib/hstore_plpython +top_builddir = ../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif + +ifeq ($(PORTNAME), win32) +# This means we need an in-tree build on Windows, not a pgxs build +SHLIB_LINK += ../hstore/libhstore.a $(wildcard ../../src/pl/plpython/libpython*.a) $(wildcard ../../src/pl/plpython/libplpython*.a) +endif + +REGRESS_OPTS += --load-extension=hstore +ifeq ($(python_majorversion),2) +REGRESS_OPTS += --load-extension=plpythonu --load-extension=hstore_plpythonu +endif +EXTRA_INSTALL += contrib/hstore + +include $(top_srcdir)/src/pl/plpython/regress-python3-mangle.mk diff --git a/contrib/hstore_plpython/expected/hstore_plpython.out b/contrib/hstore_plpython/expected/hstore_plpython.out new file mode 100644 index 0000000000..b7a6a92ac6 --- /dev/null +++ b/contrib/hstore_plpython/expected/hstore_plpython.out @@ -0,0 +1,128 @@ +CREATE EXTENSION plpython2u; +CREATE EXTENSION hstore_plpython2u; +-- test hstore -> python +CREATE FUNCTION test1(val hstore) RETURNS int +LANGUAGE plpythonu +TRANSFORM FOR TYPE hstore +AS $$ +assert isinstance(val, dict) +i = list(val.items()) +i.sort() +plpy.info(i) +return len(val) +$$; +SELECT test1('aa=>bb, cc=>NULL'::hstore); +INFO: [('aa', 'bb'), ('cc', None)] +CONTEXT: PL/Python function "test1" + test1 +------- + 2 +(1 row) + +-- the same with the versioned language name +CREATE FUNCTION test1n(val hstore) RETURNS int +LANGUAGE plpython2u +TRANSFORM FOR TYPE hstore +AS $$ +assert isinstance(val, dict) +i = list(val.items()) +i.sort() +plpy.info(i) +return len(val) +$$; +SELECT test1n('aa=>bb, cc=>NULL'::hstore); +INFO: [('aa', 'bb'), ('cc', None)] +CONTEXT: PL/Python function "test1n" + test1n +-------- + 2 +(1 row) + +-- test hstore[] -> python +CREATE FUNCTION test1arr(val hstore[]) RETURNS int +LANGUAGE plpythonu +TRANSFORM FOR TYPE hstore +AS $$ +assert(val == [{'aa': 'bb', 'cc': None}, {'dd': 'ee'}]) +return len(val) +$$; +SELECT test1arr(array['aa=>bb, cc=>NULL'::hstore, 'dd=>ee']); + test1arr +---------- + 2 +(1 row) + +-- test python -> hstore +CREATE FUNCTION test2() RETURNS hstore +LANGUAGE plpythonu +TRANSFORM FOR TYPE hstore +AS $$ +val = {'a': 1, 'b': 'boo', 'c': None} +return val +$$; +SELECT test2(); + test2 +--------------------------------- + "a"=>"1", "b"=>"boo", "c"=>NULL +(1 row) + +-- test python -> hstore[] +CREATE FUNCTION test2arr() RETURNS hstore[] +LANGUAGE plpythonu +TRANSFORM FOR TYPE hstore +AS $$ +val = [{'a': 1, 'b': 'boo', 'c': None}, {'d': 2}] +return val +$$; + SELECT test2arr(); + test2arr +-------------------------------------------------------------- + {"\"a\"=>\"1\", \"b\"=>\"boo\", \"c\"=>NULL","\"d\"=>\"2\""} +(1 row) + +-- test as part of prepare/execute +CREATE FUNCTION test3() RETURNS void +LANGUAGE plpythonu +TRANSFORM FOR TYPE hstore +AS $$ +rv = plpy.execute("SELECT 'aa=>bb, cc=>NULL'::hstore AS col1") +assert(rv[0]["col1"] == {'aa': 'bb', 'cc': None}) + +val = {'a': 1, 'b': 'boo', 'c': None} +plan = plpy.prepare("SELECT $1::text AS col1", ["hstore"]) +rv = plpy.execute(plan, [val]) +assert(rv[0]["col1"] == '"a"=>"1", "b"=>"boo", "c"=>NULL') +$$; +SELECT test3(); + test3 +------- + +(1 row) + +-- test trigger +CREATE TABLE test1 (a int, b hstore); +INSERT INTO test1 VALUES (1, 'aa=>bb, cc=>NULL'); +SELECT * FROM test1; + a | b +---+------------------------ + 1 | "aa"=>"bb", "cc"=>NULL +(1 row) + +CREATE FUNCTION test4() RETURNS trigger +LANGUAGE plpythonu +TRANSFORM FOR TYPE hstore +AS $$ +assert(TD["new"] == {'a': 1, 'b': {'aa': 'bb', 'cc': None}}) +if TD["new"]["a"] == 1: + TD["new"]["b"] = {'a': 1, 'b': 'boo', 'c': None} + +return "MODIFY" +$$; +CREATE TRIGGER test4 BEFORE UPDATE ON test1 FOR EACH ROW EXECUTE PROCEDURE test4(); +UPDATE test1 SET a = a; +SELECT * FROM test1; + a | b +---+--------------------------------- + 1 | "a"=>"1", "b"=>"boo", "c"=>NULL +(1 row) + diff --git a/contrib/hstore_plpython/hstore_plpython.c b/contrib/hstore_plpython/hstore_plpython.c new file mode 100644 index 0000000000..a3316dd9eb --- /dev/null +++ b/contrib/hstore_plpython/hstore_plpython.c @@ -0,0 +1,114 @@ +#include "postgres.h" +#include "fmgr.h" +#include "plpython.h" +#include "plpy_typeio.h" +#include "hstore.h" + +PG_MODULE_MAGIC; + + +PG_FUNCTION_INFO_V1(hstore_to_plpython); + +Datum +hstore_to_plpython(PG_FUNCTION_ARGS) +{ + HStore *in = PG_GETARG_HS(0); + int i; + int count = HS_COUNT(in); + char *base = STRPTR(in); + HEntry *entries = ARRPTR(in); + PyObject *dict; + + dict = PyDict_New(); + + for (i = 0; i < count; i++) + { + PyObject *key; + + key = PyString_FromStringAndSize(HS_KEY(entries, base, i), HS_KEYLEN(entries, i)); + if (HS_VALISNULL(entries, i)) + PyDict_SetItem(dict, key, Py_None); + else + { + PyObject *value; + + value = PyString_FromStringAndSize(HS_VAL(entries, base, i), HS_VALLEN(entries, i)); + PyDict_SetItem(dict, key, value); + Py_XDECREF(value); + } + Py_XDECREF(key); + } + + return PointerGetDatum(dict); +} + + +PG_FUNCTION_INFO_V1(plpython_to_hstore); + +Datum +plpython_to_hstore(PG_FUNCTION_ARGS) +{ + PyObject *dict; + volatile PyObject *items_v = NULL; + int32 pcount; + HStore *out; + + dict = (PyObject *) PG_GETARG_POINTER(0); + if (!PyMapping_Check(dict)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("not a Python mapping"))); + + pcount = PyMapping_Size(dict); + items_v = PyMapping_Items(dict); + + PG_TRY(); + { + int32 buflen; + int32 i; + Pairs *pairs; + PyObject *items = (PyObject *) items_v; + + pairs = palloc(pcount * sizeof(*pairs)); + + for (i = 0; i < pcount; i++) + { + PyObject *tuple; + PyObject *key; + PyObject *value; + + tuple = PyList_GetItem(items, i); + key = PyTuple_GetItem(tuple, 0); + value = PyTuple_GetItem(tuple, 1); + + pairs[i].key = PLyObject_AsString(key); + pairs[i].keylen = hstoreCheckKeyLen(strlen(pairs[i].key)); + pairs[i].needfree = true; + + if (value == Py_None) + { + pairs[i].val = NULL; + pairs[i].vallen = 0; + pairs[i].isnull = true; + } + else + { + pairs[i].val = PLyObject_AsString(value); + pairs[i].vallen = hstoreCheckValLen(strlen(pairs[i].val)); + pairs[i].isnull = false; + } + } + Py_DECREF(items_v); + + pcount = hstoreUniquePairs(pairs, pcount, &buflen); + out = hstorePairs(pairs, pcount, buflen); + } + PG_CATCH(); + { + Py_DECREF(items_v); + PG_RE_THROW(); + } + PG_END_TRY(); + + PG_RETURN_POINTER(out); +} diff --git a/contrib/hstore_plpython/hstore_plpython2u--1.0.sql b/contrib/hstore_plpython/hstore_plpython2u--1.0.sql new file mode 100644 index 0000000000..c998de51c9 --- /dev/null +++ b/contrib/hstore_plpython/hstore_plpython2u--1.0.sql @@ -0,0 +1,19 @@ +-- make sure the prerequisite libraries are loaded +DO '1' LANGUAGE plpython2u; +SELECT NULL::hstore; + + +CREATE FUNCTION hstore_to_plpython2(val internal) RETURNS internal +LANGUAGE C STRICT IMMUTABLE +AS 'MODULE_PATHNAME', 'hstore_to_plpython'; + +CREATE FUNCTION plpython2_to_hstore(val internal) RETURNS hstore +LANGUAGE C STRICT IMMUTABLE +AS 'MODULE_PATHNAME', 'plpython_to_hstore'; + +CREATE TRANSFORM FOR hstore LANGUAGE plpython2u ( + FROM SQL WITH FUNCTION hstore_to_plpython2(internal), + TO SQL WITH FUNCTION plpython2_to_hstore(internal) +); + +COMMENT ON TRANSFORM FOR hstore LANGUAGE plpython2u IS 'transform between hstore and Python dict'; diff --git a/contrib/hstore_plpython/hstore_plpython2u.control b/contrib/hstore_plpython/hstore_plpython2u.control new file mode 100644 index 0000000000..ed90567112 --- /dev/null +++ b/contrib/hstore_plpython/hstore_plpython2u.control @@ -0,0 +1,6 @@ +# hstore_plpython2u extension +comment = 'transform between hstore and plpython2u' +default_version = '1.0' +module_pathname = '$libdir/hstore_plpython2' +relocatable = true +requires = 'hstore,plpython2u' diff --git a/contrib/hstore_plpython/hstore_plpython3u--1.0.sql b/contrib/hstore_plpython/hstore_plpython3u--1.0.sql new file mode 100644 index 0000000000..61d0e47793 --- /dev/null +++ b/contrib/hstore_plpython/hstore_plpython3u--1.0.sql @@ -0,0 +1,19 @@ +-- make sure the prerequisite libraries are loaded +DO '1' LANGUAGE plpython3u; +SELECT NULL::hstore; + + +CREATE FUNCTION hstore_to_plpython3(val internal) RETURNS internal +LANGUAGE C STRICT IMMUTABLE +AS 'MODULE_PATHNAME', 'hstore_to_plpython'; + +CREATE FUNCTION plpython3_to_hstore(val internal) RETURNS hstore +LANGUAGE C STRICT IMMUTABLE +AS 'MODULE_PATHNAME', 'plpython_to_hstore'; + +CREATE TRANSFORM FOR hstore LANGUAGE plpython3u ( + FROM SQL WITH FUNCTION hstore_to_plpython3(internal), + TO SQL WITH FUNCTION plpython3_to_hstore(internal) +); + +COMMENT ON TRANSFORM FOR hstore LANGUAGE plpython3u IS 'transform between hstore and Python dict'; diff --git a/contrib/hstore_plpython/hstore_plpython3u.control b/contrib/hstore_plpython/hstore_plpython3u.control new file mode 100644 index 0000000000..d86f38e9a7 --- /dev/null +++ b/contrib/hstore_plpython/hstore_plpython3u.control @@ -0,0 +1,6 @@ +# hstore_plpython3u extension +comment = 'transform between hstore and plpython3u' +default_version = '1.0' +module_pathname = '$libdir/hstore_plpython3' +relocatable = true +requires = 'hstore,plpython3u' diff --git a/contrib/hstore_plpython/hstore_plpythonu--1.0.sql b/contrib/hstore_plpython/hstore_plpythonu--1.0.sql new file mode 100644 index 0000000000..6acb97aab9 --- /dev/null +++ b/contrib/hstore_plpython/hstore_plpythonu--1.0.sql @@ -0,0 +1,19 @@ +-- make sure the prerequisite libraries are loaded +DO '1' LANGUAGE plpythonu; +SELECT NULL::hstore; + + +CREATE FUNCTION hstore_to_plpython(val internal) RETURNS internal +LANGUAGE C STRICT IMMUTABLE +AS 'MODULE_PATHNAME'; + +CREATE FUNCTION plpython_to_hstore(val internal) RETURNS hstore +LANGUAGE C STRICT IMMUTABLE +AS 'MODULE_PATHNAME'; + +CREATE TRANSFORM FOR hstore LANGUAGE plpythonu ( + FROM SQL WITH FUNCTION hstore_to_plpython(internal), + TO SQL WITH FUNCTION plpython_to_hstore(internal) +); + +COMMENT ON TRANSFORM FOR hstore LANGUAGE plpythonu IS 'transform between hstore and Python dict'; diff --git a/contrib/hstore_plpython/hstore_plpythonu.control b/contrib/hstore_plpython/hstore_plpythonu.control new file mode 100644 index 0000000000..8e9b35e43b --- /dev/null +++ b/contrib/hstore_plpython/hstore_plpythonu.control @@ -0,0 +1,6 @@ +# hstore_plpythonu extension +comment = 'transform between hstore and plpythonu' +default_version = '1.0' +module_pathname = '$libdir/hstore_plpython2' +relocatable = true +requires = 'hstore,plpythonu' diff --git a/contrib/hstore_plpython/sql/hstore_plpython.sql b/contrib/hstore_plpython/sql/hstore_plpython.sql new file mode 100644 index 0000000000..9ff2ebcd83 --- /dev/null +++ b/contrib/hstore_plpython/sql/hstore_plpython.sql @@ -0,0 +1,107 @@ +CREATE EXTENSION plpython2u; +CREATE EXTENSION hstore_plpython2u; + + +-- test hstore -> python +CREATE FUNCTION test1(val hstore) RETURNS int +LANGUAGE plpythonu +TRANSFORM FOR TYPE hstore +AS $$ +assert isinstance(val, dict) +i = list(val.items()) +i.sort() +plpy.info(i) +return len(val) +$$; + +SELECT test1('aa=>bb, cc=>NULL'::hstore); + + +-- the same with the versioned language name +CREATE FUNCTION test1n(val hstore) RETURNS int +LANGUAGE plpython2u +TRANSFORM FOR TYPE hstore +AS $$ +assert isinstance(val, dict) +i = list(val.items()) +i.sort() +plpy.info(i) +return len(val) +$$; + +SELECT test1n('aa=>bb, cc=>NULL'::hstore); + + +-- test hstore[] -> python +CREATE FUNCTION test1arr(val hstore[]) RETURNS int +LANGUAGE plpythonu +TRANSFORM FOR TYPE hstore +AS $$ +assert(val == [{'aa': 'bb', 'cc': None}, {'dd': 'ee'}]) +return len(val) +$$; + +SELECT test1arr(array['aa=>bb, cc=>NULL'::hstore, 'dd=>ee']); + + +-- test python -> hstore +CREATE FUNCTION test2() RETURNS hstore +LANGUAGE plpythonu +TRANSFORM FOR TYPE hstore +AS $$ +val = {'a': 1, 'b': 'boo', 'c': None} +return val +$$; + +SELECT test2(); + + +-- test python -> hstore[] +CREATE FUNCTION test2arr() RETURNS hstore[] +LANGUAGE plpythonu +TRANSFORM FOR TYPE hstore +AS $$ +val = [{'a': 1, 'b': 'boo', 'c': None}, {'d': 2}] +return val +$$; + + SELECT test2arr(); + + +-- test as part of prepare/execute +CREATE FUNCTION test3() RETURNS void +LANGUAGE plpythonu +TRANSFORM FOR TYPE hstore +AS $$ +rv = plpy.execute("SELECT 'aa=>bb, cc=>NULL'::hstore AS col1") +assert(rv[0]["col1"] == {'aa': 'bb', 'cc': None}) + +val = {'a': 1, 'b': 'boo', 'c': None} +plan = plpy.prepare("SELECT $1::text AS col1", ["hstore"]) +rv = plpy.execute(plan, [val]) +assert(rv[0]["col1"] == '"a"=>"1", "b"=>"boo", "c"=>NULL') +$$; + +SELECT test3(); + + +-- test trigger +CREATE TABLE test1 (a int, b hstore); +INSERT INTO test1 VALUES (1, 'aa=>bb, cc=>NULL'); +SELECT * FROM test1; + +CREATE FUNCTION test4() RETURNS trigger +LANGUAGE plpythonu +TRANSFORM FOR TYPE hstore +AS $$ +assert(TD["new"] == {'a': 1, 'b': {'aa': 'bb', 'cc': None}}) +if TD["new"]["a"] == 1: + TD["new"]["b"] = {'a': 1, 'b': 'boo', 'c': None} + +return "MODIFY" +$$; + +CREATE TRIGGER test4 BEFORE UPDATE ON test1 FOR EACH ROW EXECUTE PROCEDURE test4(); + +UPDATE test1 SET a = a; +SELECT * FROM test1; diff --git a/contrib/intarray/_int_gin.c b/contrib/intarray/_int_gin.c index 58352cac80..fb16b66edb 100644 --- a/contrib/intarray/_int_gin.c +++ b/contrib/intarray/_int_gin.c @@ -4,8 +4,7 @@ #include "postgres.h" #include "access/gin.h" -#include "access/gist.h" -#include "access/skey.h" +#include "access/stratnum.h" #include "_int.h" diff --git a/contrib/intarray/_int_gist.c b/contrib/intarray/_int_gist.c index 07108eb15e..888c277e60 100644 --- a/contrib/intarray/_int_gist.c +++ b/contrib/intarray/_int_gist.c @@ -6,7 +6,7 @@ #include <limits.h> #include "access/gist.h" -#include "access/skey.h" +#include "access/stratnum.h" #include "_int.h" diff --git a/contrib/intarray/_intbig_gist.c b/contrib/intarray/_intbig_gist.c index 235db38957..6dae7c91c1 100644 --- a/contrib/intarray/_intbig_gist.c +++ b/contrib/intarray/_intbig_gist.c @@ -4,7 +4,7 @@ #include "postgres.h" #include "access/gist.h" -#include "access/skey.h" +#include "access/stratnum.h" #include "_int.h" diff --git a/contrib/isn/isn.c b/contrib/isn/isn.c index 5fbd253491..40398245f6 100644 --- a/contrib/isn/isn.c +++ b/contrib/isn/isn.c @@ -511,7 +511,7 @@ str2ean(const char *num) } /* - * ean2string --- Try to convert an ean13 number to an hyphenated string. + * ean2string --- Try to convert an ean13 number to a hyphenated string. * Assumes there's enough space in result to hold * the string (maximum MAXEAN13LEN+1 bytes) * This doesn't verify for a valid check digit. diff --git a/contrib/ltree/_ltree_gist.c b/contrib/ltree/_ltree_gist.c index 41be68d7ee..37cd991694 100644 --- a/contrib/ltree/_ltree_gist.c +++ b/contrib/ltree/_ltree_gist.c @@ -8,7 +8,7 @@ #include "postgres.h" #include "access/gist.h" -#include "access/skey.h" +#include "access/stratnum.h" #include "crc32.h" #include "ltree.h" diff --git a/contrib/ltree/crc32.c b/contrib/ltree/crc32.c index 1c08d264f7..403dae0d7d 100644 --- a/contrib/ltree/crc32.c +++ b/contrib/ltree/crc32.c @@ -26,13 +26,14 @@ unsigned int ltree_crc32_sz(char *buf, int size) { - pg_crc32 crc; + pg_crc32 crc; char *p = buf; INIT_TRADITIONAL_CRC32(crc); while (size > 0) { - char c = (char) TOLOWER(*p); + char c = (char) TOLOWER(*p); + COMP_TRADITIONAL_CRC32(crc, &c, 1); size--; p++; diff --git a/contrib/ltree/ltree_gist.c b/contrib/ltree/ltree_gist.c index 2d89f1aed4..83da62018e 100644 --- a/contrib/ltree/ltree_gist.c +++ b/contrib/ltree/ltree_gist.c @@ -6,7 +6,7 @@ #include "postgres.h" #include "access/gist.h" -#include "access/skey.h" +#include "access/stratnum.h" #include "crc32.h" #include "ltree.h" diff --git a/contrib/ltree_plpython/.gitignore b/contrib/ltree_plpython/.gitignore new file mode 100644 index 0000000000..ce6fab94a0 --- /dev/null +++ b/contrib/ltree_plpython/.gitignore @@ -0,0 +1,6 @@ +# Generated subdirectories +/expected/python3/ +/log/ +/results/ +/sql/python3/ +/tmp_check/ diff --git a/contrib/ltree_plpython/Makefile b/contrib/ltree_plpython/Makefile new file mode 100644 index 0000000000..64ca1275f1 --- /dev/null +++ b/contrib/ltree_plpython/Makefile @@ -0,0 +1,37 @@ +# contrib/ltree_plpython/Makefile + +MODULE_big = ltree_plpython$(python_majorversion) +OBJS = ltree_plpython.o $(WIN32RES) +PGFILEDESC = "ltree_plpython - ltree transform for plpython" + +PG_CPPFLAGS = -I$(top_srcdir)/src/pl/plpython $(python_includespec) -I$(top_srcdir)/contrib/ltree + +EXTENSION = ltree_plpythonu ltree_plpython2u ltree_plpython3u +DATA = ltree_plpythonu--1.0.sql ltree_plpython2u--1.0.sql ltree_plpython3u--1.0.sql + +REGRESS = ltree_plpython +REGRESS_PLPYTHON3_MANGLE := $(REGRESS) + +ifdef USE_PGXS +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = contrib/ltree_plpython +top_builddir = ../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif + +ifeq ($(PORTNAME), win32) +# This means we need an in-tree build on Windows, not a pgxs build +SHLIB_LINK += $(wildcard ../../src/pl/plpython/libpython*.a) +endif + +REGRESS_OPTS += --load-extension=ltree +ifeq ($(python_majorversion),2) +REGRESS_OPTS += --load-extension=plpythonu --load-extension=ltree_plpythonu +endif +EXTRA_INSTALL += contrib/ltree + +include $(top_srcdir)/src/pl/plpython/regress-python3-mangle.mk diff --git a/contrib/ltree_plpython/expected/ltree_plpython.out b/contrib/ltree_plpython/expected/ltree_plpython.out new file mode 100644 index 0000000000..934529ee0f --- /dev/null +++ b/contrib/ltree_plpython/expected/ltree_plpython.out @@ -0,0 +1,45 @@ +CREATE EXTENSION plpython2u; +CREATE EXTENSION ltree_plpython2u; +CREATE FUNCTION test1(val ltree) RETURNS int +LANGUAGE plpythonu +TRANSFORM FOR TYPE ltree +AS $$ +plpy.info(repr(val)) +return len(val) +$$; +SELECT test1('aa.bb.cc'::ltree); +INFO: ['aa', 'bb', 'cc'] +CONTEXT: PL/Python function "test1" + test1 +------- + 3 +(1 row) + +CREATE FUNCTION test1n(val ltree) RETURNS int +LANGUAGE plpython2u +TRANSFORM FOR TYPE ltree +AS $$ +plpy.info(repr(val)) +return len(val) +$$; +SELECT test1n('aa.bb.cc'::ltree); +INFO: ['aa', 'bb', 'cc'] +CONTEXT: PL/Python function "test1n" + test1n +-------- + 3 +(1 row) + +CREATE FUNCTION test2() RETURNS ltree +LANGUAGE plpythonu +TRANSFORM FOR TYPE ltree +AS $$ +return ['foo', 'bar', 'baz'] +$$; +-- plpython to ltree is not yet implemented, so this will fail, +-- because it will try to parse the Python list as an ltree input +-- string. +SELECT test2(); +ERROR: syntax error at position 0 +CONTEXT: while creating return value +PL/Python function "test2" diff --git a/contrib/ltree_plpython/ltree_plpython.c b/contrib/ltree_plpython/ltree_plpython.c new file mode 100644 index 0000000000..26b7b3c275 --- /dev/null +++ b/contrib/ltree_plpython/ltree_plpython.c @@ -0,0 +1,31 @@ +#include "postgres.h" +#include "fmgr.h" +#include "plpython.h" +#include "ltree.h" + +PG_MODULE_MAGIC; + + +PG_FUNCTION_INFO_V1(ltree_to_plpython); + +Datum +ltree_to_plpython(PG_FUNCTION_ARGS) +{ + ltree *in = PG_GETARG_LTREE(0); + int i; + PyObject *list; + ltree_level *curlevel; + + list = PyList_New(in->numlevel); + + curlevel = LTREE_FIRST(in); + for (i = 0; i < in->numlevel; i++) + { + PyList_SetItem(list, i, PyString_FromStringAndSize(curlevel->name, curlevel->len)); + curlevel = LEVEL_NEXT(curlevel); + } + + PG_FREE_IF_COPY(in, 0); + + return PointerGetDatum(list); +} diff --git a/contrib/ltree_plpython/ltree_plpython2u--1.0.sql b/contrib/ltree_plpython/ltree_plpython2u--1.0.sql new file mode 100644 index 0000000000..29a12d45a2 --- /dev/null +++ b/contrib/ltree_plpython/ltree_plpython2u--1.0.sql @@ -0,0 +1,12 @@ +-- make sure the prerequisite libraries are loaded +DO '1' LANGUAGE plpython2u; +SELECT NULL::ltree; + + +CREATE FUNCTION ltree_to_plpython2(val internal) RETURNS internal +LANGUAGE C STRICT IMMUTABLE +AS 'MODULE_PATHNAME', 'ltree_to_plpython'; + +CREATE TRANSFORM FOR ltree LANGUAGE plpython2u ( + FROM SQL WITH FUNCTION ltree_to_plpython2(internal) +); diff --git a/contrib/ltree_plpython/ltree_plpython2u.control b/contrib/ltree_plpython/ltree_plpython2u.control new file mode 100644 index 0000000000..bedfd0acba --- /dev/null +++ b/contrib/ltree_plpython/ltree_plpython2u.control @@ -0,0 +1,6 @@ +# ltree_plpython2u extension +comment = 'transform between ltree and plpython2u' +default_version = '1.0' +module_pathname = '$libdir/ltree_plpython2' +relocatable = true +requires = 'ltree,plpython2u' diff --git a/contrib/ltree_plpython/ltree_plpython3u--1.0.sql b/contrib/ltree_plpython/ltree_plpython3u--1.0.sql new file mode 100644 index 0000000000..1300a78c66 --- /dev/null +++ b/contrib/ltree_plpython/ltree_plpython3u--1.0.sql @@ -0,0 +1,12 @@ +-- make sure the prerequisite libraries are loaded +DO '1' LANGUAGE plpython3u; +SELECT NULL::ltree; + + +CREATE FUNCTION ltree_to_plpython3(val internal) RETURNS internal +LANGUAGE C STRICT IMMUTABLE +AS 'MODULE_PATHNAME', 'ltree_to_plpython'; + +CREATE TRANSFORM FOR ltree LANGUAGE plpython3u ( + FROM SQL WITH FUNCTION ltree_to_plpython3(internal) +); diff --git a/contrib/ltree_plpython/ltree_plpython3u.control b/contrib/ltree_plpython/ltree_plpython3u.control new file mode 100644 index 0000000000..96c9764331 --- /dev/null +++ b/contrib/ltree_plpython/ltree_plpython3u.control @@ -0,0 +1,6 @@ +# ltree_plpython3u extension +comment = 'transform between ltree and plpython3u' +default_version = '1.0' +module_pathname = '$libdir/ltree_plpython3' +relocatable = true +requires = 'ltree,plpython3u' diff --git a/contrib/ltree_plpython/ltree_plpythonu--1.0.sql b/contrib/ltree_plpython/ltree_plpythonu--1.0.sql new file mode 100644 index 0000000000..1d1af28f4e --- /dev/null +++ b/contrib/ltree_plpython/ltree_plpythonu--1.0.sql @@ -0,0 +1,12 @@ +-- make sure the prerequisite libraries are loaded +DO '1' LANGUAGE plpythonu; +SELECT NULL::ltree; + + +CREATE FUNCTION ltree_to_plpython(val internal) RETURNS internal +LANGUAGE C STRICT IMMUTABLE +AS 'MODULE_PATHNAME'; + +CREATE TRANSFORM FOR ltree LANGUAGE plpythonu ( + FROM SQL WITH FUNCTION ltree_to_plpython(internal) +); diff --git a/contrib/ltree_plpython/ltree_plpythonu.control b/contrib/ltree_plpython/ltree_plpythonu.control new file mode 100644 index 0000000000..b03c89a2e6 --- /dev/null +++ b/contrib/ltree_plpython/ltree_plpythonu.control @@ -0,0 +1,6 @@ +# ltree_plpythonu extension +comment = 'transform between ltree and plpythonu' +default_version = '1.0' +module_pathname = '$libdir/ltree_plpython2' +relocatable = true +requires = 'ltree,plpythonu' diff --git a/contrib/ltree_plpython/sql/ltree_plpython.sql b/contrib/ltree_plpython/sql/ltree_plpython.sql new file mode 100644 index 0000000000..f08ff6a3f0 --- /dev/null +++ b/contrib/ltree_plpython/sql/ltree_plpython.sql @@ -0,0 +1,37 @@ +CREATE EXTENSION plpython2u; +CREATE EXTENSION ltree_plpython2u; + + +CREATE FUNCTION test1(val ltree) RETURNS int +LANGUAGE plpythonu +TRANSFORM FOR TYPE ltree +AS $$ +plpy.info(repr(val)) +return len(val) +$$; + +SELECT test1('aa.bb.cc'::ltree); + + +CREATE FUNCTION test1n(val ltree) RETURNS int +LANGUAGE plpython2u +TRANSFORM FOR TYPE ltree +AS $$ +plpy.info(repr(val)) +return len(val) +$$; + +SELECT test1n('aa.bb.cc'::ltree); + + +CREATE FUNCTION test2() RETURNS ltree +LANGUAGE plpythonu +TRANSFORM FOR TYPE ltree +AS $$ +return ['foo', 'bar', 'baz'] +$$; + +-- plpython to ltree is not yet implemented, so this will fail, +-- because it will try to parse the Python list as an ltree input +-- string. +SELECT test2(); diff --git a/contrib/pageinspect/brinfuncs.c b/contrib/pageinspect/brinfuncs.c index 1b15a7bdfe..7adcfa8937 100644 --- a/contrib/pageinspect/brinfuncs.c +++ b/contrib/pageinspect/brinfuncs.c @@ -58,7 +58,7 @@ brin_page_type(PG_FUNCTION_ARGS) { bytea *raw_page = PG_GETARG_BYTEA_P(0); Page page = VARDATA(raw_page); - char *type; + char *type; switch (BrinPageType(page)) { @@ -86,8 +86,8 @@ brin_page_type(PG_FUNCTION_ARGS) static Page verify_brin_page(bytea *raw_page, uint16 type, const char *strtype) { - Page page; - int raw_page_size; + Page page; + int raw_page_size; raw_page_size = VARSIZE(raw_page) - VARHDRSZ; @@ -95,7 +95,7 @@ verify_brin_page(bytea *raw_page, ui |