diff options
author | Shigeru Hanada | 2011-02-17 08:15:36 +0000 |
---|---|---|
committer | Shigeru Hanada | 2011-02-17 08:15:36 +0000 |
commit | 14d78946d9befd9920fa398c6751fd51ae55652d (patch) | |
tree | 94bf0778f8687e39a6541de556b096fe1aaceabc | |
parent | d9379f2c568fe6aa850a4fc2be7c9644909f73ae (diff) | |
parent | d08817a28495bcdcb20482d9ba30a6893154a5ae (diff) |
Merge branch 'foreign_scan' into file_fdw
585 files changed, 20678 insertions, 6001 deletions
diff --git a/GNUmakefile.in b/GNUmakefile.in index 8ccbdcc49f..b9c5f317b6 100644 --- a/GNUmakefile.in +++ b/GNUmakefile.in @@ -60,6 +60,9 @@ check: all check installcheck installcheck-parallel: $(MAKE) -C src/test $@ +# TODO: add contrib +$(call recurse,check-world,src/test src/pl src/interfaces/ecpg,check) + $(call recurse,installcheck-world,src/test src/pl src/interfaces/ecpg contrib,installcheck) GNUmakefile: GNUmakefile.in $(top_builddir)/config.status @@ -121,4 +124,4 @@ distcheck: dist rm -rf $(distdir) $(dummy) @echo "Distribution integrity checks out." -.PHONY: dist distdir distcheck docs install-docs world install-world installcheck-world +.PHONY: dist distdir distcheck docs install-docs world check-world install-world installcheck-world @@ -11,7 +11,7 @@ # GNUmakefile won't exist yet, so we catch that case as well. -all check install installdirs installcheck installcheck-parallel uninstall clean distclean maintainer-clean dist distcheck world install-world installcheck-world: +all check install installdirs installcheck installcheck-parallel uninstall clean distclean maintainer-clean dist distcheck world check-world install-world installcheck-world: @if [ ! -f GNUmakefile ] ; then \ echo "You need to run the 'configure' program first. See the file"; \ echo "'INSTALL' for installation instructions." ; \ diff --git a/config/acx_pthread.m4 b/config/acx_pthread.m4 index ceb161a556..6ff241eba1 100644 --- a/config/acx_pthread.m4 +++ b/config/acx_pthread.m4 @@ -142,7 +142,8 @@ main (int argc, char **argv) } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext - if test "`(eval $ac_link 2>&1 1>&5)`" = ""; then + # Check both linking and compiling, because they might tolerate different options. + if test "`(eval $ac_link 2>&1 1>&5)`" = "" && test "`(eval $ac_compile 2>&1 1>&5)`" = ""; then # we continue with more flags because Linux needs -lpthread # for libpq builds on PostgreSQL. The test above only # tests for building binaries, not shared libraries. diff --git a/config/c-library.m4 b/config/c-library.m4 index 98e03e3d18..cddeafaec2 100644 --- a/config/c-library.m4 +++ b/config/c-library.m4 @@ -297,3 +297,32 @@ int main() ])dnl AC_CACHE_VAL AC_MSG_RESULT([$pgac_cv_printf_arg_control]) ])# PGAC_FUNC_PRINTF_ARG_CONTROL + + +# PGAC_TYPE_LOCALE_T +# ------------------ +# Check for the locale_t type and find the right header file. Mac OS +# X needs xlocale.h; standard is locale.h, but glibc also has an +# xlocale.h file that we should not use. +# +AC_DEFUN([PGAC_TYPE_LOCALE_T], +[AC_CACHE_CHECK([for locale_t], pgac_cv_type_locale_t, +[AC_COMPILE_IFELSE([AC_LANG_PROGRAM( +[#include <locale.h> +locale_t x;], +[])], +[pgac_cv_type_locale_t=yes], +[AC_COMPILE_IFELSE([AC_LANG_PROGRAM( +[#include <xlocale.h> +locale_t x;], +[])], +[pgac_cv_type_locale_t='yes (in xlocale.h)'], +[pgac_cv_type_locale_t=no])])]) +if test "$pgac_cv_type_locale_t" != no; then + AC_DEFINE(HAVE_LOCALE_T, 1, + [Define to 1 if the system has the type `locale_t'.]) +fi +if test "$pgac_cv_type_locale_t" = 'yes (in xlocale.h)'; then + AC_DEFINE(LOCALE_T_IN_XLOCALE, 1, + [Define to 1 if `locale_t' requires <xlocale.h>.]) +fi])])# PGAC_HEADER_XLOCALE @@ -16830,6 +16830,114 @@ _ACEOF fi +{ $as_echo "$as_me:$LINENO: checking for locale_t" >&5 +$as_echo_n "checking for locale_t... " >&6; } +if test "${pgac_cv_type_locale_t+set}" = set; then + $as_echo_n "(cached) " >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <locale.h> +locale_t x; +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + pgac_cv_type_locale_t=yes +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <xlocale.h> +locale_t x; +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:$LINENO: $ac_try_echo\"" +$as_echo "$ac_try_echo") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + $as_echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + pgac_cv_type_locale_t='yes (in xlocale.h)' +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + pgac_cv_type_locale_t=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:$LINENO: result: $pgac_cv_type_locale_t" >&5 +$as_echo "$pgac_cv_type_locale_t" >&6; } +if test "$pgac_cv_type_locale_t" != no; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_LOCALE_T 1 +_ACEOF + +fi +if test "$pgac_cv_type_locale_t" = 'yes (in xlocale.h)'; then + +cat >>confdefs.h <<\_ACEOF +#define LOCALE_T_IN_XLOCALE 1 +_ACEOF + +fi + { $as_echo "$as_me:$LINENO: checking for struct cmsgcred" >&5 $as_echo_n "checking for struct cmsgcred... " >&6; } if test "${ac_cv_type_struct_cmsgcred+set}" = set; then @@ -22706,7 +22814,8 @@ main (int argc, char **argv) } _ACEOF rm -f conftest.$ac_objext conftest$ac_exeext - if test "`(eval $ac_link 2>&1 1>&5)`" = ""; then + # Check both linking and compiling, because they might tolerate different options. + if test "`(eval $ac_link 2>&1 1>&5)`" = "" && test "`(eval $ac_compile 2>&1 1>&5)`" = ""; then # we continue with more flags because Linux needs -lpthread # for libpq builds on PostgreSQL. The test above only # tests for building binaries, not shared libraries. diff --git a/configure.in b/configure.in index e9a1b2dcba..0dffc51d0b 100644 --- a/configure.in +++ b/configure.in @@ -1118,6 +1118,8 @@ AC_TYPE_INTPTR_T AC_TYPE_UINTPTR_T AC_TYPE_LONG_LONG_INT +PGAC_TYPE_LOCALE_T + AC_CHECK_TYPES([struct cmsgcred, struct fcred, struct sockcred], [], [], [#include <sys/param.h> #include <sys/types.h> diff --git a/contrib/adminpack/.gitignore b/contrib/adminpack/.gitignore deleted file mode 100644 index ea9a442f3a..0000000000 --- a/contrib/adminpack/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/adminpack.sql diff --git a/contrib/adminpack/Makefile b/contrib/adminpack/Makefile index d4413ad133..5cbc8f0c71 100644 --- a/contrib/adminpack/Makefile +++ b/contrib/adminpack/Makefile @@ -1,10 +1,11 @@ # contrib/adminpack/Makefile MODULE_big = adminpack -PG_CPPFLAGS = -I$(libpq_srcdir) -DATA_built = adminpack.sql -DATA = uninstall_adminpack.sql OBJS = adminpack.o +PG_CPPFLAGS = -I$(libpq_srcdir) + +EXTENSION = adminpack +DATA = adminpack--1.0.sql ifdef USE_PGXS PG_CONFIG = pg_config diff --git a/contrib/adminpack/adminpack.sql.in b/contrib/adminpack/adminpack--1.0.sql index 6e389975d0..090702231c 100644 --- a/contrib/adminpack/adminpack.sql.in +++ b/contrib/adminpack/adminpack--1.0.sql @@ -1,4 +1,4 @@ -/* contrib/adminpack/adminpack.sql.in */ +/* contrib/adminpack/adminpack--1.0.sql */ /* *********************************************** * Administrative functions for PostgreSQL @@ -6,27 +6,27 @@ /* generic file access functions */ -CREATE OR REPLACE FUNCTION pg_catalog.pg_file_write(text, text, bool) +CREATE FUNCTION pg_catalog.pg_file_write(text, text, bool) RETURNS bigint AS 'MODULE_PATHNAME', 'pg_file_write' LANGUAGE C VOLATILE STRICT; -CREATE OR REPLACE FUNCTION pg_catalog.pg_file_rename(text, text, text) +CREATE FUNCTION pg_catalog.pg_file_rename(text, text, text) RETURNS bool AS 'MODULE_PATHNAME', 'pg_file_rename' LANGUAGE C VOLATILE; -CREATE OR REPLACE FUNCTION pg_catalog.pg_file_rename(text, text) +CREATE FUNCTION pg_catalog.pg_file_rename(text, text) RETURNS bool AS 'SELECT pg_catalog.pg_file_rename($1, $2, NULL::pg_catalog.text);' LANGUAGE SQL VOLATILE STRICT; -CREATE OR REPLACE FUNCTION pg_catalog.pg_file_unlink(text) +CREATE FUNCTION pg_catalog.pg_file_unlink(text) RETURNS bool AS 'MODULE_PATHNAME', 'pg_file_unlink' LANGUAGE C VOLATILE STRICT; -CREATE OR REPLACE FUNCTION pg_catalog.pg_logdir_ls() +CREATE FUNCTION pg_catalog.pg_logdir_ls() RETURNS setof record AS 'MODULE_PATHNAME', 'pg_logdir_ls' LANGUAGE C VOLATILE STRICT; @@ -34,17 +34,17 @@ LANGUAGE C VOLATILE STRICT; /* Renaming of existing backend functions for pgAdmin compatibility */ -CREATE OR REPLACE FUNCTION pg_catalog.pg_file_read(text, bigint, bigint) +CREATE FUNCTION pg_catalog.pg_file_read(text, bigint, bigint) RETURNS text AS 'pg_read_file' LANGUAGE INTERNAL VOLATILE STRICT; -CREATE OR REPLACE FUNCTION pg_catalog.pg_file_length(text) +CREATE FUNCTION pg_catalog.pg_file_length(text) RETURNS bigint AS 'SELECT size FROM pg_catalog.pg_stat_file($1)' LANGUAGE SQL VOLATILE STRICT; -CREATE OR REPLACE FUNCTION pg_catalog.pg_logfile_rotate() +CREATE FUNCTION pg_catalog.pg_logfile_rotate() RETURNS int4 AS 'pg_rotate_logfile' LANGUAGE INTERNAL VOLATILE STRICT; diff --git a/contrib/adminpack/adminpack.c b/contrib/adminpack/adminpack.c index 381554d114..c149dd6c63 100644 --- a/contrib/adminpack/adminpack.c +++ b/contrib/adminpack/adminpack.c @@ -73,32 +73,30 @@ convert_and_check_filename(text *arg, bool logAllowed) canonicalize_path(filename); /* filename can change length here */ - /* Disallow ".." in the path */ - if (path_contains_parent_reference(filename)) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - (errmsg("reference to parent directory (\"..\") not allowed")))); - if (is_absolute_path(filename)) { - /* Allow absolute references within DataDir */ - if (path_is_prefix_of_path(DataDir, filename)) - return filename; - /* The log directory might be outside our datadir, but allow it */ - if (logAllowed && - is_absolute_path(Log_directory) && - path_is_prefix_of_path(Log_directory, filename)) - return filename; - - ereport(ERROR, + /* Disallow '/a/b/data/..' */ + if (path_contains_parent_reference(filename)) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("reference to parent directory (\"..\") not allowed")))); + /* + * Allow absolute paths if within DataDir or Log_directory, even + * though Log_directory might be outside DataDir. + */ + if (!path_is_prefix_of_path(DataDir, filename) && + (!logAllowed || !is_absolute_path(Log_directory) || + !path_is_prefix_of_path(Log_directory, filename))) + ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("absolute path not allowed")))); - return NULL; /* keep compiler quiet */ - } - else - { - return filename; } + else if (!path_is_relative_and_below_cwd(filename)) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("path must be in or below the current directory")))); + + return filename; } diff --git a/contrib/adminpack/adminpack.control b/contrib/adminpack/adminpack.control new file mode 100644 index 0000000000..c79413f378 --- /dev/null +++ b/contrib/adminpack/adminpack.control @@ -0,0 +1,6 @@ +# adminpack extension +comment = 'administrative functions for PostgreSQL' +default_version = '1.0' +module_pathname = '$libdir/adminpack' +relocatable = false +schema = pg_catalog diff --git a/contrib/adminpack/uninstall_adminpack.sql b/contrib/adminpack/uninstall_adminpack.sql deleted file mode 100644 index 682cf67760..0000000000 --- a/contrib/adminpack/uninstall_adminpack.sql +++ /dev/null @@ -1,10 +0,0 @@ -/* contrib/adminpack/uninstall_adminpack.sql */ - -DROP FUNCTION pg_catalog.pg_file_write(text, text, bool) ; -DROP FUNCTION pg_catalog.pg_file_rename(text, text, text) ; -DROP FUNCTION pg_catalog.pg_file_rename(text, text) ; -DROP FUNCTION pg_catalog.pg_file_unlink(text) ; -DROP FUNCTION pg_catalog.pg_logdir_ls() ; -DROP FUNCTION pg_catalog.pg_file_read(text, bigint, bigint) ; -DROP FUNCTION pg_catalog.pg_file_length(text) ; -DROP FUNCTION pg_catalog.pg_logfile_rotate() ; diff --git a/contrib/btree_gin/.gitignore b/contrib/btree_gin/.gitignore index 7cebcf00f8..19b6c5ba42 100644 --- a/contrib/btree_gin/.gitignore +++ b/contrib/btree_gin/.gitignore @@ -1,3 +1,2 @@ -/btree_gin.sql # Generated subdirectories /results/ diff --git a/contrib/btree_gin/Makefile b/contrib/btree_gin/Makefile index 8bc53f72da..09fd3e6e11 100644 --- a/contrib/btree_gin/Makefile +++ b/contrib/btree_gin/Makefile @@ -3,8 +3,9 @@ MODULE_big = btree_gin OBJS = btree_gin.o -DATA_built = btree_gin.sql -DATA = uninstall_btree_gin.sql +EXTENSION = btree_gin +DATA = btree_gin--1.0.sql btree_gin--unpackaged--1.0.sql + REGRESS = install_btree_gin int2 int4 int8 float4 float8 money oid \ timestamp timestamptz time timetz date interval \ macaddr inet cidr text varchar char bytea bit varbit \ diff --git a/contrib/btree_gin/btree_gin.sql.in b/contrib/btree_gin/btree_gin--1.0.sql index 19cc0b3df4..07f93640f3 100644 --- a/contrib/btree_gin/btree_gin.sql.in +++ b/contrib/btree_gin/btree_gin--1.0.sql @@ -1,24 +1,21 @@ -/* contrib/btree_gin/btree_gin.sql.in */ +/* contrib/btree_gin/btree_gin--1.0.sql */ --- Adjust this setting to control where the objects get created. -SET search_path = public; - -CREATE OR REPLACE FUNCTION gin_btree_consistent(internal, int2, anyelement, int4, internal, internal) +CREATE FUNCTION gin_btree_consistent(internal, int2, anyelement, int4, internal, internal) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_extract_value_int2(int2, internal) +CREATE FUNCTION gin_extract_value_int2(int2, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_compare_prefix_int2(int2, int2, int2, internal) +CREATE FUNCTION gin_compare_prefix_int2(int2, int2, int2, internal) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_extract_query_int2(int2, internal, int2, internal, internal) +CREATE FUNCTION gin_extract_query_int2(int2, internal, int2, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -38,17 +35,17 @@ AS FUNCTION 5 gin_compare_prefix_int2(int2,int2,int2, internal), STORAGE int2; -CREATE OR REPLACE FUNCTION gin_extract_value_int4(int4, internal) +CREATE FUNCTION gin_extract_value_int4(int4, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_compare_prefix_int4(int4, int4, int2, internal) +CREATE FUNCTION gin_compare_prefix_int4(int4, int4, int2, internal) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_extract_query_int4(int4, internal, int2, internal, internal) +CREATE FUNCTION gin_extract_query_int4(int4, internal, int2, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -68,17 +65,17 @@ AS FUNCTION 5 gin_compare_prefix_int4(int4,int4,int2, internal), STORAGE int4; -CREATE OR REPLACE FUNCTION gin_extract_value_int8(int8, internal) +CREATE FUNCTION gin_extract_value_int8(int8, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_compare_prefix_int8(int8, int8, int2, internal) +CREATE FUNCTION gin_compare_prefix_int8(int8, int8, int2, internal) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_extract_query_int8(int8, internal, int2, internal, internal) +CREATE FUNCTION gin_extract_query_int8(int8, internal, int2, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -98,17 +95,17 @@ AS FUNCTION 5 gin_compare_prefix_int8(int8,int8,int2, internal), STORAGE int8; -CREATE OR REPLACE FUNCTION gin_extract_value_float4(float4, internal) +CREATE FUNCTION gin_extract_value_float4(float4, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_compare_prefix_float4(float4, float4, int2, internal) +CREATE FUNCTION gin_compare_prefix_float4(float4, float4, int2, internal) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_extract_query_float4(float4, internal, int2, internal, internal) +CREATE FUNCTION gin_extract_query_float4(float4, internal, int2, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -128,17 +125,17 @@ AS FUNCTION 5 gin_compare_prefix_float4(float4,float4,int2, internal), STORAGE float4; -CREATE OR REPLACE FUNCTION gin_extract_value_float8(float8, internal) +CREATE FUNCTION gin_extract_value_float8(float8, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_compare_prefix_float8(float8, float8, int2, internal) +CREATE FUNCTION gin_compare_prefix_float8(float8, float8, int2, internal) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_extract_query_float8(float8, internal, int2, internal, internal) +CREATE FUNCTION gin_extract_query_float8(float8, internal, int2, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -158,17 +155,17 @@ AS FUNCTION 5 gin_compare_prefix_float8(float8,float8,int2, internal), STORAGE float8; -CREATE OR REPLACE FUNCTION gin_extract_value_money(money, internal) +CREATE FUNCTION gin_extract_value_money(money, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_compare_prefix_money(money, money, int2, internal) +CREATE FUNCTION gin_compare_prefix_money(money, money, int2, internal) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_extract_query_money(money, internal, int2, internal, internal) +CREATE FUNCTION gin_extract_query_money(money, internal, int2, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -188,17 +185,17 @@ AS FUNCTION 5 gin_compare_prefix_money(money,money,int2, internal), STORAGE money; -CREATE OR REPLACE FUNCTION gin_extract_value_oid(oid, internal) +CREATE FUNCTION gin_extract_value_oid(oid, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_compare_prefix_oid(oid, oid, int2, internal) +CREATE FUNCTION gin_compare_prefix_oid(oid, oid, int2, internal) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_extract_query_oid(oid, internal, int2, internal, internal) +CREATE FUNCTION gin_extract_query_oid(oid, internal, int2, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -218,17 +215,17 @@ AS FUNCTION 5 gin_compare_prefix_oid(oid,oid,int2, internal), STORAGE oid; -CREATE OR REPLACE FUNCTION gin_extract_value_timestamp(timestamp, internal) +CREATE FUNCTION gin_extract_value_timestamp(timestamp, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_compare_prefix_timestamp(timestamp, timestamp, int2, internal) +CREATE FUNCTION gin_compare_prefix_timestamp(timestamp, timestamp, int2, internal) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_extract_query_timestamp(timestamp, internal, int2, internal, internal) +CREATE FUNCTION gin_extract_query_timestamp(timestamp, internal, int2, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -248,17 +245,17 @@ AS FUNCTION 5 gin_compare_prefix_timestamp(timestamp,timestamp,int2, internal), STORAGE timestamp; -CREATE OR REPLACE FUNCTION gin_extract_value_timestamptz(timestamptz, internal) +CREATE FUNCTION gin_extract_value_timestamptz(timestamptz, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_compare_prefix_timestamptz(timestamptz, timestamptz, int2, internal) +CREATE FUNCTION gin_compare_prefix_timestamptz(timestamptz, timestamptz, int2, internal) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_extract_query_timestamptz(timestamptz, internal, int2, internal, internal) +CREATE FUNCTION gin_extract_query_timestamptz(timestamptz, internal, int2, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -278,17 +275,17 @@ AS FUNCTION 5 gin_compare_prefix_timestamptz(timestamptz,timestamptz,int2, internal), STORAGE timestamptz; -CREATE OR REPLACE FUNCTION gin_extract_value_time(time, internal) +CREATE FUNCTION gin_extract_value_time(time, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_compare_prefix_time(time, time, int2, internal) +CREATE FUNCTION gin_compare_prefix_time(time, time, int2, internal) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_extract_query_time(time, internal, int2, internal, internal) +CREATE FUNCTION gin_extract_query_time(time, internal, int2, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -308,17 +305,17 @@ AS FUNCTION 5 gin_compare_prefix_time(time,time,int2, internal), STORAGE time; -CREATE OR REPLACE FUNCTION gin_extract_value_timetz(timetz, internal) +CREATE FUNCTION gin_extract_value_timetz(timetz, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_compare_prefix_timetz(timetz, timetz, int2, internal) +CREATE FUNCTION gin_compare_prefix_timetz(timetz, timetz, int2, internal) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_extract_query_timetz(timetz, internal, int2, internal, internal) +CREATE FUNCTION gin_extract_query_timetz(timetz, internal, int2, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -338,17 +335,17 @@ AS FUNCTION 5 gin_compare_prefix_timetz(timetz,timetz,int2, internal), STORAGE timetz; -CREATE OR REPLACE FUNCTION gin_extract_value_date(date, internal) +CREATE FUNCTION gin_extract_value_date(date, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_compare_prefix_date(date, date, int2, internal) +CREATE FUNCTION gin_compare_prefix_date(date, date, int2, internal) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_extract_query_date(date, internal, int2, internal, internal) +CREATE FUNCTION gin_extract_query_date(date, internal, int2, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -368,17 +365,17 @@ AS FUNCTION 5 gin_compare_prefix_date(date,date,int2, internal), STORAGE date; -CREATE OR REPLACE FUNCTION gin_extract_value_interval(interval, internal) +CREATE FUNCTION gin_extract_value_interval(interval, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_compare_prefix_interval(interval, interval, int2, internal) +CREATE FUNCTION gin_compare_prefix_interval(interval, interval, int2, internal) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_extract_query_interval(interval, internal, int2, internal, internal) +CREATE FUNCTION gin_extract_query_interval(interval, internal, int2, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -398,17 +395,17 @@ AS FUNCTION 5 gin_compare_prefix_interval(interval,interval,int2, internal), STORAGE interval; -CREATE OR REPLACE FUNCTION gin_extract_value_macaddr(macaddr, internal) +CREATE FUNCTION gin_extract_value_macaddr(macaddr, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_compare_prefix_macaddr(macaddr, macaddr, int2, internal) +CREATE FUNCTION gin_compare_prefix_macaddr(macaddr, macaddr, int2, internal) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_extract_query_macaddr(macaddr, internal, int2, internal, internal) +CREATE FUNCTION gin_extract_query_macaddr(macaddr, internal, int2, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -428,17 +425,17 @@ AS FUNCTION 5 gin_compare_prefix_macaddr(macaddr,macaddr,int2, internal), STORAGE macaddr; -CREATE OR REPLACE FUNCTION gin_extract_value_inet(inet, internal) +CREATE FUNCTION gin_extract_value_inet(inet, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_compare_prefix_inet(inet, inet, int2, internal) +CREATE FUNCTION gin_compare_prefix_inet(inet, inet, int2, internal) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_extract_query_inet(inet, internal, int2, internal, internal) +CREATE FUNCTION gin_extract_query_inet(inet, internal, int2, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -458,17 +455,17 @@ AS FUNCTION 5 gin_compare_prefix_inet(inet,inet,int2, internal), STORAGE inet; -CREATE OR REPLACE FUNCTION gin_extract_value_cidr(cidr, internal) +CREATE FUNCTION gin_extract_value_cidr(cidr, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_compare_prefix_cidr(cidr, cidr, int2, internal) +CREATE FUNCTION gin_compare_prefix_cidr(cidr, cidr, int2, internal) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_extract_query_cidr(cidr, internal, int2, internal, internal) +CREATE FUNCTION gin_extract_query_cidr(cidr, internal, int2, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -488,17 +485,17 @@ AS FUNCTION 5 gin_compare_prefix_cidr(cidr,cidr,int2, internal), STORAGE cidr; -CREATE OR REPLACE FUNCTION gin_extract_value_text(text, internal) +CREATE FUNCTION gin_extract_value_text(text, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_compare_prefix_text(text, text, int2, internal) +CREATE FUNCTION gin_compare_prefix_text(text, text, int2, internal) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_extract_query_text(text, internal, int2, internal, internal) +CREATE FUNCTION gin_extract_query_text(text, internal, int2, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -533,17 +530,17 @@ AS FUNCTION 5 gin_compare_prefix_text(text,text,int2, internal), STORAGE varchar; -CREATE OR REPLACE FUNCTION gin_extract_value_char("char", internal) +CREATE FUNCTION gin_extract_value_char("char", internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_compare_prefix_char("char", "char", int2, internal) +CREATE FUNCTION gin_compare_prefix_char("char", "char", int2, internal) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_extract_query_char("char", internal, int2, internal, internal) +CREATE FUNCTION gin_extract_query_char("char", internal, int2, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -563,17 +560,17 @@ AS FUNCTION 5 gin_compare_prefix_char("char","char",int2, internal), STORAGE "char"; -CREATE OR REPLACE FUNCTION gin_extract_value_bytea(bytea, internal) +CREATE FUNCTION gin_extract_value_bytea(bytea, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_compare_prefix_bytea(bytea, bytea, int2, internal) +CREATE FUNCTION gin_compare_prefix_bytea(bytea, bytea, int2, internal) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_extract_query_bytea(bytea, internal, int2, internal, internal) +CREATE FUNCTION gin_extract_query_bytea(bytea, internal, int2, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -593,17 +590,17 @@ AS FUNCTION 5 gin_compare_prefix_bytea(bytea,bytea,int2, internal), STORAGE bytea; -CREATE OR REPLACE FUNCTION gin_extract_value_bit(bit, internal) +CREATE FUNCTION gin_extract_value_bit(bit, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_compare_prefix_bit(bit, bit, int2, internal) +CREATE FUNCTION gin_compare_prefix_bit(bit, bit, int2, internal) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_extract_query_bit(bit, internal, int2, internal, internal) +CREATE FUNCTION gin_extract_query_bit(bit, internal, int2, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -623,17 +620,17 @@ AS FUNCTION 5 gin_compare_prefix_bit(bit,bit,int2, internal), STORAGE bit; -CREATE OR REPLACE FUNCTION gin_extract_value_varbit(varbit, internal) +CREATE FUNCTION gin_extract_value_varbit(varbit, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_compare_prefix_varbit(varbit, varbit, int2, internal) +CREATE FUNCTION gin_compare_prefix_varbit(varbit, varbit, int2, internal) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_extract_query_varbit(varbit, internal, int2, internal, internal) +CREATE FUNCTION gin_extract_query_varbit(varbit, internal, int2, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -653,22 +650,22 @@ AS FUNCTION 5 gin_compare_prefix_varbit(varbit,varbit,int2, internal), STORAGE varbit; -CREATE OR REPLACE FUNCTION gin_extract_value_numeric(numeric, internal) +CREATE FUNCTION gin_extract_value_numeric(numeric, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_compare_prefix_numeric(numeric, numeric, int2, internal) +CREATE FUNCTION gin_compare_prefix_numeric(numeric, numeric, int2, internal) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_extract_query_numeric(numeric, internal, int2, internal, internal) +CREATE FUNCTION gin_extract_query_numeric(numeric, internal, int2, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION gin_numeric_cmp(numeric, numeric) +CREATE FUNCTION gin_numeric_cmp(numeric, numeric) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; diff --git a/contrib/btree_gin/btree_gin--unpackaged--1.0.sql b/contrib/btree_gin/btree_gin--unpackaged--1.0.sql new file mode 100644 index 0000000000..fe1ddeab87 --- /dev/null +++ b/contrib/btree_gin/btree_gin--unpackaged--1.0.sql @@ -0,0 +1,116 @@ +/* contrib/btree_gin/btree_gin--unpackaged--1.0.sql */ + +ALTER EXTENSION btree_gin ADD function gin_btree_consistent(internal,smallint,anyelement,integer,internal,internal); +ALTER EXTENSION btree_gin ADD function gin_extract_value_int2(smallint,internal); +ALTER EXTENSION btree_gin ADD function gin_compare_prefix_int2(smallint,smallint,smallint,internal); +ALTER EXTENSION btree_gin ADD function gin_extract_query_int2(smallint,internal,smallint,internal,internal); +ALTER EXTENSION btree_gin ADD operator family int2_ops using gin; +ALTER EXTENSION btree_gin ADD operator class int2_ops using gin; +ALTER EXTENSION btree_gin ADD function gin_extract_value_int4(integer,internal); +ALTER EXTENSION btree_gin ADD function gin_compare_prefix_int4(integer,integer,smallint,internal); +ALTER EXTENSION btree_gin ADD function gin_extract_query_int4(integer,internal,smallint,internal,internal); +ALTER EXTENSION btree_gin ADD operator family int4_ops using gin; +ALTER EXTENSION btree_gin ADD operator class int4_ops using gin; +ALTER EXTENSION btree_gin ADD function gin_extract_value_int8(bigint,internal); +ALTER EXTENSION btree_gin ADD function gin_compare_prefix_int8(bigint,bigint,smallint,internal); +ALTER EXTENSION btree_gin ADD function gin_extract_query_int8(bigint,internal,smallint,internal,internal); +ALTER EXTENSION btree_gin ADD operator family int8_ops using gin; +ALTER EXTENSION btree_gin ADD operator class int8_ops using gin; +ALTER EXTENSION btree_gin ADD function gin_extract_value_float4(real,internal); +ALTER EXTENSION btree_gin ADD function gin_compare_prefix_float4(real,real,smallint,internal); +ALTER EXTENSION btree_gin ADD function gin_extract_query_float4(real,internal,smallint,internal,internal); +ALTER EXTENSION btree_gin ADD operator family float4_ops using gin; +ALTER EXTENSION btree_gin ADD operator class float4_ops using gin; +ALTER EXTENSION btree_gin ADD function gin_extract_value_float8(double precision,internal); +ALTER EXTENSION btree_gin ADD function gin_compare_prefix_float8(double precision,double precision,smallint,internal); +ALTER EXTENSION btree_gin ADD function gin_extract_query_float8(double precision,internal,smallint,internal,internal); +ALTER EXTENSION btree_gin ADD operator family float8_ops using gin; +ALTER EXTENSION btree_gin ADD operator class float8_ops using gin; +ALTER EXTENSION btree_gin ADD function gin_extract_value_money(money,internal); +ALTER EXTENSION btree_gin ADD function gin_compare_prefix_money(money,money,smallint,internal); +ALTER EXTENSION btree_gin ADD function gin_extract_query_money(money,internal,smallint,internal,internal); +ALTER EXTENSION btree_gin ADD operator family money_ops using gin; +ALTER EXTENSION btree_gin ADD operator class money_ops using gin; +ALTER EXTENSION btree_gin ADD function gin_extract_value_oid(oid,internal); +ALTER EXTENSION btree_gin ADD function gin_compare_prefix_oid(oid,oid,smallint,internal); +ALTER EXTENSION btree_gin ADD function gin_extract_query_oid(oid,internal,smallint,internal,internal); +ALTER EXTENSION btree_gin ADD operator family oid_ops using gin; +ALTER EXTENSION btree_gin ADD operator class oid_ops using gin; +ALTER EXTENSION btree_gin ADD function gin_extract_value_timestamp(timestamp without time zone,internal); +ALTER EXTENSION btree_gin ADD function gin_compare_prefix_timestamp(timestamp without time zone,timestamp without time zone,smallint,internal); +ALTER EXTENSION btree_gin ADD function gin_extract_query_timestamp(timestamp without time zone,internal,smallint,internal,internal); +ALTER EXTENSION btree_gin ADD operator family timestamp_ops using gin; +ALTER EXTENSION btree_gin ADD operator class timestamp_ops using gin; +ALTER EXTENSION btree_gin ADD function gin_extract_value_timestamptz(timestamp with time zone,internal); +ALTER EXTENSION btree_gin ADD function gin_compare_prefix_timestamptz(timestamp with time zone,timestamp with time zone,smallint,internal); +ALTER EXTENSION btree_gin ADD function gin_extract_query_timestamptz(timestamp with time zone,internal,smallint,internal,internal); +ALTER EXTENSION btree_gin ADD operator family timestamptz_ops using gin; +ALTER EXTENSION btree_gin ADD operator class timestamptz_ops using gin; +ALTER EXTENSION btree_gin ADD function gin_extract_value_time(time without time zone,internal); +ALTER EXTENSION btree_gin ADD function gin_compare_prefix_time(time without time zone,time without time zone,smallint,internal); +ALTER EXTENSION btree_gin ADD function gin_extract_query_time(time without time zone,internal,smallint,internal,internal); +ALTER EXTENSION btree_gin ADD operator family time_ops using gin; +ALTER EXTENSION btree_gin ADD operator class time_ops using gin; +ALTER EXTENSION btree_gin ADD function gin_extract_value_timetz(time with time zone,internal); +ALTER EXTENSION btree_gin ADD function gin_compare_prefix_timetz(time with time zone,time with time zone,smallint,internal); +ALTER EXTENSION btree_gin ADD function gin_extract_query_timetz(time with time zone,internal,smallint,internal,internal); +ALTER EXTENSION btree_gin ADD operator family timetz_ops using gin; +ALTER EXTENSION btree_gin ADD operator class timetz_ops using gin; +ALTER EXTENSION btree_gin ADD function gin_extract_value_date(date,internal); +ALTER EXTENSION btree_gin ADD function gin_compare_prefix_date(date,date,smallint,internal); +ALTER EXTENSION btree_gin ADD function gin_extract_query_date(date,internal,smallint,internal,internal); +ALTER EXTENSION btree_gin ADD operator family date_ops using gin; +ALTER EXTENSION btree_gin ADD operator class date_ops using gin; +ALTER EXTENSION btree_gin ADD function gin_extract_value_interval(interval,internal); +ALTER EXTENSION btree_gin ADD function gin_compare_prefix_interval(interval,interval,smallint,internal); +ALTER EXTENSION btree_gin ADD function gin_extract_query_interval(interval,internal,smallint,internal,internal); +ALTER EXTENSION btree_gin ADD operator family interval_ops using gin; +ALTER EXTENSION btree_gin ADD operator class interval_ops using gin; +ALTER EXTENSION btree_gin ADD function gin_extract_value_macaddr(macaddr,internal); +ALTER EXTENSION btree_gin ADD function gin_compare_prefix_macaddr(macaddr,macaddr,smallint,internal); +ALTER EXTENSION btree_gin ADD function gin_extract_query_macaddr(macaddr,internal,smallint,internal,internal); +ALTER EXTENSION btree_gin ADD operator family macaddr_ops using gin; +ALTER EXTENSION btree_gin ADD operator class macaddr_ops using gin; +ALTER EXTENSION btree_gin ADD function gin_extract_value_inet(inet,internal); +ALTER EXTENSION btree_gin ADD function gin_compare_prefix_inet(inet,inet,smallint,internal); +ALTER EXTENSION btree_gin ADD function gin_extract_query_inet(inet,internal,smallint,internal,internal); +ALTER EXTENSION btree_gin ADD operator family inet_ops using gin; +ALTER EXTENSION btree_gin ADD operator class inet_ops using gin; +ALTER EXTENSION btree_gin ADD function gin_extract_value_cidr(cidr,internal); +ALTER EXTENSION btree_gin ADD function gin_compare_prefix_cidr(cidr,cidr,smallint,internal); +ALTER EXTENSION btree_gin ADD function gin_extract_query_cidr(cidr,internal,smallint,internal,internal); +ALTER EXTENSION btree_gin ADD operator family cidr_ops using gin; +ALTER EXTENSION btree_gin ADD operator class cidr_ops using gin; +ALTER EXTENSION btree_gin ADD function gin_extract_value_text(text,internal); +ALTER EXTENSION btree_gin ADD function gin_compare_prefix_text(text,text,smallint,internal); +ALTER EXTENSION btree_gin ADD function gin_extract_query_text(text,internal,smallint,internal,internal); +ALTER EXTENSION btree_gin ADD operator family text_ops using gin; +ALTER EXTENSION btree_gin ADD operator class text_ops using gin; +ALTER EXTENSION btree_gin ADD operator family varchar_ops using gin; +ALTER EXTENSION btree_gin ADD operator class varchar_ops using gin; +ALTER EXTENSION btree_gin ADD function gin_extract_value_char("char",internal); +ALTER EXTENSION btree_gin ADD function gin_compare_prefix_char("char","char",smallint,internal); +ALTER EXTENSION btree_gin ADD function gin_extract_query_char("char",internal,smallint,internal,internal); +ALTER EXTENSION btree_gin ADD operator family char_ops using gin; +ALTER EXTENSION btree_gin ADD operator class char_ops using gin; +ALTER EXTENSION btree_gin ADD function gin_extract_value_bytea(bytea,internal); +ALTER EXTENSION btree_gin ADD function gin_compare_prefix_bytea(bytea,bytea,smallint,internal); +ALTER EXTENSION btree_gin ADD function gin_extract_query_bytea(bytea,internal,smallint,internal,internal); +ALTER EXTENSION btree_gin ADD operator family bytea_ops using gin; +ALTER EXTENSION btree_gin ADD operator class bytea_ops using gin; +ALTER EXTENSION btree_gin ADD function gin_extract_value_bit(bit,internal); +ALTER EXTENSION btree_gin ADD function gin_compare_prefix_bit(bit,bit,smallint,internal); +ALTER EXTENSION btree_gin ADD function gin_extract_query_bit(bit,internal,smallint,internal,internal); +ALTER EXTENSION btree_gin ADD operator family bit_ops using gin; +ALTER EXTENSION btree_gin ADD operator class bit_ops using gin; +ALTER EXTENSION btree_gin ADD function gin_extract_value_varbit(bit varying,internal); +ALTER EXTENSION btree_gin ADD function gin_compare_prefix_varbit(bit varying,bit varying,smallint,internal); +ALTER EXTENSION btree_gin ADD function gin_extract_query_varbit(bit varying,internal,smallint,internal,internal); +ALTER EXTENSION btree_gin ADD operator family varbit_ops using gin; +ALTER EXTENSION btree_gin ADD operator class varbit_ops using gin; +ALTER EXTENSION btree_gin ADD function gin_extract_value_numeric(numeric,internal); +ALTER EXTENSION btree_gin ADD function gin_compare_prefix_numeric(numeric,numeric,smallint,internal); +ALTER EXTENSION btree_gin ADD function gin_extract_query_numeric(numeric,internal,smallint,internal,internal); +ALTER EXTENSION btree_gin ADD function gin_numeric_cmp(numeric,numeric); +ALTER EXTENSION btree_gin ADD operator family numeric_ops using gin; +ALTER EXTENSION btree_gin ADD operator class numeric_ops using gin; diff --git a/contrib/btree_gin/btree_gin.c b/contrib/btree_gin/btree_gin.c index 486a662c78..144f6db184 100644 --- a/contrib/btree_gin/btree_gin.c +++ b/contrib/btree_gin/btree_gin.c @@ -7,6 +7,7 @@ #include "fmgr.h" #include "access/skey.h" +#include "catalog/pg_collation.h" #include "utils/builtins.h" #include "utils/bytea.h" #include "utils/cash.h" @@ -120,8 +121,9 @@ gin_compare_prefix_##type(PG_FUNCTION_ARGS) \ int32 res, \ cmp; \ \ - cmp = DatumGetInt32(DirectFunctionCall2( \ + cmp = DatumGetInt32(DirectFunctionCall2WithCollation( \ TypeInfo_##type.typecmp, \ + DEFAULT_COLLATION_OID, \ (data->strategy == BTLessStrategyNumber || \ data->strategy == BTLessEqualStrategyNumber) \ ? data->datum : a, \ diff --git a/contrib/btree_gin/btree_gin.control b/contrib/btree_gin/btree_gin.control new file mode 100644 index 0000000000..3b2cb2d709 --- /dev/null +++ b/contrib/btree_gin/btree_gin.control @@ -0,0 +1,5 @@ +# btree_gin extension +comment = 'support for indexing common datatypes in GIN' +default_version = '1.0' +module_pathname = '$libdir/btree_gin' +relocatable = true diff --git a/contrib/btree_gin/expected/install_btree_gin.out b/contrib/btree_gin/expected/install_btree_gin.out index 43f11fa263..0fae4c5bfe 100644 --- a/contrib/btree_gin/expected/install_btree_gin.out +++ b/contrib/btree_gin/expected/install_btree_gin.out @@ -1,3 +1 @@ -SET client_min_messages = warning; -\set ECHO none -RESET client_min_messages; +CREATE EXTENSION btree_gin; diff --git a/contrib/btree_gin/sql/install_btree_gin.sql b/contrib/btree_gin/sql/install_btree_gin.sql index f54c8b4a0f..0fae4c5bfe 100644 --- a/contrib/btree_gin/sql/install_btree_gin.sql +++ b/contrib/btree_gin/sql/install_btree_gin.sql @@ -1,5 +1 @@ -SET client_min_messages = warning; -\set ECHO none -\i btree_gin.sql -\set ECHO all -RESET client_min_messages; +CREATE EXTENSION btree_gin; diff --git a/contrib/btree_gin/uninstall_btree_gin.sql b/contrib/btree_gin/uninstall_btree_gin.sql deleted file mode 100644 index 30324dc709..0000000000 --- a/contrib/btree_gin/uninstall_btree_gin.sql +++ /dev/null @@ -1,98 +0,0 @@ -/* contrib/btree_gin/uninstall_btree_gin.sql */ - --- Adjust this setting to control where the objects get dropped. -SET search_path = public; - -DROP OPERATOR FAMILY int2_ops USING gin CASCADE; -DROP OPERATOR FAMILY int4_ops USING gin CASCADE; -DROP OPERATOR FAMILY int8_ops USING gin CASCADE; -DROP OPERATOR FAMILY float4_ops USING gin CASCADE; -DROP OPERATOR FAMILY float8_ops USING gin CASCADE; -DROP OPERATOR FAMILY money_ops USING gin CASCADE; -DROP OPERATOR FAMILY oid_ops USING gin CASCADE; -DROP OPERATOR FAMILY timestamp_ops USING gin CASCADE; -DROP OPERATOR FAMILY timestamptz_ops USING gin CASCADE; -DROP OPERATOR FAMILY time_ops USING gin CASCADE; -DROP OPERATOR FAMILY timetz_ops USING gin CASCADE; -DROP OPERATOR FAMILY date_ops USING gin CASCADE; -DROP OPERATOR FAMILY interval_ops USING gin CASCADE; -DROP OPERATOR FAMILY macaddr_ops USING gin CASCADE; -DROP OPERATOR FAMILY inet_ops USING gin CASCADE; -DROP OPERATOR FAMILY cidr_ops USING gin CASCADE; -DROP OPERATOR FAMILY text_ops USING gin CASCADE; -DROP OPERATOR FAMILY varchar_ops USING gin CASCADE; -DROP OPERATOR FAMILY char_ops USING gin CASCADE; -DROP OPERATOR FAMILY bytea_ops USING gin CASCADE; -DROP OPERATOR FAMILY bit_ops USING gin CASCADE; -DROP OPERATOR FAMILY varbit_ops USING gin CASCADE; -DROP OPERATOR FAMILY numeric_ops USING gin CASCADE; - -DROP FUNCTION gin_btree_consistent(internal, int2, anyelement, int4, internal, internal); - -DROP FUNCTION gin_extract_value_int2(int2, internal); -DROP FUNCTION gin_compare_prefix_int2(int2, int2, int2, internal); -DROP FUNCTION gin_extract_query_int2(int2, internal, int2, internal, internal); -DROP FUNCTION gin_extract_value_int4(int4, internal); -DROP FUNCTION gin_compare_prefix_int4(int4, int4, int2, internal); -DROP FUNCTION gin_extract_query_int4(int4, internal, int2, internal, internal); -DROP FUNCTION gin_extract_value_int8(int8, internal); -DROP FUNCTION gin_compare_prefix_int8(int8, int8, int2, internal); -DROP FUNCTION gin_extract_query_int8(int8, internal, int2, internal, internal); -DROP FUNCTION gin_extract_value_float4(float4, internal); -DROP FUNCTION gin_compare_prefix_float4(float4, float4, int2, internal); -DROP FUNCTION gin_extract_query_float4(float4, internal, int2, internal, internal); -DROP FUNCTION gin_extract_value_float8(float8, internal); -DROP FUNCTION gin_compare_prefix_float8(float8, float8, int2, internal); -DROP FUNCTION gin_extract_query_float8(float8, internal, int2, internal, internal); -DROP FUNCTION gin_extract_value_money(money, internal); -DROP FUNCTION gin_compare_prefix_money(money, money, int2, internal); -DROP FUNCTION gin_extract_query_money(money, internal, int2, internal, internal); -DROP FUNCTION gin_extract_value_oid(oid, internal); -DROP FUNCTION gin_compare_prefix_oid(oid, oid, int2, internal); -DROP FUNCTION gin_extract_query_oid(oid, internal, int2, internal, internal); -DROP FUNCTION gin_extract_value_timestamp(timestamp, internal); -DROP FUNCTION gin_compare_prefix_timestamp(timestamp, timestamp, int2, internal); -DROP FUNCTION gin_extract_query_timestamp(timestamp, internal, int2, internal, internal); -DROP FUNCTION gin_extract_value_timestamptz(timestamptz, internal); -DROP FUNCTION gin_compare_prefix_timestamptz(timestamptz, timestamptz, int2, internal); -DROP FUNCTION gin_extract_query_timestamptz(timestamptz, internal, int2, internal, internal); -DROP FUNCTION gin_extract_value_time(time, internal); -DROP FUNCTION gin_compare_prefix_time(time, time, int2, internal); -DROP FUNCTION gin_extract_query_time(time, internal, int2, internal, internal); -DROP FUNCTION gin_extract_value_timetz(timetz, internal); -DROP FUNCTION gin_compare_prefix_timetz(timetz, timetz, int2, internal); -DROP FUNCTION gin_extract_query_timetz(timetz, internal, int2, internal, internal); -DROP FUNCTION gin_extract_value_date(date, internal); -DROP FUNCTION gin_compare_prefix_date(date, date, int2, internal); -DROP FUNCTION gin_extract_query_date(date, internal, int2, internal, internal); -DROP FUNCTION gin_extract_value_interval(interval, internal); -DROP FUNCTION gin_compare_prefix_interval(interval, interval, int2, internal); -DROP FUNCTION gin_extract_query_interval(interval, internal, int2, internal, internal); -DROP FUNCTION gin_extract_value_macaddr(macaddr, internal); -DROP FUNCTION gin_compare_prefix_macaddr(macaddr, macaddr, int2, internal); -DROP FUNCTION gin_extract_query_macaddr(macaddr, internal, int2, internal, internal); -DROP FUNCTION gin_extract_value_inet(inet, internal); -DROP FUNCTION gin_compare_prefix_inet(inet, inet, int2, internal); -DROP FUNCTION gin_extract_query_inet(inet, internal, int2, internal, internal); -DROP FUNCTION gin_extract_value_cidr(cidr, internal); -DROP FUNCTION gin_compare_prefix_cidr(cidr, cidr, int2, internal); -DROP FUNCTION gin_extract_query_cidr(cidr, internal, int2, internal, internal); -DROP FUNCTION gin_extract_value_text(text, internal); -DROP FUNCTION gin_compare_prefix_text(text, text, int2, internal); -DROP FUNCTION gin_extract_query_text(text, internal, int2, internal, internal); -DROP FUNCTION gin_extract_value_char("char", internal); -DROP FUNCTION gin_compare_prefix_char("char", "char", int2, internal); -DROP FUNCTION gin_extract_query_char("char", internal, int2, internal, internal); -DROP FUNCTION gin_extract_value_bytea(bytea, internal); -DROP FUNCTION gin_compare_prefix_bytea(bytea, bytea, int2, internal); -DROP FUNCTION gin_extract_query_bytea(bytea, internal, int2, internal, internal); -DROP FUNCTION gin_extract_value_bit(bit, internal); -DROP FUNCTION gin_compare_prefix_bit(bit, bit, int2, internal); -DROP FUNCTION gin_extract_query_bit(bit, internal, int2, internal, internal); -DROP FUNCTION gin_extract_value_varbit(varbit, internal); -DROP FUNCTION gin_compare_prefix_varbit(varbit, varbit, int2, internal); -DROP FUNCTION gin_extract_query_varbit(varbit, internal, int2, internal, internal); -DROP FUNCTION gin_extract_value_numeric(numeric, internal); -DROP FUNCTION gin_compare_prefix_numeric(numeric, numeric, int2, internal); -DROP FUNCTION gin_extract_query_numeric(numeric, internal, int2, internal, internal); -DROP FUNCTION gin_numeric_cmp(numeric, numeric); diff --git a/contrib/btree_gist/.gitignore b/contrib/btree_gist/.gitignore index 46318eaa7b..19b6c5ba42 100644 --- a/contrib/btree_gist/.gitignore +++ b/contrib/btree_gist/.gitignore @@ -1,3 +1,2 @@ -/btree_gist.sql # Generated subdirectories /results/ diff --git a/contrib/btree_gist/Makefile b/contrib/btree_gist/Makefile index e152cd881d..4b931de08a 100644 --- a/contrib/btree_gist/Makefile +++ b/contrib/btree_gist/Makefile @@ -7,8 +7,8 @@ OBJS = btree_gist.o btree_utils_num.o btree_utils_var.o btree_int2.o btre btree_date.o btree_interval.o btree_macaddr.o btree_inet.o btree_text.o \ btree_bytea.o btree_bit.o btree_numeric.o -DATA_built = btree_gist.sql -DATA = uninstall_btree_gist.sql +EXTENSION = btree_gist +DATA = btree_gist--1.0.sql btree_gist--unpackaged--1.0.sql REGRESS = init int2 int4 int8 float4 float8 cash oid timestamp timestamptz time timetz \ date interval macaddr inet cidr text varchar char bytea bit varbit numeric not_equal diff --git a/contrib/btree_gist/btree_gist.sql.in b/contrib/btree_gist/btree_gist--1.0.sql index 01cd30f2de..f1866f25ed 100644 --- a/contrib/btree_gist/btree_gist.sql.in +++ b/contrib/btree_gist/btree_gist--1.0.sql @@ -1,14 +1,11 @@ -/* contrib/btree_gist/btree_gist.sql.in */ +/* contrib/btree_gist/btree_gist--1.0.sql */ --- Adjust this setting to control where the objects get created. -SET search_path = public; - -CREATE OR REPLACE FUNCTION gbtreekey4_in(cstring) +CREATE FUNCTION gbtreekey4_in(cstring) RETURNS gbtreekey4 AS 'MODULE_PATHNAME', 'gbtreekey_in' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbtreekey4_out(gbtreekey4) +CREATE FUNCTION gbtreekey4_out(gbtreekey4) RETURNS cstring AS 'MODULE_PATHNAME', 'gbtreekey_out' LANGUAGE C IMMUTABLE STRICT; @@ -19,12 +16,12 @@ CREATE TYPE gbtreekey4 ( OUTPUT = gbtreekey4_out ); -CREATE OR REPLACE FUNCTION gbtreekey8_in(cstring) +CREATE FUNCTION gbtreekey8_in(cstring) RETURNS gbtreekey8 AS 'MODULE_PATHNAME', 'gbtreekey_in' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbtreekey8_out(gbtreekey8) +CREATE FUNCTION gbtreekey8_out(gbtreekey8) RETURNS cstring AS 'MODULE_PATHNAME', 'gbtreekey_out' LANGUAGE C IMMUTABLE STRICT; @@ -35,12 +32,12 @@ CREATE TYPE gbtreekey8 ( OUTPUT = gbtreekey8_out ); -CREATE OR REPLACE FUNCTION gbtreekey16_in(cstring) +CREATE FUNCTION gbtreekey16_in(cstring) RETURNS gbtreekey16 AS 'MODULE_PATHNAME', 'gbtreekey_in' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbtreekey16_out(gbtreekey16) +CREATE FUNCTION gbtreekey16_out(gbtreekey16) RETURNS cstring AS 'MODULE_PATHNAME', 'gbtreekey_out' LANGUAGE C IMMUTABLE STRICT; @@ -51,12 +48,12 @@ CREATE TYPE gbtreekey16 ( OUTPUT = gbtreekey16_out ); -CREATE OR REPLACE FUNCTION gbtreekey32_in(cstring) +CREATE FUNCTION gbtreekey32_in(cstring) RETURNS gbtreekey32 AS 'MODULE_PATHNAME', 'gbtreekey_in' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbtreekey32_out(gbtreekey32) +CREATE FUNCTION gbtreekey32_out(gbtreekey32) RETURNS cstring AS 'MODULE_PATHNAME', 'gbtreekey_out' LANGUAGE C IMMUTABLE STRICT; @@ -67,12 +64,12 @@ CREATE TYPE gbtreekey32 ( OUTPUT = gbtreekey32_out ); -CREATE OR REPLACE FUNCTION gbtreekey_var_in(cstring) +CREATE FUNCTION gbtreekey_var_in(cstring) RETURNS gbtreekey_var AS 'MODULE_PATHNAME', 'gbtreekey_in' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbtreekey_var_out(gbtreekey_var) +CREATE FUNCTION gbtreekey_var_out(gbtreekey_var) RETURNS cstring AS 'MODULE_PATHNAME', 'gbtreekey_out' LANGUAGE C IMMUTABLE STRICT; @@ -94,42 +91,42 @@ CREATE TYPE gbtreekey_var ( -- -- -- define the GiST support methods -CREATE OR REPLACE FUNCTION gbt_oid_consistent(internal,oid,int2,oid,internal) +CREATE FUNCTION gbt_oid_consistent(internal,oid,int2,oid,internal) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_oid_compress(internal) +CREATE FUNCTION gbt_oid_compress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_decompress(internal) +CREATE FUNCTION gbt_decompress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_var_decompress(internal) +CREATE FUNCTION gbt_var_decompress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_oid_penalty(internal,internal,internal) +CREATE FUNCTION gbt_oid_penalty(internal,internal,internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_oid_picksplit(internal, internal) +CREATE FUNCTION gbt_oid_picksplit(internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_oid_union(bytea, internal) +CREATE FUNCTION gbt_oid_union(bytea, internal) RETURNS gbtreekey8 AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_oid_same(internal, internal, internal) +CREATE FUNCTION gbt_oid_same(internal, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; @@ -162,32 +159,32 @@ AS -- -- -- define the GiST support methods -CREATE OR REPLACE FUNCTION gbt_int2_consistent(internal,int2,int2,oid,internal) +CREATE FUNCTION gbt_int2_consistent(internal,int2,int2,oid,internal) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_int2_compress(internal) +CREATE FUNCTION gbt_int2_compress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_int2_penalty(internal,internal,internal) +CREATE FUNCTION gbt_int2_penalty(internal,internal,internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_int2_picksplit(internal, internal) +CREATE FUNCTION gbt_int2_picksplit(internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_int2_union(bytea, internal) +CREATE FUNCTION gbt_int2_union(bytea, internal) RETURNS gbtreekey4 AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_int2_same(internal, internal, internal) +CREATE FUNCTION gbt_int2_same(internal, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; @@ -219,32 +216,32 @@ AS -- -- -- define the GiST support methods -CREATE OR REPLACE FUNCTION gbt_int4_consistent(internal,int4,int2,oid,internal) +CREATE FUNCTION gbt_int4_consistent(internal,int4,int2,oid,internal) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_int4_compress(internal) +CREATE FUNCTION gbt_int4_compress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_int4_penalty(internal,internal,internal) +CREATE FUNCTION gbt_int4_penalty(internal,internal,internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_int4_picksplit(internal, internal) +CREATE FUNCTION gbt_int4_picksplit(internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_int4_union(bytea, internal) +CREATE FUNCTION gbt_int4_union(bytea, internal) RETURNS gbtreekey8 AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_int4_same(internal, internal, internal) +CREATE FUNCTION gbt_int4_same(internal, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; @@ -276,32 +273,32 @@ AS -- -- -- define the GiST support methods -CREATE OR REPLACE FUNCTION gbt_int8_consistent(internal,int8,int2,oid,internal) +CREATE FUNCTION gbt_int8_consistent(internal,int8,int2,oid,internal) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_int8_compress(internal) +CREATE FUNCTION gbt_int8_compress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_int8_penalty(internal,internal,internal) +CREATE FUNCTION gbt_int8_penalty(internal,internal,internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_int8_picksplit(internal, internal) +CREATE FUNCTION gbt_int8_picksplit(internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_int8_union(bytea, internal) +CREATE FUNCTION gbt_int8_union(bytea, internal) RETURNS gbtreekey16 AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_int8_same(internal, internal, internal) +CREATE FUNCTION gbt_int8_same(internal, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; @@ -334,32 +331,32 @@ AS -- -- -- define the GiST support methods -CREATE OR REPLACE FUNCTION gbt_float4_consistent(internal,float4,int2,oid,internal) +CREATE FUNCTION gbt_float4_consistent(internal,float4,int2,oid,internal) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_float4_compress(internal) +CREATE FUNCTION gbt_float4_compress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_float4_penalty(internal,internal,internal) +CREATE FUNCTION gbt_float4_penalty(internal,internal,internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_float4_picksplit(internal, internal) +CREATE FUNCTION gbt_float4_picksplit(internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_float4_union(bytea, internal) +CREATE FUNCTION gbt_float4_union(bytea, internal) RETURNS gbtreekey8 AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_float4_same(internal, internal, internal) +CREATE FUNCTION gbt_float4_same(internal, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; @@ -394,32 +391,32 @@ AS -- -- -- define the GiST support methods -CREATE OR REPLACE FUNCTION gbt_float8_consistent(internal,float8,int2,oid,internal) +CREATE FUNCTION gbt_float8_consistent(internal,float8,int2,oid,internal) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_float8_compress(internal) +CREATE FUNCTION gbt_float8_compress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_float8_penalty(internal,internal,internal) +CREATE FUNCTION gbt_float8_penalty(internal,internal,internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_float8_picksplit(internal, internal) +CREATE FUNCTION gbt_float8_picksplit(internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_float8_union(bytea, internal) +CREATE FUNCTION gbt_float8_union(bytea, internal) RETURNS gbtreekey16 AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_float8_same(internal, internal, internal) +CREATE FUNCTION gbt_float8_same(internal, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; @@ -452,42 +449,42 @@ AS -- -- -CREATE OR REPLACE FUNCTION gbt_ts_consistent(internal,timestamp,int2,oid,internal) +CREATE FUNCTION gbt_ts_consistent(internal,timestamp,int2,oid,internal) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_tstz_consistent(internal,timestamptz,int2,oid,internal) +CREATE FUNCTION gbt_tstz_consistent(internal,timestamptz,int2,oid,internal) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_ts_compress(internal) +CREATE FUNCTION gbt_ts_compress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_tstz_compress(internal) +CREATE FUNCTION gbt_tstz_compress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_ts_penalty(internal,internal,internal) +CREATE FUNCTION gbt_ts_penalty(internal,internal,internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_ts_picksplit(internal, internal) +CREATE FUNCTION gbt_ts_picksplit(internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_ts_union(bytea, internal) +CREATE FUNCTION gbt_ts_union(bytea, internal) RETURNS gbtreekey16 AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_ts_same(internal, internal, internal) +CREATE FUNCTION gbt_ts_same(internal, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; @@ -540,42 +537,42 @@ AS -- -- -CREATE OR REPLACE FUNCTION gbt_time_consistent(internal,time,int2,oid,internal) +CREATE FUNCTION gbt_time_consistent(internal,time,int2,oid,internal) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_timetz_consistent(internal,timetz,int2,oid,internal) +CREATE FUNCTION gbt_timetz_consistent(internal,timetz,int2,oid,internal) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_time_compress(internal) +CREATE FUNCTION gbt_time_compress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_timetz_compress(internal) +CREATE FUNCTION gbt_timetz_compress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_time_penalty(internal,internal,internal) +CREATE FUNCTION gbt_time_penalty(internal,internal,internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_time_picksplit(internal, internal) +CREATE FUNCTION gbt_time_picksplit(internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_time_union(bytea, internal) +CREATE FUNCTION gbt_time_union(bytea, internal) RETURNS gbtreekey16 AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_time_same(internal, internal, internal) +CREATE FUNCTION gbt_time_same(internal, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; @@ -626,32 +623,32 @@ AS -- -- -CREATE OR REPLACE FUNCTION gbt_date_consistent(internal,date,int2,oid,internal) +CREATE FUNCTION gbt_date_consistent(internal,date,int2,oid,internal) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_date_compress(internal) +CREATE FUNCTION gbt_date_compress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_date_penalty(internal,internal,internal) +CREATE FUNCTION gbt_date_penalty(internal,internal,internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_date_picksplit(internal, internal) +CREATE FUNCTION gbt_date_picksplit(internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_date_union(bytea, internal) +CREATE FUNCTION gbt_date_union(bytea, internal) RETURNS gbtreekey8 AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_date_same(internal, internal, internal) +CREATE FUNCTION gbt_date_same(internal, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; @@ -684,37 +681,37 @@ AS -- -- -CREATE OR REPLACE FUNCTION gbt_intv_consistent(internal,interval,int2,oid,internal) +CREATE FUNCTION gbt_intv_consistent(internal,interval,int2,oid,internal) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_intv_compress(internal) +CREATE FUNCTION gbt_intv_compress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_intv_decompress(internal) +CREATE FUNCTION gbt_intv_decompress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_intv_penalty(internal,internal,internal) +CREATE FUNCTION gbt_intv_penalty(internal,internal,internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_intv_picksplit(internal, internal) +CREATE FUNCTION gbt_intv_picksplit(internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_intv_union(bytea, internal) +CREATE FUNCTION gbt_intv_union(bytea, internal) RETURNS gbtreekey32 AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_intv_same(internal, internal, internal) +CREATE FUNCTION gbt_intv_same(internal, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; @@ -746,32 +743,32 @@ AS -- -- -- define the GiST support methods -CREATE OR REPLACE FUNCTION gbt_cash_consistent(internal,money,int2,oid,internal) +CREATE FUNCTION gbt_cash_consistent(internal,money,int2,oid,internal) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_cash_compress(internal) +CREATE FUNCTION gbt_cash_compress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_cash_penalty(internal,internal,internal) +CREATE FUNCTION gbt_cash_penalty(internal,internal,internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_cash_picksplit(internal, internal) +CREATE FUNCTION gbt_cash_picksplit(internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_cash_union(bytea, internal) +CREATE FUNCTION gbt_cash_union(bytea, internal) RETURNS gbtreekey8 AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_cash_same(internal, internal, internal) +CREATE FUNCTION gbt_cash_same(internal, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; @@ -803,32 +800,32 @@ AS -- -- -- define the GiST support methods -CREATE OR REPLACE FUNCTION gbt_macad_consistent(internal,macaddr,int2,oid,internal) +CREATE FUNCTION gbt_macad_consistent(internal,macaddr,int2,oid,internal) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_macad_compress(internal) +CREATE FUNCTION gbt_macad_compress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_macad_penalty(internal,internal,internal) +CREATE FUNCTION gbt_macad_penalty(internal,internal,internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_macad_picksplit(internal, internal) +CREATE FUNCTION gbt_macad_picksplit(internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_macad_union(bytea, internal) +CREATE FUNCTION gbt_macad_union(bytea, internal) RETURNS gbtreekey16 AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_macad_same(internal, internal, internal) +CREATE FUNCTION gbt_macad_same(internal, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; @@ -862,42 +859,42 @@ AS -- -- -- define the GiST support methods -CREATE OR REPLACE FUNCTION gbt_text_consistent(internal,text,int2,oid,internal) +CREATE FUNCTION gbt_text_consistent(internal,text,int2,oid,internal) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_bpchar_consistent(internal,bpchar,int2,oid,internal) +CREATE FUNCTION gbt_bpchar_consistent(internal,bpchar,int2,oid,internal) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_text_compress(internal) +CREATE FUNCTION gbt_text_compress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_bpchar_compress(internal) +CREATE FUNCTION gbt_bpchar_compress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_text_penalty(internal,internal,internal) +CREATE FUNCTION gbt_text_penalty(internal,internal,internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_text_picksplit(internal, internal) +CREATE FUNCTION gbt_text_picksplit(internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_text_union(bytea, internal) +CREATE FUNCTION gbt_text_union(bytea, internal) RETURNS gbtreekey_var AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_text_same(internal, internal, internal) +CREATE FUNCTION gbt_text_same(internal, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; @@ -950,32 +947,32 @@ AS -- -- -- define the GiST support methods -CREATE OR REPLACE FUNCTION gbt_bytea_consistent(internal,bytea,int2,oid,internal) +CREATE FUNCTION gbt_bytea_consistent(internal,bytea,int2,oid,internal) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_bytea_compress(internal) +CREATE FUNCTION gbt_bytea_compress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_bytea_penalty(internal,internal,internal) +CREATE FUNCTION gbt_bytea_penalty(internal,internal,internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_bytea_picksplit(internal, internal) +CREATE FUNCTION gbt_bytea_picksplit(internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_bytea_union(bytea, internal) +CREATE FUNCTION gbt_bytea_union(bytea, internal) RETURNS gbtreekey_var AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_bytea_same(internal, internal, internal) +CREATE FUNCTION gbt_bytea_same(internal, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; @@ -1008,32 +1005,32 @@ AS -- -- -- define the GiST support methods -CREATE OR REPLACE FUNCTION gbt_numeric_consistent(internal,numeric,int2,oid,internal) +CREATE FUNCTION gbt_numeric_consistent(internal,numeric,int2,oid,internal) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_numeric_compress(internal) +CREATE FUNCTION gbt_numeric_compress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_numeric_penalty(internal,internal,internal) +CREATE FUNCTION gbt_numeric_penalty(internal,internal,internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_numeric_picksplit(internal, internal) +CREATE FUNCTION gbt_numeric_picksplit(internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_numeric_union(bytea, internal) +CREATE FUNCTION gbt_numeric_union(bytea, internal) RETURNS gbtreekey_var AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_numeric_same(internal, internal, internal) +CREATE FUNCTION gbt_numeric_same(internal, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; @@ -1064,32 +1061,32 @@ AS -- -- -- define the GiST support methods -CREATE OR REPLACE FUNCTION gbt_bit_consistent(internal,bit,int2,oid,internal) +CREATE FUNCTION gbt_bit_consistent(internal,bit,int2,oid,internal) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_bit_compress(internal) +CREATE FUNCTION gbt_bit_compress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_bit_penalty(internal,internal,internal) +CREATE FUNCTION gbt_bit_penalty(internal,internal,internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_bit_picksplit(internal, internal) +CREATE FUNCTION gbt_bit_picksplit(internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_bit_union(bytea, internal) +CREATE FUNCTION gbt_bit_union(bytea, internal) RETURNS gbtreekey_var AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_bit_same(internal, internal, internal) +CREATE FUNCTION gbt_bit_same(internal, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; @@ -1143,32 +1140,32 @@ AS -- -- -- define the GiST support methods -CREATE OR REPLACE FUNCTION gbt_inet_consistent(internal,inet,int2,oid,internal) +CREATE FUNCTION gbt_inet_consistent(internal,inet,int2,oid,internal) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_inet_compress(internal) +CREATE FUNCTION gbt_inet_compress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_inet_penalty(internal,internal,internal) +CREATE FUNCTION gbt_inet_penalty(internal,internal,internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_inet_picksplit(internal, internal) +CREATE FUNCTION gbt_inet_picksplit(internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_inet_union(bytea, internal) +CREATE FUNCTION gbt_inet_union(bytea, internal) RETURNS gbtreekey16 AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gbt_inet_same(internal, internal, internal) +CREATE FUNCTION gbt_inet_same(internal, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; diff --git a/contrib/btree_gist/btree_gist--unpackaged--1.0.sql b/contrib/btree_gist/btree_gist--unpackaged--1.0.sql new file mode 100644 index 0000000000..18c1464991 --- /dev/null +++ b/contrib/btree_gist/btree_gist--unpackaged--1.0.sql @@ -0,0 +1,172 @@ +/* contrib/btree_gist/btree_gist--unpackaged--1.0.sql */ + +ALTER EXTENSION btree_gist ADD type gbtreekey4; +ALTER EXTENSION btree_gist ADD function gbtreekey4_in(cstring); +ALTER EXTENSION btree_gist ADD function gbtreekey4_out(gbtreekey4); +ALTER EXTENSION btree_gist ADD type gbtreekey8; +ALTER EXTENSION btree_gist ADD function gbtreekey8_in(cstring); +ALTER EXTENSION btree_gist ADD function gbtreekey8_out(gbtreekey8); +ALTER EXTENSION btree_gist ADD type gbtreekey16; +ALTER EXTENSION btree_gist ADD function gbtreekey16_in(cstring); +ALTER EXTENSION btree_gist ADD function gbtreekey16_out(gbtreekey16); +ALTER EXTENSION btree_gist ADD type gbtreekey32; +ALTER EXTENSION btree_gist ADD function gbtreekey32_in(cstring); +ALTER EXTENSION btree_gist ADD function gbtreekey32_out(gbtreekey32); +ALTER EXTENSION btree_gist ADD type gbtreekey_var; +ALTER EXTENSION btree_gist ADD function gbtreekey_var_in(cstring); +ALTER EXTENSION btree_gist ADD function gbtreekey_var_out(gbtreekey_var); +ALTER EXTENSION btree_gist ADD function gbt_oid_consistent(internal,oid,smallint,oid,internal); +ALTER EXTENSION btree_gist ADD function gbt_oid_compress(internal); +ALTER EXTENSION btree_gist ADD function gbt_decompress(internal); +ALTER EXTENSION btree_gist ADD function gbt_var_decompress(internal); +ALTER EXTENSION btree_gist ADD function gbt_oid_penalty(internal,internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_oid_picksplit(internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_oid_union(bytea,internal); +ALTER EXTENSION btree_gist ADD function gbt_oid_same(internal,internal,internal); +ALTER EXTENSION btree_gist ADD operator family gist_oid_ops using gist; +ALTER EXTENSION btree_gist ADD operator class gist_oid_ops using gist; +ALTER EXTENSION btree_gist ADD function gbt_int2_consistent(internal,smallint,smallint,oid,internal); +ALTER EXTENSION btree_gist ADD function gbt_int2_compress(internal); +ALTER EXTENSION btree_gist ADD function gbt_int2_penalty(internal,internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_int2_picksplit(internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_int2_union(bytea,internal); +ALTER EXTENSION btree_gist ADD function gbt_int2_same(internal,internal,internal); +ALTER EXTENSION btree_gist ADD operator family gist_int2_ops using gist; +ALTER EXTENSION btree_gist ADD operator class gist_int2_ops using gist; +ALTER EXTENSION btree_gist ADD function gbt_int4_consistent(internal,integer,smallint,oid,internal); +ALTER EXTENSION btree_gist ADD function gbt_int4_compress(internal); +ALTER EXTENSION btree_gist ADD function gbt_int4_penalty(internal,internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_int4_picksplit(internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_int4_union(bytea,internal); +ALTER EXTENSION btree_gist ADD function gbt_int4_same(internal,internal,internal); +ALTER EXTENSION btree_gist ADD operator family gist_int4_ops using gist; +ALTER EXTENSION btree_gist ADD operator class gist_int4_ops using gist; +ALTER EXTENSION btree_gist ADD function gbt_int8_consistent(internal,bigint,smallint,oid,internal); +ALTER EXTENSION btree_gist ADD function gbt_int8_compress(internal); +ALTER EXTENSION btree_gist ADD function gbt_int8_penalty(internal,internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_int8_picksplit(internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_int8_union(bytea,internal); +ALTER EXTENSION btree_gist ADD function gbt_int8_same(internal,internal,internal); +ALTER EXTENSION btree_gist ADD operator family gist_int8_ops using gist; +ALTER EXTENSION btree_gist ADD operator class gist_int8_ops using gist; +ALTER EXTENSION btree_gist ADD function gbt_float4_consistent(internal,real,smallint,oid,internal); +ALTER EXTENSION btree_gist ADD function gbt_float4_compress(internal); +ALTER EXTENSION btree_gist ADD function gbt_float4_penalty(internal,internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_float4_picksplit(internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_float4_union(bytea,internal); +ALTER EXTENSION btree_gist ADD function gbt_float4_same(internal,internal,internal); +ALTER EXTENSION btree_gist ADD operator family gist_float4_ops using gist; +ALTER EXTENSION btree_gist ADD operator class gist_float4_ops using gist; +ALTER EXTENSION btree_gist ADD function gbt_float8_consistent(internal,double precision,smallint,oid,internal); +ALTER EXTENSION btree_gist ADD function gbt_float8_compress(internal); +ALTER EXTENSION btree_gist ADD function gbt_float8_penalty(internal,internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_float8_picksplit(internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_float8_union(bytea,internal); +ALTER EXTENSION btree_gist ADD function gbt_float8_same(internal,internal,internal); +ALTER EXTENSION btree_gist ADD operator family gist_float8_ops using gist; +ALTER EXTENSION btree_gist ADD operator class gist_float8_ops using gist; +ALTER EXTENSION btree_gist ADD function gbt_ts_consistent(internal,timestamp without time zone,smallint,oid,internal); +ALTER EXTENSION btree_gist ADD function gbt_tstz_consistent(internal,timestamp with time zone,smallint,oid,internal); +ALTER EXTENSION btree_gist ADD function gbt_ts_compress(internal); +ALTER EXTENSION btree_gist ADD function gbt_tstz_compress(internal); +ALTER EXTENSION btree_gist ADD function gbt_ts_penalty(internal,internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_ts_picksplit(internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_ts_union(bytea,internal); +ALTER EXTENSION btree_gist ADD function gbt_ts_same(internal,internal,internal); +ALTER EXTENSION btree_gist ADD operator family gist_timestamp_ops using gist; +ALTER EXTENSION btree_gist ADD operator class gist_timestamp_ops using gist; +ALTER EXTENSION btree_gist ADD operator family gist_timestamptz_ops using gist; +ALTER EXTENSION btree_gist ADD operator class gist_timestamptz_ops using gist; +ALTER EXTENSION btree_gist ADD function gbt_time_consistent(internal,time without time zone,smallint,oid,internal); +ALTER EXTENSION btree_gist ADD function gbt_timetz_consistent(internal,time with time zone,smallint,oid,internal); +ALTER EXTENSION btree_gist ADD function gbt_time_compress(internal); +ALTER EXTENSION btree_gist ADD function gbt_timetz_compress(internal); +ALTER EXTENSION btree_gist ADD function gbt_time_penalty(internal,internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_time_picksplit(internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_time_union(bytea,internal); +ALTER EXTENSION btree_gist ADD function gbt_time_same(internal,internal,internal); +ALTER EXTENSION btree_gist ADD operator family gist_time_ops using gist; +ALTER EXTENSION btree_gist ADD operator class gist_time_ops using gist; +ALTER EXTENSION btree_gist ADD operator family gist_timetz_ops using gist; +ALTER EXTENSION btree_gist ADD operator class gist_timetz_ops using gist; +ALTER EXTENSION btree_gist ADD function gbt_date_consistent(internal,date,smallint,oid,internal); +ALTER EXTENSION btree_gist ADD function gbt_date_compress(internal); +ALTER EXTENSION btree_gist ADD function gbt_date_penalty(internal,internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_date_picksplit(internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_date_union(bytea,internal); +ALTER EXTENSION btree_gist ADD function gbt_date_same(internal,internal,internal); +ALTER EXTENSION btree_gist ADD operator family gist_date_ops using gist; +ALTER EXTENSION btree_gist ADD operator class gist_date_ops using gist; +ALTER EXTENSION btree_gist ADD function gbt_intv_consistent(internal,interval,smallint,oid,internal); +ALTER EXTENSION btree_gist ADD function gbt_intv_compress(internal); +ALTER EXTENSION btree_gist ADD function gbt_intv_decompress(internal); +ALTER EXTENSION btree_gist ADD function gbt_intv_penalty(internal,internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_intv_picksplit(internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_intv_union(bytea,internal); +ALTER EXTENSION btree_gist ADD function gbt_intv_same(internal,internal,internal); +ALTER EXTENSION btree_gist ADD operator family gist_interval_ops using gist; +ALTER EXTENSION btree_gist ADD operator class gist_interval_ops using gist; +ALTER EXTENSION btree_gist ADD function gbt_cash_consistent(internal,money,smallint,oid,internal); +ALTER EXTENSION btree_gist ADD function gbt_cash_compress(internal); +ALTER EXTENSION btree_gist ADD function gbt_cash_penalty(internal,internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_cash_picksplit(internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_cash_union(bytea,internal); +ALTER EXTENSION btree_gist ADD function gbt_cash_same(internal,internal,internal); +ALTER EXTENSION btree_gist ADD operator family gist_cash_ops using gist; +ALTER EXTENSION btree_gist ADD operator class gist_cash_ops using gist; +ALTER EXTENSION btree_gist ADD function gbt_macad_consistent(internal,macaddr,smallint,oid,internal); +ALTER EXTENSION btree_gist ADD function gbt_macad_compress(internal); +ALTER EXTENSION btree_gist ADD function gbt_macad_penalty(internal,internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_macad_picksplit(internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_macad_union(bytea,internal); +ALTER EXTENSION btree_gist ADD function gbt_macad_same(internal,internal,internal); +ALTER EXTENSION btree_gist ADD operator family gist_macaddr_ops using gist; +ALTER EXTENSION btree_gist ADD operator class gist_macaddr_ops using gist; +ALTER EXTENSION btree_gist ADD function gbt_text_consistent(internal,text,smallint,oid,internal); +ALTER EXTENSION btree_gist ADD function gbt_bpchar_consistent(internal,character,smallint,oid,internal); +ALTER EXTENSION btree_gist ADD function gbt_text_compress(internal); +ALTER EXTENSION btree_gist ADD function gbt_bpchar_compress(internal); +ALTER EXTENSION btree_gist ADD function gbt_text_penalty(internal,internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_text_picksplit(internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_text_union(bytea,internal); +ALTER EXTENSION btree_gist ADD function gbt_text_same(internal,internal,internal); +ALTER EXTENSION btree_gist ADD operator family gist_text_ops using gist; +ALTER EXTENSION btree_gist ADD operator class gist_text_ops using gist; +ALTER EXTENSION btree_gist ADD operator family gist_bpchar_ops using gist; +ALTER EXTENSION btree_gist ADD operator class gist_bpchar_ops using gist; +ALTER EXTENSION btree_gist ADD function gbt_bytea_consistent(internal,bytea,smallint,oid,internal); +ALTER EXTENSION btree_gist ADD function gbt_bytea_compress(internal); +ALTER EXTENSION btree_gist ADD function gbt_bytea_penalty(internal,internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_bytea_picksplit(internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_bytea_union(bytea,internal); +ALTER EXTENSION btree_gist ADD function gbt_bytea_same(internal,internal,internal); +ALTER EXTENSION btree_gist ADD operator family gist_bytea_ops using gist; +ALTER EXTENSION btree_gist ADD operator class gist_bytea_ops using gist; +ALTER EXTENSION btree_gist ADD function gbt_numeric_consistent(internal,numeric,smallint,oid,internal); +ALTER EXTENSION btree_gist ADD function gbt_numeric_compress(internal); +ALTER EXTENSION btree_gist ADD function gbt_numeric_penalty(internal,internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_numeric_picksplit(internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_numeric_union(bytea,internal); +ALTER EXTENSION btree_gist ADD function gbt_numeric_same(internal,internal,internal); +ALTER EXTENSION btree_gist ADD operator family gist_numeric_ops using gist; +ALTER EXTENSION btree_gist ADD operator class gist_numeric_ops using gist; +ALTER EXTENSION btree_gist ADD function gbt_bit_consistent(internal,bit,smallint,oid,internal); +ALTER EXTENSION btree_gist ADD function gbt_bit_compress(internal); +ALTER EXTENSION btree_gist ADD function gbt_bit_penalty(internal,internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_bit_picksplit(internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_bit_union(bytea,internal); +ALTER EXTENSION btree_gist ADD function gbt_bit_same(internal,internal,internal); +ALTER EXTENSION btree_gist ADD operator family gist_bit_ops using gist; +ALTER EXTENSION btree_gist ADD operator class gist_bit_ops using gist; +ALTER EXTENSION btree_gist ADD operator family gist_vbit_ops using gist; +ALTER EXTENSION btree_gist ADD operator class gist_vbit_ops using gist; +ALTER EXTENSION btree_gist ADD function gbt_inet_consistent(internal,inet,smallint,oid,internal); +ALTER EXTENSION btree_gist ADD function gbt_inet_compress(internal); +ALTER EXTENSION btree_gist ADD function gbt_inet_penalty(internal,internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_inet_picksplit(internal,internal); +ALTER EXTENSION btree_gist ADD function gbt_inet_union(bytea,internal); +ALTER EXTENSION btree_gist ADD function gbt_inet_same(internal,internal,internal); +ALTER EXTENSION btree_gist ADD operator family gist_inet_ops using gist; +ALTER EXTENSION btree_gist ADD operator class gist_inet_ops using gist; +ALTER EXTENSION btree_gist ADD operator family gist_cidr_ops using gist; +ALTER EXTENSION btree_gist ADD operator class gist_cidr_ops using gist; diff --git a/contrib/btree_gist/btree_gist.control b/contrib/btree_gist/btree_gist.control new file mode 100644 index 0000000000..10e2f949c1 --- /dev/null +++ b/contrib/btree_gist/btree_gist.control @@ -0,0 +1,5 @@ +# btree_gist extension +comment = 'support for indexing common datatypes in GiST' +default_version = '1.0' +module_pathname = '$libdir/btree_gist' +relocatable = true diff --git a/contrib/btree_gist/btree_text.c b/contrib/btree_gist/btree_text.c index 40d0b9ad79..665dfe78b4 100644 --- a/contrib/btree_gist/btree_text.c +++ b/contrib/btree_gist/btree_text.c @@ -3,6 +3,7 @@ */ #include "btree_gist.h" #include "btree_utils_var.h" +#include "catalog/pg_collation.h" #include "utils/builtins.h" /* @@ -32,37 +33,37 @@ Datum gbt_text_same(PG_FUNCTION_ARGS); static bool gbt_textgt(const void *a, const void *b) { - return (DatumGetBool(DirectFunctionCall2(text_gt, PointerGetDatum(a), PointerGetDatum(b)))); + return (DatumGetBool(DirectFunctionCall2WithCollation(text_gt, DEFAULT_COLLATION_OID, PointerGetDatum(a), PointerGetDatum(b)))); } static bool gbt_textge(const void *a, const void *b) { - return (DatumGetBool(DirectFunctionCall2(text_ge, PointerGetDatum(a), PointerGetDatum(b)))); + return (DatumGetBool(DirectFunctionCall2WithCollation(text_ge, DEFAULT_COLLATION_OID, PointerGetDatum(a), PointerGetDatum(b)))); } static bool gbt_texteq(const void *a, const void *b) { - return (DatumGetBool(DirectFunctionCall2(texteq, PointerGetDatum(a), PointerGetDatum(b)))); + return (DatumGetBool(DirectFunctionCall2WithCollation(texteq, DEFAULT_COLLATION_OID, PointerGetDatum(a), PointerGetDatum(b)))); } static bool gbt_textle(const void *a, const void *b) { - return (DatumGetBool(DirectFunctionCall2(text_le, PointerGetDatum(a), PointerGetDatum(b)))); + return (DatumGetBool(DirectFunctionCall2WithCollation(text_le, DEFAULT_COLLATION_OID, PointerGetDatum(a), PointerGetDatum(b)))); } static bool gbt_textlt(const void *a, const void *b) { - return (DatumGetBool(DirectFunctionCall2(text_lt, PointerGetDatum(a), PointerGetDatum(b)))); + return (DatumGetBool(DirectFunctionCall2WithCollation(text_lt, DEFAULT_COLLATION_OID, PointerGetDatum(a), PointerGetDatum(b)))); } static int32 gbt_textcmp(const bytea *a, const bytea *b) { - return DatumGetInt32(DirectFunctionCall2(bttextcmp, PointerGetDatum(a), PointerGetDatum(b))); + return DatumGetInt32(DirectFunctionCall2WithCollation(bttextcmp, DEFAULT_COLLATION_OID, PointerGetDatum(a), PointerGetDatum(b))); } static gbtree_vinfo tinfo = diff --git a/contrib/btree_gist/btree_utils_var.c b/contrib/btree_gist/btree_utils_var.c index 5fc93cfbff..8f3173e499 100644 --- a/contrib/btree_gist/btree_utils_var.c +++ b/contrib/btree_gist/btree_utils_var.c @@ -8,6 +8,7 @@ #include <float.h> #include "btree_utils_var.h" +#include "catalog/pg_collation.h" #include "utils/pg_locale.h" #include "utils/builtins.h" #include "utils/rel.h" @@ -156,7 +157,7 @@ gbt_bytea_pf_match(const bytea *pf, const bytea *query, const gbtree_vinfo *tinf if (tinfo->eml > 1) { - out = (varstr_cmp(q, nlen, n, nlen) == 0); + out = (varstr_cmp(q, nlen, n, nlen, DEFAULT_COLLATION_OID) == 0); } else { diff --git a/contrib/btree_gist/expected/init.out b/contrib/btree_gist/expected/init.out index c808249545..afe0534682 100644 --- a/contrib/btree_gist/expected/init.out +++ b/contrib/btree_gist/expected/init.out @@ -1,7 +1 @@ --- --- first, define the datatype. Turn off echoing so that expected file --- does not depend on contents of btree_gist.sql. --- -SET client_min_messages = warning; -\set ECHO none -RESET client_min_messages; +CREATE EXTENSION btree_gist; diff --git a/contrib/btree_gist/sql/init.sql b/contrib/btree_gist/sql/init.sql index 7fafde12d8..afe0534682 100644 --- a/contrib/btree_gist/sql/init.sql +++ b/contrib/btree_gist/sql/init.sql @@ -1,9 +1 @@ --- --- first, define the datatype. Turn off echoing so that expected file --- does not depend on contents of btree_gist.sql. --- -SET client_min_messages = warning; -\set ECHO none -\i btree_gist.sql -\set ECHO all -RESET client_min_messages; +CREATE EXTENSION btree_gist; diff --git a/contrib/btree_gist/uninstall_btree_gist.sql b/contrib/btree_gist/uninstall_btree_gist.sql deleted file mode 100644 index 30b9da4c73..0000000000 --- a/contrib/btree_gist/uninstall_btree_gist.sql +++ /dev/null @@ -1,280 +0,0 @@ -/* contrib/btree_gist/uninstall_btree_gist.sql */ - --- Adjust this setting to control where the objects get dropped. -SET search_path = public; - -DROP OPERATOR CLASS gist_cidr_ops USING gist; - -DROP OPERATOR CLASS gist_inet_ops USING gist; - -DROP FUNCTION gbt_inet_same(internal, internal, internal); - -DROP FUNCTION gbt_inet_union(bytea, internal); - -DROP FUNCTION gbt_inet_picksplit(internal, internal); - -DROP FUNCTION gbt_inet_penalty(internal,internal,internal); - -DROP FUNCTION gbt_inet_compress(internal); - -DROP FUNCTION gbt_inet_consistent(internal,inet,int2,oid,internal); - -DROP OPERATOR CLASS gist_vbit_ops USING gist; - -DROP OPERATOR CLASS gist_bit_ops USING gist; - -DROP FUNCTION gbt_bit_same(internal, internal, internal); - -DROP FUNCTION gbt_bit_union(bytea, internal); - -DROP FUNCTION gbt_bit_picksplit(internal, internal); - -DROP FUNCTION gbt_bit_penalty(internal,internal,internal); - -DROP FUNCTION gbt_bit_compress(internal); - -DROP FUNCTION gbt_bit_consistent(internal,bit,int2,oid,internal); - -DROP OPERATOR CLASS gist_numeric_ops USING gist; - -DROP FUNCTION gbt_numeric_same(internal, internal, internal); - -DROP FUNCTION gbt_numeric_union(bytea, internal); - -DROP FUNCTION gbt_numeric_picksplit(internal, internal); - -DROP FUNCTION gbt_numeric_penalty(internal,internal,internal); - -DROP FUNCTION gbt_numeric_compress(internal); - -DROP FUNCTION gbt_numeric_consistent(internal,numeric,int2,oid,internal); - -DROP OPERATOR CLASS gist_bytea_ops USING gist; - -DROP FUNCTION gbt_bytea_same(internal, internal, internal); - -DROP FUNCTION gbt_bytea_union(bytea, internal); - -DROP FUNCTION gbt_bytea_picksplit(internal, internal); - -DROP FUNCTION gbt_bytea_penalty(internal,internal,internal); - -DROP FUNCTION gbt_bytea_compress(internal); - -DROP FUNCTION gbt_bytea_consistent(internal,bytea,int2,oid,internal); - -DROP OPERATOR CLASS gist_bpchar_ops USING gist; - -DROP OPERATOR CLASS gist_text_ops USING gist; - -DROP FUNCTION gbt_text_same(internal, internal, internal); - -DROP FUNCTION gbt_text_union(bytea, internal); - -DROP FUNCTION gbt_text_picksplit(internal, internal); - -DROP FUNCTION gbt_text_penalty(internal,internal,internal); - -DROP FUNCTION gbt_bpchar_compress(internal); - -DROP FUNCTION gbt_text_compress(internal); - -DROP FUNCTION gbt_bpchar_consistent(internal,bpchar,int2,oid,internal); - -DROP FUNCTION gbt_text_consistent(internal,text,int2,oid,internal); - -DROP OPERATOR CLASS gist_macaddr_ops USING gist; - -DROP FUNCTION gbt_macad_same(internal, internal, internal); - -DROP FUNCTION gbt_macad_union(bytea, internal); - -DROP FUNCTION gbt_macad_picksplit(internal, internal); - -DROP FUNCTION gbt_macad_penalty(internal,internal,internal); - -DROP FUNCTION gbt_macad_compress(internal); - -DROP FUNCTION gbt_macad_consistent(internal,macaddr,int2,oid,internal); - -DROP OPERATOR CLASS gist_cash_ops USING gist; - -DROP FUNCTION gbt_cash_same(internal, internal, internal); - -DROP FUNCTION gbt_cash_union(bytea, internal); - -DROP FUNCTION gbt_cash_picksplit(internal, internal); - -DROP FUNCTION gbt_cash_penalty(internal,internal,internal); - -DROP FUNCTION gbt_cash_compress(internal); - -DROP FUNCTION gbt_cash_consistent(internal,money,int2,oid,internal); - -DROP OPERATOR CLASS gist_interval_ops USING gist; - -DROP FUNCTION gbt_intv_same(internal, internal, internal); - -DROP FUNCTION gbt_intv_union(bytea, internal); - -DROP FUNCTION gbt_intv_picksplit(internal, internal); - -DROP FUNCTION gbt_intv_penalty(internal,internal,internal); - -DROP FUNCTION gbt_intv_decompress(internal); - -DROP FUNCTION gbt_intv_compress(internal); - -DROP FUNCTION gbt_intv_consistent(internal,interval,int2,oid,internal); - -DROP OPERATOR CLASS gist_date_ops USING gist; - -DROP FUNCTION gbt_date_same(internal, internal, internal); - -DROP FUNCTION gbt_date_union(bytea, internal); - -DROP FUNCTION gbt_date_picksplit(internal, internal); - -DROP FUNCTION gbt_date_penalty(internal,internal,internal); - -DROP FUNCTION gbt_date_compress(internal); - -DROP FUNCTION gbt_date_consistent(internal,date,int2,oid,internal); - -DROP OPERATOR CLASS gist_timetz_ops USING gist; - -DROP OPERATOR CLASS gist_time_ops USING gist; - -DROP FUNCTION gbt_time_same(internal, internal, internal); - -DROP FUNCTION gbt_time_union(bytea, internal); - -DROP FUNCTION gbt_time_picksplit(internal, internal); - -DROP FUNCTION gbt_time_penalty(internal,internal,internal); - -DROP FUNCTION gbt_timetz_compress(internal); - -DROP FUNCTION gbt_time_compress(internal); - -DROP FUNCTION gbt_timetz_consistent(internal,timetz,int2,oid,internal); - -DROP FUNCTION gbt_time_consistent(internal,time,int2,oid,internal); - -DROP OPERATOR CLASS gist_timestamptz_ops USING gist; - -DROP OPERATOR CLASS gist_timestamp_ops USING gist; - -DROP FUNCTION gbt_ts_same(internal, internal, internal); - -DROP FUNCTION gbt_ts_union(bytea, internal); - -DROP FUNCTION gbt_ts_picksplit(internal, internal); - -DROP FUNCTION gbt_ts_penalty(internal,internal,internal); - -DROP FUNCTION gbt_tstz_compress(internal); - -DROP FUNCTION gbt_ts_compress(internal); - -DROP FUNCTION gbt_tstz_consistent(internal,timestamptz,int2,oid,internal); - -DROP FUNCTION gbt_ts_consistent(internal,timestamp,int2,oid,internal); - -DROP OPERATOR CLASS gist_float8_ops USING gist; - -DROP FUNCTION gbt_float8_same(internal, internal, internal); - -DROP FUNCTION gbt_float8_union(bytea, internal); - -DROP FUNCTION gbt_float8_picksplit(internal, internal); - -DROP FUNCTION gbt_float8_penalty(internal,internal,internal); - -DROP FUNCTION gbt_float8_compress(internal); - -DROP FUNCTION gbt_float8_consistent(internal,float8,int2,oid,internal); - -DROP OPERATOR CLASS gist_float4_ops USING gist; - -DROP FUNCTION gbt_float4_same(internal, internal, internal); - -DROP FUNCTION gbt_float4_union(bytea, internal); - -DROP FUNCTION gbt_float4_picksplit(internal, internal); - -DROP FUNCTION gbt_float4_penalty(internal,internal,internal); - -DROP FUNCTION gbt_float4_compress(internal); - -DROP FUNCTION gbt_float4_consistent(internal,float4,int2,oid,internal); - -DROP OPERATOR CLASS gist_int8_ops USING gist; - -DROP FUNCTION gbt_int8_same(internal, internal, internal); - -DROP FUNCTION gbt_int8_union(bytea, internal); - -DROP FUNCTION gbt_int8_picksplit(internal, internal); - -DROP FUNCTION gbt_int8_penalty(internal,internal,internal); - -DROP FUNCTION gbt_int8_compress(internal); - -DROP FUNCTION gbt_int8_consistent(internal,int8,int2,oid,internal); - -DROP OPERATOR CLASS gist_int4_ops USING gist; - -DROP FUNCTION gbt_int4_same(internal, internal, internal); - -DROP FUNCTION gbt_int4_union(bytea, internal); - -DROP FUNCTION gbt_int4_picksplit(internal, internal); - -DROP FUNCTION gbt_int4_penalty(internal,internal,internal); - -DROP FUNCTION gbt_int4_compress(internal); - -DROP FUNCTION gbt_int4_consistent(internal,int4,int2,oid,internal); - -DROP OPERATOR CLASS gist_int2_ops USING gist; - -DROP FUNCTION gbt_int2_same(internal, internal, internal); - -DROP FUNCTION gbt_int2_union(bytea, internal); - -DROP FUNCTION gbt_int2_picksplit(internal, internal); - -DROP FUNCTION gbt_int2_penalty(internal,internal,internal); - -DROP FUNCTION gbt_int2_compress(internal); - -DROP FUNCTION gbt_int2_consistent(internal,int2,int2,oid,internal); - -DROP OPERATOR CLASS gist_oid_ops USING gist; - -DROP FUNCTION gbt_oid_same(internal, internal, internal); - -DROP FUNCTION gbt_oid_union(bytea, internal); - -DROP FUNCTION gbt_oid_picksplit(internal, internal); - -DROP FUNCTION gbt_oid_penalty(internal,internal,internal); - -DROP FUNCTION gbt_var_decompress(internal); - -DROP FUNCTION gbt_decompress(internal); - -DROP FUNCTION gbt_oid_compress(internal); - -DROP FUNCTION gbt_oid_consistent(internal,oid,int2,oid,internal); - -DROP TYPE gbtreekey_var CASCADE; - -DROP TYPE gbtreekey32 CASCADE; - -DROP TYPE gbtreekey16 CASCADE; - -DROP TYPE gbtreekey8 CASCADE; - -DROP TYPE gbtreekey4 CASCADE; diff --git a/contrib/chkpass/.gitignore b/contrib/chkpass/.gitignore deleted file mode 100644 index 9029d666aa..0000000000 --- a/contrib/chkpass/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/chkpass.sql diff --git a/contrib/chkpass/Makefile b/contrib/chkpass/Makefile index 3677dfcb56..b775aef17d 100644 --- a/contrib/chkpass/Makefile +++ b/contrib/chkpass/Makefile @@ -2,9 +2,11 @@ MODULE_big = chkpass OBJS = chkpass.o + +EXTENSION = chkpass +DATA = chkpass--1.0.sql chkpass--unpackaged--1.0.sql + SHLIB_LINK = $(filter -lcrypt, $(LIBS)) -DATA_built = chkpass.sql -DATA = uninstall_chkpass.sql ifdef USE_PGXS PG_CONFIG = pg_config diff --git a/contrib/chkpass/chkpass.sql.in b/contrib/chkpass/chkpass--1.0.sql index 3cec0224b0..755fee3bc3 100644 --- a/contrib/chkpass/chkpass.sql.in +++ b/contrib/chkpass/chkpass--1.0.sql @@ -1,18 +1,15 @@ -/* contrib/chkpass/chkpass.sql.in */ - --- Adjust this setting to control where the objects get created. -SET search_path = public; +/* contrib/chkpass/chkpass--1.0.sql */ -- -- Input and output functions and the type itself: -- -CREATE OR REPLACE FUNCTION chkpass_in(cstring) +CREATE FUNCTION chkpass_in(cstring) RETURNS chkpass AS 'MODULE_PATHNAME' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION chkpass_out(chkpass) +CREATE FUNCTION chkpass_out(chkpass) RETURNS cstring AS 'MODULE_PATHNAME' LANGUAGE C STRICT; @@ -23,7 +20,7 @@ CREATE TYPE chkpass ( output = chkpass_out ); -CREATE OR REPLACE FUNCTION raw(chkpass) +CREATE FUNCTION raw(chkpass) RETURNS text AS 'MODULE_PATHNAME', 'chkpass_rout' LANGUAGE C STRICT; @@ -32,12 +29,12 @@ CREATE OR REPLACE FUNCTION raw(chkpass) -- The various boolean tests: -- -CREATE OR REPLACE FUNCTION eq(chkpass, text) +CREATE FUNCTION eq(chkpass, text) RETURNS bool AS 'MODULE_PATHNAME', 'chkpass_eq' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION ne(chkpass, text) +CREATE FUNCTION ne(chkpass, text) RETURNS bool AS 'MODULE_PATHNAME', 'chkpass_ne' LANGUAGE C STRICT; diff --git a/contrib/chkpass/chkpass--unpackaged--1.0.sql b/contrib/chkpass/chkpass--unpackaged--1.0.sql new file mode 100644 index 0000000000..bf91950f3c --- /dev/null +++ b/contrib/chkpass/chkpass--unpackaged--1.0.sql @@ -0,0 +1,10 @@ +/* contrib/chkpass/chkpass--unpackaged--1.0.sql */ + +ALTER EXTENSION chkpass ADD type chkpass; +ALTER EXTENSION chkpass ADD function chkpass_in(cstring); +ALTER EXTENSION chkpass ADD function chkpass_out(chkpass); +ALTER EXTENSION chkpass ADD function raw(chkpass); +ALTER EXTENSION chkpass ADD function eq(chkpass,text); +ALTER EXTENSION chkpass ADD function ne(chkpass,text); +ALTER EXTENSION chkpass ADD operator <>(chkpass,text); +ALTER EXTENSION chkpass ADD operator =(chkpass,text); diff --git a/contrib/chkpass/chkpass.control b/contrib/chkpass/chkpass.control new file mode 100644 index 0000000000..bd4b3d3d0d --- /dev/null +++ b/contrib/chkpass/chkpass.control @@ -0,0 +1,5 @@ +# chkpass extension +comment = 'data type for auto-encrypted passwords' +default_version = '1.0' +module_pathname = '$libdir/chkpass' +relocatable = true diff --git a/contrib/chkpass/uninstall_chkpass.sql b/contrib/chkpass/uninstall_chkpass.sql deleted file mode 100644 index 93ab6eb4eb..0000000000 --- a/contrib/chkpass/uninstall_chkpass.sql +++ /dev/null @@ -1,16 +0,0 @@ -/* contrib/chkpass/uninstall_chkpass.sql */ - --- Adjust this setting to control where the objects get dropped. -SET search_path = public; - -DROP OPERATOR <>(chkpass, text); - -DROP OPERATOR =(chkpass, text); - -DROP FUNCTION ne(chkpass, text); - -DROP FUNCTION eq(chkpass, text); - -DROP FUNCTION raw(chkpass); - -DROP TYPE chkpass CASCADE; diff --git a/contrib/citext/.gitignore b/contrib/citext/.gitignore index e626817156..19b6c5ba42 100644 --- a/contrib/citext/.gitignore +++ b/contrib/citext/.gitignore @@ -1,3 +1,2 @@ -/citext.sql # Generated subdirectories /results/ diff --git a/contrib/citext/Makefile b/contrib/citext/Makefile index c868eca884..65942528dd 100644 --- a/contrib/citext/Makefile +++ b/contrib/citext/Makefile @@ -1,8 +1,10 @@ # contrib/citext/Makefile MODULES = citext -DATA_built = citext.sql -DATA = uninstall_citext.sql + +EXTENSION = citext +DATA = citext--1.0.sql citext--unpackaged--1.0.sql + REGRESS = citext ifdef USE_PGXS diff --git a/contrib/citext/citext.sql.in b/contrib/citext/citext--1.0.sql index 1e75b55397..2760f7e08d 100644 --- a/contrib/citext/citext.sql.in +++ b/contrib/citext/citext--1.0.sql @@ -1,7 +1,4 @@ -/* contrib/citext/citext.sql.in */ - --- Adjust this setting to control where the objects get created. -SET search_path = public; +/* contrib/citext/citext--1.0.sql */ -- -- PostgreSQL code for CITEXT. @@ -19,22 +16,22 @@ CREATE TYPE citext; -- -- Input and output functions. -- -CREATE OR REPLACE FUNCTION citextin(cstring) +CREATE FUNCTION citextin(cstring) RETURNS citext AS 'textin' LANGUAGE internal IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION citextout(citext) +CREATE FUNCTION citextout(citext) RETURNS cstring AS 'textout' LANGUAGE internal IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION citextrecv(internal) +CREATE FUNCTION citextrecv(internal) RETURNS citext AS 'textrecv' LANGUAGE internal STABLE STRICT; -CREATE OR REPLACE FUNCTION citextsend(citext) +CREATE FUNCTION citextsend(citext) RETURNS bytea AS 'textsend' LANGUAGE internal STABLE STRICT; @@ -52,7 +49,8 @@ CREATE TYPE citext ( STORAGE = extended, -- make it a non-preferred member of string type category CATEGORY = 'S', - PREFERRED = false + PREFERRED = false, + COLLATABLE = true ); -- @@ -60,17 +58,17 @@ CREATE TYPE citext ( -- automatically kick in. -- -CREATE OR REPLACE FUNCTION citext(bpchar) +CREATE FUNCTION citext(bpchar) RETURNS citext AS 'rtrim1' LANGUAGE internal IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION citext(boolean) +CREATE FUNCTION citext(boolean) RETURNS citext AS 'booltext' LANGUAGE internal IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION citext(inet) +CREATE FUNCTION citext(inet) RETURNS citext AS 'network_show' LANGUAGE internal IMMUTABLE STRICT; @@ -92,32 +90,32 @@ CREATE CAST (inet AS citext) WITH FUNCTION citext(inet) AS ASSIGNMENT; -- Operator Functions. -- -CREATE OR REPLACE FUNCTION citext_eq( citext, citext ) +CREATE FUNCTION citext_eq( citext, citext ) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION citext_ne( citext, citext ) +CREATE FUNCTION citext_ne( citext, citext ) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION citext_lt( citext, citext ) +CREATE FUNCTION citext_lt( citext, citext ) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION citext_le( citext, citext ) +CREATE FUNCTION citext_le( citext, citext ) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION citext_gt( citext, citext ) +CREATE FUNCTION citext_gt( citext, citext ) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION citext_ge( citext, citext ) +CREATE FUNCTION citext_ge( citext, citext ) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; @@ -192,12 +190,12 @@ CREATE OPERATOR > ( -- Support functions for indexing. -- -CREATE OR REPLACE FUNCTION citext_cmp(citext, citext) +CREATE FUNCTION citext_cmp(citext, citext) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION citext_hash(citext) +CREATE FUNCTION citext_hash(citext) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -228,12 +226,12 @@ DEFAULT FOR TYPE citext USING hash AS -- Aggregates. -- -CREATE OR REPLACE FUNCTION citext_smaller(citext, citext) +CREATE FUNCTION citext_smaller(citext, citext) RETURNS citext AS 'MODULE_PATHNAME' LANGUAGE 'C' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION citext_larger(citext, citext) +CREATE FUNCTION citext_larger(citext, citext) RETURNS citext AS 'MODULE_PATHNAME' LANGUAGE 'C' IMMUTABLE STRICT; @@ -254,19 +252,19 @@ CREATE AGGREGATE max(citext) ( -- CITEXT pattern matching. -- -CREATE OR REPLACE FUNCTION texticlike(citext, citext) +CREATE FUNCTION texticlike(citext, citext) RETURNS bool AS 'texticlike' LANGUAGE internal IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION texticnlike(citext, citext) +CREATE FUNCTION texticnlike(citext, citext) RETURNS bool AS 'texticnlike' LANGUAGE internal IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION texticregexeq(citext, citext) +CREATE FUNCTION texticregexeq(citext, citext) RETURNS bool AS 'texticregexeq' LANGUAGE internal IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION texticregexne(citext, citext) +CREATE FUNCTION texticregexne(citext, citext) RETURNS bool AS 'texticregexne' LANGUAGE internal IMMUTABLE STRICT; @@ -346,19 +344,19 @@ CREATE OPERATOR !~~* ( -- Matching citext to text. -- -CREATE OR REPLACE FUNCTION texticlike(citext, text) +CREATE FUNCTION texticlike(citext, text) RETURNS bool AS 'texticlike' LANGUAGE internal IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION texticnlike(citext, text) +CREATE FUNCTION texticnlike(citext, text) RETURNS bool AS 'texticnlike' LANGUAGE internal IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION texticregexeq(citext, text) +CREATE FUNCTION texticregexeq(citext, text) RETURNS bool AS 'texticregexeq' LANGUAGE internal IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION texticregexne(citext, text) +CREATE FUNCTION texticregexne(citext, text) RETURNS bool AS 'texticregexne' LANGUAGE internal IMMUTABLE STRICT; @@ -439,50 +437,50 @@ CREATE OPERATOR !~~* ( -- XXX TODO Ideally these would be implemented in C. -- -CREATE OR REPLACE FUNCTION regexp_matches( citext, citext ) RETURNS TEXT[] AS $$ +CREATE FUNCTION regexp_matches( citext, citext ) RETURNS TEXT[] AS $$ SELECT pg_catalog.regexp_matches( $1::pg_catalog.text, $2::pg_catalog.text, 'i' ); $$ LANGUAGE SQL IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION regexp_matches( citext, citext, text ) RETURNS TEXT[] AS $$ +CREATE FUNCTION regexp_matches( citext, citext, text ) RETURNS 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; -CREATE OR REPLACE FUNCTION regexp_replace( citext, citext, text ) returns TEXT AS $$ +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'); $$ LANGUAGE SQL IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION regexp_replace( citext, citext, text, text ) returns TEXT AS $$ +CREATE FUNCTION regexp_replace( citext, citext, text, text ) returns TEXT AS $$ SELECT pg_catalog.regexp_replace( $1::pg_catalog.text, $2::pg_catalog.text, $3, CASE WHEN pg_catalog.strpos($4, 'c') = 0 THEN $4 || 'i' ELSE $4 END); $$ LANGUAGE SQL IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION regexp_split_to_array( citext, citext ) RETURNS TEXT[] AS $$ +CREATE FUNCTION regexp_split_to_array( citext, citext ) RETURNS TEXT[] AS $$ SELECT pg_catalog.regexp_split_to_array( $1::pg_catalog.text, $2::pg_catalog.text, 'i' ); $$ LANGUAGE SQL IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION regexp_split_to_array( citext, citext, text ) RETURNS TEXT[] AS $$ +CREATE FUNCTION regexp_split_to_array( citext, citext, text ) RETURNS TEXT[] AS $$ SELECT pg_catalog.regexp_split_to_array( $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; -CREATE OR REPLACE FUNCTION regexp_split_to_table( citext, citext ) RETURNS SETOF TEXT AS $$ +CREATE FUNCTION regexp_split_to_table( citext, citext ) RETURNS SETOF TEXT AS $$ SELECT pg_catalog.regexp_split_to_table( $1::pg_catalog.text, $2::pg_catalog.text, 'i' ); $$ LANGUAGE SQL IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION regexp_split_to_table( citext, citext, text ) RETURNS SETOF TEXT AS $$ +CREATE FUNCTION regexp_split_to_table( citext, citext, text ) RETURNS SETOF TEXT AS $$ SELECT pg_catalog.regexp_split_to_table( $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; -CREATE OR REPLACE FUNCTION strpos( citext, citext ) RETURNS INT AS $$ +CREATE FUNCTION strpos( citext, citext ) RETURNS INT AS $$ SELECT pg_catalog.strpos( pg_catalog.lower( $1::pg_catalog.text ), pg_catalog.lower( $2::pg_catalog.text ) ); $$ LANGUAGE SQL IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION replace( citext, citext, citext ) RETURNS TEXT AS $$ +CREATE FUNCTION replace( citext, citext, citext ) RETURNS TEXT AS $$ SELECT pg_catalog.regexp_replace( $1::pg_catalog.text, pg_catalog.regexp_replace($2::pg_catalog.text, '([^a-zA-Z_0-9])', E'\\\\\\1', 'g'), $3::pg_catalog.text, 'gi' ); $$ LANGUAGE SQL IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION split_part( citext, citext, int ) RETURNS TEXT AS $$ +CREATE FUNCTION split_part( citext, citext, int ) RETURNS TEXT AS $$ SELECT (pg_catalog.regexp_split_to_array( $1::pg_catalog.text, pg_catalog.regexp_replace($2::pg_catalog.text, '([^a-zA-Z_0-9])', E'\\\\\\1', 'g'), 'i'))[$3]; $$ LANGUAGE SQL IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION translate( citext, citext, text ) RETURNS TEXT AS $$ +CREATE FUNCTION translate( citext, citext, text ) RETURNS TEXT AS $$ SELECT pg_catalog.translate( pg_catalog.translate( $1::pg_catalog.text, pg_catalog.lower($2::pg_catalog.text), $3), pg_catalog.upper($2::pg_catalog.text), $3); $$ LANGUAGE SQL IMMUTABLE STRICT; diff --git a/contrib/citext/citext--unpackaged--1.0.sql b/contrib/citext/citext--unpackaged--1.0.sql new file mode 100644 index 0000000000..7dcdc39413 --- /dev/null +++ b/contrib/citext/citext--unpackaged--1.0.sql @@ -0,0 +1,76 @@ +/* contrib/citext/citext--unpackaged--1.0.sql */ + +ALTER EXTENSION citext ADD type citext; +ALTER EXTENSION citext ADD function citextin(cstring); +ALTER EXTENSION citext ADD function citextout(citext); +ALTER EXTENSION citext ADD function citextrecv(internal); +ALTER EXTENSION citext ADD function citextsend(citext); +ALTER EXTENSION citext ADD function citext(character); +ALTER EXTENSION citext ADD function citext(boolean); +ALTER EXTENSION citext ADD function citext(inet); +ALTER EXTENSION citext ADD cast (citext as text); +ALTER EXTENSION citext ADD cast (citext as character varying); +ALTER EXTENSION citext ADD cast (citext as character); +ALTER EXTENSION citext ADD cast (text as citext); +ALTER EXTENSION citext ADD cast (character varying as citext); +ALTER EXTENSION citext ADD cast (character as citext); +ALTER EXTENSION citext ADD cast (boolean as citext); +ALTER EXTENSION citext ADD cast (inet as citext); +ALTER EXTENSION citext ADD function citext_eq(citext,citext); +ALTER EXTENSION citext ADD function citext_ne(citext,citext); +ALTER EXTENSION citext ADD function citext_lt(citext,citext); +ALTER EXTENSION citext ADD function citext_le(citext,citext); +ALTER EXTENSION citext ADD function citext_gt(citext,citext); +ALTER EXTENSION citext ADD function citext_ge(citext,citext); +ALTER EXTENSION citext ADD operator <>(citext,citext); +ALTER EXTENSION citext ADD operator =(citext,citext); +ALTER EXTENSION citext ADD operator >(citext,citext); +ALTER EXTENSION citext ADD operator >=(citext,citext); +ALTER EXTENSION citext ADD operator <(citext,citext); +ALTER EXTENSION citext ADD operator <=(citext,citext); +ALTER EXTENSION citext ADD function citext_cmp(citext,citext); +ALTER EXTENSION citext ADD function citext_hash(citext); +ALTER EXTENSION citext ADD operator family citext_ops using btree; +ALTER EXTENSION citext ADD operator class citext_ops using btree; +ALTER EXTENSION citext ADD operator family citext_ops using hash; +ALTER EXTENSION citext ADD operator class citext_ops using hash; +ALTER EXTENSION citext ADD function citext_smaller(citext,citext); +ALTER EXTENSION citext ADD function citext_larger(citext,citext); +ALTER EXTENSION citext ADD function min(citext); +ALTER EXTENSION citext ADD function max(citext); +ALTER EXTENSION citext ADD function texticlike(citext,citext); +ALTER EXTENSION citext ADD function texticnlike(citext,citext); +ALTER EXTENSION citext ADD function texticregexeq(citext,citext); +ALTER EXTENSION citext ADD function texticregexne(citext,citext); +ALTER EXTENSION citext ADD operator !~(citext,citext); +ALTER EXTENSION citext ADD operator ~(citext,citext); +ALTER EXTENSION citext ADD operator !~*(citext,citext); +ALTER EXTENSION citext ADD operator ~*(citext,citext); +ALTER EXTENSION citext ADD operator !~~(citext,citext); +ALTER EXTENSION citext ADD operator ~~(citext,citext); +ALTER EXTENSION citext ADD operator !~~*(citext,citext); +ALTER EXTENSION citext ADD operator ~~*(citext,citext); +ALTER EXTENSION citext ADD function texticlike(citext,text); +ALTER EXTENSION citext ADD function texticnlike(citext,text); +ALTER EXTENSION citext ADD function texticregexeq(citext,text); +ALTER EXTENSION citext ADD function texticregexne(citext,text); +ALTER EXTENSION citext ADD operator !~(citext,text); +ALTER EXTENSION citext ADD operator ~(citext,text); +ALTER EXTENSION citext ADD operator !~*(citext,text); +ALTER EXTENSION citext ADD operator ~*(citext,text); +ALTER EXTENSION citext ADD operator !~~(citext,text); +ALTER EXTENSION citext ADD operator ~~(citext,text); +ALTER EXTENSION citext ADD operator !~~*(citext,text); +ALTER EXTENSION citext ADD operator ~~*(citext,text); +ALTER EXTENSION citext ADD function regexp_matches(citext,citext); +ALTER EXTENSION citext ADD function regexp_matches(citext,citext,text); +ALTER EXTENSION citext ADD function regexp_replace(citext,citext,text); +ALTER EXTENSION citext ADD function regexp_replace(citext,citext,text,text); +ALTER EXTENSION citext ADD function regexp_split_to_array(citext,citext); +ALTER EXTENSION citext ADD function regexp_split_to_array(citext,citext,text); +ALTER EXTENSION citext ADD function regexp_split_to_table(citext,citext); +ALTER EXTENSION citext ADD function regexp_split_to_table(citext,citext,text); +ALTER EXTENSION citext ADD function strpos(citext,citext); +ALTER EXTENSION citext ADD function replace(citext,citext,citext); +ALTER EXTENSION citext ADD function split_part(citext,citext,integer); +ALTER EXTENSION citext ADD function translate(citext,citext,text); diff --git a/contrib/citext/citext.c b/contrib/citext/citext.c index 9991825853..25e4dd3999 100644 --- a/contrib/citext/citext.c +++ b/contrib/citext/citext.c @@ -18,7 +18,7 @@ PG_MODULE_MAGIC; * ==================== */ -static int32 citextcmp(text *left, text *right); +static int32 citextcmp(text *left, text *right, Oid collid); extern Datum citext_cmp(PG_FUNCTION_ARGS); extern Datum citext_hash(PG_FUNCTION_ARGS); extern Datum citext_eq(PG_FUNCTION_ARGS); @@ -42,17 +42,18 @@ extern Datum citext_larger(PG_FUNCTION_ARGS); * Returns int32 negative, zero, or positive. */ static int32 -citextcmp(text *left, text *right) +citextcmp(text *left, text *right, Oid collid) { char *lcstr, *rcstr; int32 result; - lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left)); - rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right)); + lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), collid); + rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), collid); result = varstr_cmp(lcstr, strlen(lcstr), - rcstr, strlen(rcstr)); + rcstr, strlen(rcstr), + collid); pfree(lcstr); pfree(rcstr); @@ -75,7 +76,7 @@ citext_cmp(PG_FUNCTION_ARGS) text *right = PG_GETARG_TEXT_PP(1); int32 result; - result = citextcmp(left, right); + result = citextcmp(left, right, PG_GET_COLLATION()); PG_FREE_IF_COPY(left, 0); PG_FREE_IF_COPY(right, 1); @@ -92,7 +93,7 @@ citext_hash(PG_FUNCTION_ARGS) char *str; Datum result; - str = str_tolower(VARDATA_ANY(txt), VARSIZE_ANY_EXHDR(txt)); + str = str_tolower(VARDATA_ANY(txt), VARSIZE_ANY_EXHDR(txt), PG_GET_COLLATION()); result = hash_any((unsigned char *) str, strlen(str)); pfree(str); @@ -121,8 +122,8 @@ citext_eq(PG_FUNCTION_ARGS) /* We can't compare lengths in advance of downcasing ... */ - lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left)); - rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right)); + lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), PG_GET_COLLATION()); + rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), PG_GET_COLLATION()); /* * Since we only care about equality or not-equality, we can avoid all the @@ -151,8 +152,8 @@ citext_ne(PG_FUNCTION_ARGS) /* We can't compare lengths in advance of downcasing ... */ - lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left)); - rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right)); + lcstr = str_tolower(VARDATA_ANY(left), VARSIZE_ANY_EXHDR(left), PG_GET_COLLATION()); + rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right), PG_GET_COLLATION()); /* * Since we only care about equality or not-equality, we can avoid all the @@ -177,7 +178,7 @@ citext_lt(PG_FUNCTION_ARGS) text *right = PG_GETARG_TEXT_PP(1); bool result; - result = citextcmp(left, right) < 0; + result = citextcmp(left, right, PG_GET_COLLATION()) < 0; PG_FREE_IF_COPY(left, 0); PG_FREE_IF_COPY(right, 1); @@ -194,7 +195,7 @@ citext_le(PG_FUNCTION_ARGS) text *right = PG_GETARG_TEXT_PP(1); bool result; - result = citextcmp(left, right) <= 0; + result = citextcmp(left, right, PG_GET_COLLATION()) <= 0; PG_FREE_IF_COPY(left, 0); PG_FREE_IF_COPY(right, 1); @@ -211,7 +212,7 @@ citext_gt(PG_FUNCTION_ARGS) text *right = PG_GETARG_TEXT_PP(1); bool result; - result = citextcmp(left, right) > 0; + result = citextcmp(left, right, PG_GET_COLLATION()) > 0; PG_FREE_IF_COPY(left, 0); PG_FREE_IF_COPY(right, 1); @@ -228,7 +229,7 @@ citext_ge(PG_FUNCTION_ARGS) text *right = PG_GETARG_TEXT_PP(1); bool result; - result = citextcmp(left, right) >= 0; + result = citextcmp(left, right, PG_GET_COLLATION()) >= 0; PG_FREE_IF_COPY(left, 0); PG_FREE_IF_COPY(right, 1); @@ -251,7 +252,7 @@ citext_smaller(PG_FUNCTION_ARGS) text *right = PG_GETARG_TEXT_PP(1); text *result; - result = citextcmp(left, right) < 0 ? left : right; + result = citextcmp(left, right, PG_GET_COLLATION()) < 0 ? left : right; PG_RETURN_TEXT_P(result); } @@ -264,6 +265,6 @@ citext_larger(PG_FUNCTION_ARGS) text *right = PG_GETARG_TEXT_PP(1); text *result; - result = citextcmp(left, right) > 0 ? left : right; + result = citextcmp(left, right, PG_GET_COLLATION()) > 0 ? left : right; PG_RETURN_TEXT_P(result); } diff --git a/contrib/citext/citext.control b/contrib/citext/citext.control new file mode 100644 index 0000000000..3eb01a3360 --- /dev/null +++ b/contrib/citext/citext.control @@ -0,0 +1,5 @@ +# citext extension +comment = 'data type for case-insensitive character strings' +default_version = '1.0' +module_pathname = '$libdir/citext' +relocatable = true diff --git a/contrib/citext/expected/citext.out b/contrib/citext/expected/citext.out index 66ea5ee6ff..5392a7d1f3 100644 --- a/contrib/citext/expected/citext.out +++ b/contrib/citext/expected/citext.out @@ -1,12 +1,7 @@ -- -- Test citext datatype -- --- --- first, define the datatype. Turn off echoing so that expected file --- does not depend on contents of citext.sql. --- -SET client_min_messages = warning; -\set ECHO none +CREATE EXTENSION citext; -- Test the operators and indexing functions -- Test = and <>. SELECT 'a'::citext = 'a'::citext AS t; diff --git a/contrib/citext/expected/citext_1.out b/contrib/citext/expected/citext_1.out index c5ca1f6c54..5316ad0cda 100644 --- a/contrib/citext/expected/citext_1.out +++ b/contrib/citext/expected/citext_1.out @@ -1,12 +1,7 @@ -- -- Test citext datatype -- --- --- first, define the datatype. Turn off echoing so that expected file --- does not depend on contents of citext.sql. --- -SET client_min_messages = warning; -\set ECHO none +CREATE EXTENSION citext; -- Test the operators and indexing functions -- Test = and <>. SELECT 'a'::citext = 'a'::citext AS t; diff --git a/contrib/citext/sql/citext.sql b/contrib/citext/sql/citext.sql index 2f9b46665c..07497401a4 100644 --- a/contrib/citext/sql/citext.sql +++ b/contrib/citext/sql/citext.sql @@ -2,15 +2,7 @@ -- Test citext datatype -- --- --- first, define the datatype. Turn off echoing so that expected file --- does not depend on contents of citext.sql. --- -SET client_min_messages = warning; -\set ECHO none -\i citext.sql -RESET client_min_messages; -\set ECHO all +CREATE EXTENSION citext; -- Test the operators and indexing functions diff --git a/contrib/citext/uninstall_citext.sql b/contrib/citext/uninstall_citext.sql deleted file mode 100644 index 468987ad82..0000000000 --- a/contrib/citext/uninstall_citext.sql +++ /dev/null @@ -1,80 +0,0 @@ -/* contrib/citext/uninstall_citext.sql */ - --- Adjust this setting to control where the objects get dropped. -SET search_path = public; - -DROP OPERATOR CLASS citext_ops USING btree CASCADE; -DROP OPERATOR CLASS citext_ops USING hash CASCADE; - -DROP AGGREGATE min(citext); -DROP AGGREGATE max(citext); - -DROP OPERATOR = (citext, citext); -DROP OPERATOR <> (citext, citext); -DROP OPERATOR < (citext, citext); -DROP OPERATOR <= (citext, citext); -DROP OPERATOR >= (citext, citext); -DROP OPERATOR > (citext, citext); - -DROP OPERATOR ~ (citext, citext); -DROP OPERATOR ~* (citext, citext); -DROP OPERATOR !~ (citext, citext); -DROP OPERATOR !~* (citext, citext); -DROP OPERATOR ~~ (citext, citext); -DROP OPERATOR ~~* (citext, citext); -DROP OPERATOR !~~ (citext, citext); -DROP OPERATOR !~~* (citext, citext); - -DROP OPERATOR ~ (citext, text); -DROP OPERATOR ~* (citext, text); -DROP OPERATOR !~ (citext, text); -DROP OPERATOR !~* (citext, text); -DROP OPERATOR ~~ (citext, text); -DROP OPERATOR ~~* (citext, text); -DROP OPERATOR !~~ (citext, text); -DROP OPERATOR !~~* (citext, text); - -DROP CAST (citext AS text); -DROP CAST (citext AS varchar); -DROP CAST (citext AS bpchar); -DROP CAST (text AS citext); -DROP CAST (varchar AS citext); -DROP CAST (bpchar AS citext); -DROP CAST (boolean AS citext); -DROP CAST (inet AS citext); - -DROP FUNCTION citext(bpchar); -DROP FUNCTION citext(boolean); -DROP FUNCTION citext(inet); -DROP FUNCTION citext_eq(citext, citext); -DROP FUNCTION citext_ne(citext, citext); -DROP FUNCTION citext_lt(citext, citext); -DROP FUNCTION citext_le(citext, citext); -DROP FUNCTION citext_gt(citext, citext); -DROP FUNCTION citext_ge(citext, citext); -DROP FUNCTION citext_cmp(citext, citext); -DROP FUNCTION citext_hash(citext); -DROP FUNCTION citext_smaller(citext, citext); -DROP FUNCTION citext_larger(citext, citext); -DROP FUNCTION texticlike(citext, citext); -DROP FUNCTION texticnlike(citext, citext); -DROP FUNCTION texticregexeq(citext, citext); -DROP FUNCTION texticregexne(citext, citext); -DROP FUNCTION texticlike(citext, text); -DROP FUNCTION texticnlike(citext, text); -DROP FUNCTION texticregexeq(citext, text); -DROP FUNCTION texticregexne(citext, text); -DROP FUNCTION regexp_matches( citext, citext ); -DROP FUNCTION regexp_matches( citext, citext, text ); -DROP FUNCTION regexp_replace( citext, citext, text ); -DROP FUNCTION regexp_replace( citext, citext, text, text ); -DROP FUNCTION regexp_split_to_array( citext, citext ); -DROP FUNCTION regexp_split_to_array( citext, citext, text ); -DROP FUNCTION regexp_split_to_table( citext, citext ); -DROP FUNCTION regexp_split_to_table( citext, citext, text ); -DROP FUNCTION strpos( citext, citext ); -DROP FUNCTION replace( citext, citext, citext ); -DROP FUNCTION split_part( citext, citext, int ); -DROP FUNCTION translate( citext, citext, text ); - -DROP TYPE citext CASCADE; diff --git a/contrib/cube/.gitignore b/contrib/cube/.gitignore index 9f60da5078..a6484a05e7 100644 --- a/contrib/cube/.gitignore +++ b/contrib/cube/.gitignore @@ -1,5 +1,4 @@ /cubeparse.c /cubescan.c -/cube.sql # Generated subdirectories /results/ diff --git a/contrib/cube/Makefile b/contrib/cube/Makefile index 4fee79f84e..19fd7dc658 100644 --- a/contrib/cube/Makefile +++ b/contrib/cube/Makefile @@ -3,8 +3,9 @@ MODULE_big = cube OBJS= cube.o cubeparse.o -DATA_built = cube.sql -DATA = uninstall_cube.sql +EXTENSION = cube +DATA = cube--1.0.sql cube--unpackaged--1.0.sql + REGRESS = cube EXTRA_CLEAN = y.tab.c y.tab.h diff --git a/contrib/cube/cube.sql.in b/contrib/cube/cube--1.0.sql index a7e6b1d2b9..ee9febe005 100644 --- a/contrib/cube/cube.sql.in +++ b/contrib/cube/cube--1.0.sql @@ -1,25 +1,21 @@ -/* contrib/cube/cube.sql.in */ - --- Adjust this setting to control where the objects get created. -SET search_path = public; +/* contrib/cube/cube--1.0.sql */ -- Create the user-defined type for N-dimensional boxes --- -CREATE OR REPLACE FUNCTION cube_in(cstring) +CREATE FUNCTION cube_in(cstring) RETURNS cube AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION cube(float8[], float8[]) RETURNS cube +CREATE FUNCTION cube(float8[], float8[]) RETURNS cube AS 'MODULE_PATHNAME', 'cube_a_f8_f8' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION cube(float8[]) RETURNS cube +CREATE FUNCTION cube(float8[]) RETURNS cube AS 'MODULE_PATHNAME', 'cube_a_f8' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION cube_out(cube) +CREATE FUNCTION cube_out(cube) RETURNS cstring AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; @@ -39,70 +35,70 @@ COMMENT ON TYPE cube IS 'multi-dimensional cube ''(FLOAT-1, FLOAT-2, ..., FLOAT- -- Comparison methods -CREATE OR REPLACE FUNCTION cube_eq(cube, cube) +CREATE FUNCTION cube_eq(cube, cube) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; COMMENT ON FUNCTION cube_eq(cube, cube) IS 'same as'; -CREATE OR REPLACE FUNCTION cube_ne(cube, cube) +CREATE FUNCTION cube_ne(cube, cube) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; COMMENT ON FUNCTION cube_ne(cube, cube) IS 'different'; -CREATE OR REPLACE FUNCTION cube_lt(cube, cube) +CREATE FUNCTION cube_lt(cube, cube) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; COMMENT ON FUNCTION cube_lt(cube, cube) IS 'lower than'; -CREATE OR REPLACE FUNCTION cube_gt(cube, cube) +CREATE FUNCTION cube_gt(cube, cube) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; COMMENT ON FUNCTION cube_gt(cube, cube) IS 'greater than'; -CREATE OR REPLACE FUNCTION cube_le(cube, cube) +CREATE FUNCTION cube_le(cube, cube) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; COMMENT ON FUNCTION cube_le(cube, cube) IS 'lower than or equal to'; -CREATE OR REPLACE FUNCTION cube_ge(cube, cube) +CREATE FUNCTION cube_ge(cube, cube) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; COMMENT ON FUNCTION cube_ge(cube, cube) IS 'greater than or equal to'; -CREATE OR REPLACE FUNCTION cube_cmp(cube, cube) +CREATE FUNCTION cube_cmp(cube, cube) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; COMMENT ON FUNCTION cube_cmp(cube, cube) IS 'btree comparison function'; -CREATE OR REPLACE FUNCTION cube_contains(cube, cube) +CREATE FUNCTION cube_contains(cube, cube) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; COMMENT ON FUNCTION cube_contains(cube, cube) IS 'contains'; -CREATE OR REPLACE FUNCTION cube_contained(cube, cube) +CREATE FUNCTION cube_contained(cube, cube) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; COMMENT ON FUNCTION cube_contained(cube, cube) IS 'contained in'; -CREATE OR REPLACE FUNCTION cube_overlap(cube, cube) +CREATE FUNCTION cube_overlap(cube, cube) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; @@ -111,17 +107,17 @@ COMMENT ON FUNCTION cube_overlap(cube, cube) IS 'overlaps'; -- support routines for indexing -CREATE OR REPLACE FUNCTION cube_union(cube, cube) +CREATE FUNCTION cube_union(cube, cube) RETURNS cube AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION cube_inter(cube, cube) +CREATE FUNCTION cube_inter(cube, cube) RETURNS cube AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION cube_size(cube) +CREATE FUNCTION cube_size(cube) RETURNS float8 AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; @@ -129,61 +125,61 @@ LANGUAGE C IMMUTABLE STRICT; -- Misc N-dimensional functions -CREATE OR REPLACE FUNCTION cube_subset(cube, int4[]) +CREATE FUNCTION cube_subset(cube, int4[]) RETURNS cube AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -- proximity routines -CREATE OR REPLACE FUNCTION cube_distance(cube, cube) +CREATE FUNCTION cube_distance(cube, cube) RETURNS float8 AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -- Extracting elements functions -CREATE OR REPLACE FUNCTION cube_dim(cube) +CREATE FUNCTION cube_dim(cube) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION cube_ll_coord(cube, int4) +CREATE FUNCTION cube_ll_coord(cube, int4) RETURNS float8 AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION cube_ur_coord(cube, int4) +CREATE FUNCTION cube_ur_coord(cube, int4) RETURNS float8 AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION cube(float8) RETURNS cube +CREATE FUNCTION cube(float8) RETURNS cube AS 'MODULE_PATHNAME', 'cube_f8' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION cube(float8, float8) RETURNS cube +CREATE FUNCTION cube(float8, float8) RETURNS cube AS 'MODULE_PATHNAME', 'cube_f8_f8' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION cube(cube, float8) RETURNS cube +CREATE FUNCTION cube(cube, float8) RETURNS cube AS 'MODULE_PATHNAME', 'cube_c_f8' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION cube(cube, float8, float8) RETURNS cube +CREATE FUNCTION cube(cube, float8, float8) RETURNS cube AS 'MODULE_PATHNAME', 'cube_c_f8_f8' LANGUAGE C IMMUTABLE STRICT; -- Test if cube is also a point -CREATE OR REPLACE FUNCTION cube_is_point(cube) +CREATE FUNCTION cube_is_point(cube) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -- Increasing the size of a cube by a radius in at least n dimensions -CREATE OR REPLACE FUNCTION cube_enlarge(cube, float8, int4) +CREATE FUNCTION cube_enlarge(cube, float8, int4) RETURNS cube AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; @@ -262,37 +258,37 @@ CREATE OPERATOR ~ ( -- define the GiST support methods -CREATE OR REPLACE FUNCTION g_cube_consistent(internal,cube,int,oid,internal) +CREATE FUNCTION g_cube_consistent(internal,cube,int,oid,internal) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION g_cube_compress(internal) +CREATE FUNCTION g_cube_compress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION g_cube_decompress(internal) +CREATE FUNCTION g_cube_decompress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION g_cube_penalty(internal,internal,internal) +CREATE FUNCTION g_cube_penalty(internal,internal,internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION g_cube_picksplit(internal, internal) +CREATE FUNCTION g_cube_picksplit(internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION g_cube_union(internal, internal) +CREATE FUNCTION g_cube_union(internal, internal) RETURNS cube AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION g_cube_same(cube, cube, internal) +CREATE FUNCTION g_cube_same(cube, cube, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; diff --git a/contrib/cube/cube--unpackaged--1.0.sql b/contrib/cube/cube--unpackaged--1.0.sql new file mode 100644 index 0000000000..866c18a289 --- /dev/null +++ b/contrib/cube/cube--unpackaged--1.0.sql @@ -0,0 +1,53 @@ +/* contrib/cube/cube--unpackaged--1.0.sql */ + +ALTER EXTENSION cube ADD type cube; +ALTER EXTENSION cube ADD function cube_in(cstring); +ALTER EXTENSION cube ADD function cube(double precision[],double precision[]); +ALTER EXTENSION cube ADD function cube(double precision[]); +ALTER EXTENSION cube ADD function cube_out(cube); +ALTER EXTENSION cube ADD function cube_eq(cube,cube); +ALTER EXTENSION cube ADD function cube_ne(cube,cube); +ALTER EXTENSION cube ADD function cube_lt(cube,cube); +ALTER EXTENSION cube ADD function cube_gt(cube,cube); +ALTER EXTENSION cube ADD function cube_le(cube,cube); +ALTER EXTENSION cube ADD function cube_ge(cube,cube); +ALTER EXTENSION cube ADD function cube_cmp(cube,cube); +ALTER EXTENSION cube ADD function cube_contains(cube,cube); +ALTER EXTENSION cube ADD function cube_contained(cube,cube); +ALTER EXTENSION cube ADD function cube_overlap(cube,cube); +ALTER EXTENSION cube ADD function cube_union(cube,cube); +ALTER EXTENSION cube ADD function cube_inter(cube,cube); +ALTER EXTENSION cube ADD function cube_size(cube); +ALTER EXTENSION cube ADD function cube_subset(cube,integer[]); +ALTER EXTENSION cube ADD function cube_distance(cube,cube); +ALTER EXTENSION cube ADD function cube_dim(cube); +ALTER EXTENSION cube ADD function cube_ll_coord(cube,integer); +ALTER EXTENSION cube ADD function cube_ur_coord(cube,integer); +ALTER EXTENSION cube ADD function cube(double precision); +ALTER EXTENSION cube ADD function cube(double precision,double precision); +ALTER EXTENSION cube ADD function cube(cube,double precision); +ALTER EXTENSION cube ADD function cube(cube,double precision,double precision); +ALTER EXTENSION cube ADD function cube_is_point(cube); +ALTER EXTENSION cube ADD function cube_enlarge(cube,double precision,integer); +ALTER EXTENSION cube ADD operator >(cube,cube); +ALTER EXTENSION cube ADD operator >=(cube,cube); +ALTER EXTENSION cube ADD operator <(cube,cube); +ALTER EXTENSION cube ADD operator <=(cube,cube); +ALTER EXTENSION cube ADD operator &&(cube,cube); +ALTER EXTENSION cube ADD operator <>(cube,cube); +ALTER EXTENSION cube ADD operator =(cube,cube); +ALTER EXTENSION cube ADD operator <@(cube,cube); +ALTER EXTENSION cube ADD operator @>(cube,cube); +ALTER EXTENSION cube ADD operator ~(cube,cube); +ALTER EXTENSION cube ADD operator @(cube,cube); +ALTER EXTENSION cube ADD function g_cube_consistent(internal,cube,integer,oid,internal); +ALTER EXTENSION cube ADD function g_cube_compress(internal); +ALTER EXTENSION cube ADD function g_cube_decompress(internal); +ALTER EXTENSION cube ADD function g_cube_penalty(internal,internal,internal); +ALTER EXTENSION cube ADD function g_cube_picksplit(internal,internal); +ALTER EXTENSION cube ADD function g_cube_union(internal,internal); +ALTER EXTENSION cube ADD function g_cube_same(cube,cube,internal); +ALTER EXTENSION cube ADD operator family cube_ops using btree; +ALTER EXTENSION cube ADD operator class cube_ops using btree; +ALTER EXTENSION cube ADD operator family gist_cube_ops using gist; +ALTER EXTENSION cube ADD operator class gist_cube_ops using gist; diff --git a/contrib/cube/cube.control b/contrib/cube/cube.control new file mode 100644 index 0000000000..ddc8d2e5d1 --- /dev/null +++ b/contrib/cube/cube.control @@ -0,0 +1,5 @@ +# cube extension +comment = 'data type for multidimensional cubes' +default_version = '1.0' +module_pathname = '$libdir/cube' +relocatable = true diff --git a/contrib/cube/expected/cube.out b/contrib/cube/expected/cube.out index ae7b5b22c2..05cf3eae3c 100644 --- a/contrib/cube/expected/cube.out +++ b/contrib/cube/expected/cube.out @@ -1,13 +1,7 @@ -- -- Test cube datatype -- --- --- first, define the datatype. Turn off echoing so that expected file --- does not depend on contents of cube.sql. --- -SET client_min_messages = warning; -\set ECHO none -RESET client_min_messages; +CREATE EXTENSION cube; -- -- testing the input and output functions -- diff --git a/contrib/cube/expected/cube_1.out b/contrib/cube/expected/cube_1.out index f27e832d63..fefebf5fb9 100644 --- a/contrib/cube/expected/cube_1.out +++ b/contrib/cube/expected/cube_1.out @@ -1,13 +1,7 @@ -- -- Test cube datatype -- --- --- first, define the datatype. Turn off echoing so that expected file --- does not depend on contents of cube.sql. --- -SET client_min_messages = warning; -\set ECHO none -RESET client_min_messages; +CREATE EXTENSION cube; -- -- testing the input and output functions -- diff --git a/contrib/cube/expected/cube_2.out b/contrib/cube/expected/cube_2.out index f534ccf0b5..6d15d63570 100644 --- a/contrib/cube/expected/cube_2.out +++ b/contrib/cube/expected/cube_2.out @@ -1,13 +1,7 @@ -- -- Test cube datatype -- --- --- first, define the datatype. Turn off echoing so that expected file --- does not depend on contents of cube.sql. --- -SET client_min_messages = warning; -\set ECHO none -RESET client_min_messages; +CREATE EXTENSION cube; -- -- testing the input and output functions -- diff --git a/contrib/cube/sql/cube.sql b/contrib/cube/sql/cube.sql index 5c12183dfd..02e068edf4 100644 --- a/contrib/cube/sql/cube.sql +++ b/contrib/cube/sql/cube.sql @@ -2,15 +2,7 @@ -- Test cube datatype -- --- --- first, define the datatype. Turn off echoing so that expected file --- does not depend on contents of cube.sql. --- -SET client_min_messages = warning; -\set ECHO none -\i cube.sql -\set ECHO all -RESET client_min_messages; +CREATE EXTENSION cube; -- -- testing the input and output functions diff --git a/contrib/cube/uninstall_cube.sql b/contrib/cube/uninstall_cube.sql deleted file mode 100644 index aa7119e0d0..0000000000 --- a/contrib/cube/uninstall_cube.sql +++ /dev/null @@ -1,98 +0,0 @@ -/* contrib/cube/uninstall_cube.sql */ - --- Adjust this setting to control where the objects get dropped. -SET search_path = public; - -DROP OPERATOR CLASS gist_cube_ops USING gist; - -DROP OPERATOR CLASS cube_ops USING btree; - -DROP FUNCTION g_cube_same(cube, cube, internal); - -DROP FUNCTION g_cube_union(internal, internal); - -DROP FUNCTION g_cube_picksplit(internal, internal); - -DROP FUNCTION g_cube_penalty(internal,internal,internal); - -DROP FUNCTION g_cube_decompress(internal); - -DROP FUNCTION g_cube_compress(internal); - -DROP FUNCTION g_cube_consistent(internal,cube,int,oid,internal); - -DROP OPERATOR ~ (cube, cube); - -DROP OPERATOR @ (cube, cube); - -DROP OPERATOR <@ (cube, cube); - -DROP OPERATOR @> (cube, cube); - -DROP OPERATOR <> (cube, cube); - -DROP OPERATOR = (cube, cube); - -DROP OPERATOR && (cube, cube); - -DROP OPERATOR >= (cube, cube); - -DROP OPERATOR <= (cube, cube); - -DROP OPERATOR > (cube, cube); - -DROP OPERATOR < (cube, cube); - -DROP FUNCTION cube_enlarge(cube, float8, int4); - -DROP FUNCTION cube_is_point(cube); - -DROP FUNCTION cube(cube, float8, float8); - -DROP FUNCTION cube(cube, float8); - -DROP FUNCTION cube(float8, float8); - -DROP FUNCTION cube(float8[], float8[]); - -DROP FUNCTION cube(float8[]); - -DROP FUNCTION cube_subset(cube, int4[]); - -DROP FUNCTION cube(float8); - -DROP FUNCTION cube_ur_coord(cube, int4); - -DROP FUNCTION cube_ll_coord(cube, int4); - -DROP FUNCTION cube_dim(cube); - -DROP FUNCTION cube_distance(cube, cube); - -DROP FUNCTION cube_size(cube); - -DROP FUNCTION cube_inter(cube, cube); - -DROP FUNCTION cube_union(cube, cube); - -DROP FUNCTION cube_overlap(cube, cube); - -DROP FUNCTION cube_contained(cube, cube); - -DROP FUNCTION cube_contains(cube, cube); - -DROP FUNCTION cube_cmp(cube, cube); - -DROP FUNCTION cube_ge(cube, cube); - -DROP FUNCTION cube_le(cube, cube); - -DROP FUNCTION cube_gt(cube, cube); - -DROP FUNCTION cube_lt(cube, cube); - -DROP FUNCTION cube_ne(cube, cube); - -DROP FUNCTION cube_eq(cube, cube); - -DROP TYPE cube CASCADE; diff --git a/contrib/dblink/.gitignore b/contrib/dblink/.gitignore index fb7e8728bc..19b6c5ba42 100644 --- a/contrib/dblink/.gitignore +++ b/contrib/dblink/.gitignore @@ -1,3 +1,2 @@ -/dblink.sql # Generated subdirectories /results/ diff --git a/contrib/dblink/Makefile b/contrib/dblink/Makefile index fdfd03a4cf..ac637480eb 100644 --- a/contrib/dblink/Makefile +++ b/contrib/dblink/Makefile @@ -1,15 +1,15 @@ # contrib/dblink/Makefile MODULE_big = dblink -PG_CPPFLAGS = -I$(libpq_srcdir) OBJS = dblink.o +PG_CPPFLAGS = -I$(libpq_srcdir) SHLIB_LINK = $(libpq) SHLIB_PREREQS = submake-libpq -DATA_built = dblink.sql -DATA = uninstall_dblink.sql -REGRESS = dblink +EXTENSION = dblink +DATA = dblink--1.0.sql dblink--unpackaged--1.0.sql +REGRESS = dblink ifdef USE_PGXS PG_CONFIG = pg_config diff --git a/contrib/dblink/dblink.sql.in b/contrib/dblink/dblink--1.0.sql index 3c9d66e7df..4ac5514461 100644 --- a/contrib/dblink/dblink.sql.in +++ b/contrib/dblink/dblink--1.0.sql @@ -1,16 +1,13 @@ -/* contrib/dblink/dblink.sql.in */ - --- Adjust this setting to control where the objects get created. -SET search_path = public; +/* contrib/dblink/dblink--1.0.sql */ -- dblink_connect now restricts non-superusers to password -- authenticated connections -CREATE OR REPLACE FUNCTION dblink_connect (text) +CREATE FUNCTION dblink_connect (text) RETURNS text AS 'MODULE_PATHNAME','dblink_connect' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dblink_connect (text, text) +CREATE FUNCTION dblink_connect (text, text) RETURNS text AS 'MODULE_PATHNAME','dblink_connect' LANGUAGE C STRICT; @@ -18,12 +15,12 @@ LANGUAGE C STRICT; -- dblink_connect_u allows non-superusers to use -- non-password authenticated connections, but initially -- privileges are revoked from public -CREATE OR REPLACE FUNCTION dblink_connect_u (text) +CREATE FUNCTION dblink_connect_u (text) RETURNS text AS 'MODULE_PATHNAME','dblink_connect' LANGUAGE C STRICT SECURITY DEFINER; -CREATE OR REPLACE FUNCTION dblink_connect_u (text, text) +CREATE FUNCTION dblink_connect_u (text, text) RETURNS text AS 'MODULE_PATHNAME','dblink_connect' LANGUAGE C STRICT SECURITY DEFINER; @@ -31,179 +28,179 @@ LANGUAGE C STRICT SECURITY DEFINER; REVOKE ALL ON FUNCTION dblink_connect_u (text) FROM public; REVOKE ALL ON FUNCTION dblink_connect_u (text, text) FROM public; -CREATE OR REPLACE FUNCTION dblink_disconnect () +CREATE FUNCTION dblink_disconnect () RETURNS text AS 'MODULE_PATHNAME','dblink_disconnect' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dblink_disconnect (text) +CREATE FUNCTION dblink_disconnect (text) RETURNS text AS 'MODULE_PATHNAME','dblink_disconnect' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dblink_open (text, text) +CREATE FUNCTION dblink_open (text, text) RETURNS text AS 'MODULE_PATHNAME','dblink_open' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dblink_open (text, text, boolean) +CREATE FUNCTION dblink_open (text, text, boolean) RETURNS text AS 'MODULE_PATHNAME','dblink_open' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dblink_open (text, text, text) +CREATE FUNCTION dblink_open (text, text, text) RETURNS text AS 'MODULE_PATHNAME','dblink_open' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dblink_open (text, text, text, boolean) +CREATE FUNCTION dblink_open (text, text, text, boolean) RETURNS text AS 'MODULE_PATHNAME','dblink_open' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dblink_fetch (text, int) +CREATE FUNCTION dblink_fetch (text, int) RETURNS setof record AS 'MODULE_PATHNAME','dblink_fetch' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dblink_fetch (text, int, boolean) +CREATE FUNCTION dblink_fetch (text, int, boolean) RETURNS setof record AS 'MODULE_PATHNAME','dblink_fetch' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dblink_fetch (text, text, int) +CREATE FUNCTION dblink_fetch (text, text, int) RETURNS setof record AS 'MODULE_PATHNAME','dblink_fetch' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dblink_fetch (text, text, int, boolean) +CREATE FUNCTION dblink_fetch (text, text, int, boolean) RETURNS setof record AS 'MODULE_PATHNAME','dblink_fetch' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dblink_close (text) +CREATE FUNCTION dblink_close (text) RETURNS text AS 'MODULE_PATHNAME','dblink_close' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dblink_close (text, boolean) +CREATE FUNCTION dblink_close (text, boolean) RETURNS text AS 'MODULE_PATHNAME','dblink_close' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dblink_close (text, text) +CREATE FUNCTION dblink_close (text, text) RETURNS text AS 'MODULE_PATHNAME','dblink_close' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dblink_close (text, text, boolean) +CREATE FUNCTION dblink_close (text, text, boolean) RETURNS text AS 'MODULE_PATHNAME','dblink_close' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dblink (text, text) +CREATE FUNCTION dblink (text, text) RETURNS setof record AS 'MODULE_PATHNAME','dblink_record' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dblink (text, text, boolean) +CREATE FUNCTION dblink (text, text, boolean) RETURNS setof record AS 'MODULE_PATHNAME','dblink_record' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dblink (text) +CREATE FUNCTION dblink (text) RETURNS setof record AS 'MODULE_PATHNAME','dblink_record' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dblink (text, boolean) +CREATE FUNCTION dblink (text, boolean) RETURNS setof record AS 'MODULE_PATHNAME','dblink_record' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dblink_exec (text, text) +CREATE FUNCTION dblink_exec (text, text) RETURNS text AS 'MODULE_PATHNAME','dblink_exec' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dblink_exec (text, text, boolean) +CREATE FUNCTION dblink_exec (text, text, boolean) RETURNS text AS 'MODULE_PATHNAME','dblink_exec' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dblink_exec (text) +CREATE FUNCTION dblink_exec (text) RETURNS text AS 'MODULE_PATHNAME','dblink_exec' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dblink_exec (text,boolean) +CREATE FUNCTION dblink_exec (text,boolean) RETURNS text AS 'MODULE_PATHNAME','dblink_exec' LANGUAGE C STRICT; CREATE TYPE dblink_pkey_results AS (position int, colname text); -CREATE OR REPLACE FUNCTION dblink_get_pkey (text) +CREATE FUNCTION dblink_get_pkey (text) RETURNS setof dblink_pkey_results AS 'MODULE_PATHNAME','dblink_get_pkey' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dblink_build_sql_insert (text, int2vector, int, _text, _text) +CREATE FUNCTION dblink_build_sql_insert (text, int2vector, int, _text, _text) RETURNS text AS 'MODULE_PATHNAME','dblink_build_sql_insert' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dblink_build_sql_delete (text, int2vector, int, _text) +CREATE FUNCTION dblink_build_sql_delete (text, int2vector, int, _text) RETURNS text AS 'MODULE_PATHNAME','dblink_build_sql_delete' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dblink_build_sql_update (text, int2vector, int, _text, _text) +CREATE FUNCTION dblink_build_sql_update (text, int2vector, int, _text, _text) RETURNS text AS 'MODULE_PATHNAME','dblink_build_sql_update' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dblink_current_query () +CREATE FUNCTION dblink_current_query () RETURNS text AS 'MODULE_PATHNAME','dblink_current_query' LANGUAGE C; -CREATE OR REPLACE FUNCTION dblink_send_query(text, text) +CREATE FUNCTION dblink_send_query(text, text) RETURNS int4 AS 'MODULE_PATHNAME', 'dblink_send_query' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dblink_is_busy(text) +CREATE FUNCTION dblink_is_busy(text) RETURNS int4 AS 'MODULE_PATHNAME', 'dblink_is_busy' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dblink_get_result(text) +CREATE FUNCTION dblink_get_result(text) RETURNS SETOF record AS 'MODULE_PATHNAME', 'dblink_get_result' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dblink_get_result(text, bool) +CREATE FUNCTION dblink_get_result(text, bool) RETURNS SETOF record AS 'MODULE_PATHNAME', 'dblink_get_result' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dblink_get_connections() +CREATE FUNCTION dblink_get_connections() RETURNS text[] AS 'MODULE_PATHNAME', 'dblink_get_connections' LANGUAGE C; -CREATE OR REPLACE FUNCTION dblink_cancel_query(text) +CREATE FUNCTION dblink_cancel_query(text) RETURNS text AS 'MODULE_PATHNAME', 'dblink_cancel_query' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dblink_error_message(text) +CREATE FUNCTION dblink_error_message(text) RETURNS text AS 'MODULE_PATHNAME', 'dblink_error_message' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dblink_get_notify( +CREATE FUNCTION dblink_get_notify( OUT notify_name TEXT, OUT be_pid INT4, OUT extra TEXT @@ -212,7 +209,7 @@ RETURNS setof record AS 'MODULE_PATHNAME', 'dblink_get_notify' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dblink_get_notify( +CREATE FUNCTION dblink_get_notify( conname TEXT, OUT notify_name TEXT, OUT be_pid INT4, diff --git a/contrib/dblink/dblink--unpackaged--1.0.sql b/contrib/dblink/dblink--unpackaged--1.0.sql new file mode 100644 index 0000000000..b6d184b4a2 --- /dev/null +++ b/contrib/dblink/dblink--unpackaged--1.0.sql @@ -0,0 +1,43 @@ +/* contrib/dblink/dblink--unpackaged--1.0.sql */ + +ALTER EXTENSION dblink ADD function dblink_connect(text); +ALTER EXTENSION dblink ADD function dblink_connect(text,text); +ALTER EXTENSION dblink ADD function dblink_connect_u(text); +ALTER EXTENSION dblink ADD function dblink_connect_u(text,text); +ALTER EXTENSION dblink ADD function dblink_disconnect(); +ALTER EXTENSION dblink ADD function dblink_disconnect(text); +ALTER EXTENSION dblink ADD function dblink_open(text,text); +ALTER EXTENSION dblink ADD function dblink_open(text,text,boolean); +ALTER EXTENSION dblink ADD function dblink_open(text,text,text); +ALTER EXTENSION dblink ADD function dblink_open(text,text,text,boolean); +ALTER EXTENSION dblink ADD function dblink_fetch(text,integer); +ALTER EXTENSION dblink ADD function dblink_fetch(text,integer,boolean); +ALTER EXTENSION dblink ADD function dblink_fetch(text,text,integer); +ALTER EXTENSION dblink ADD function dblink_fetch(text,text,integer,boolean); +ALTER EXTENSION dblink ADD function dblink_close(text); +ALTER EXTENSION dblink ADD function dblink_close(text,boolean); +ALTER EXTENSION dblink ADD function dblink_close(text,text); +ALTER EXTENSION dblink ADD function dblink_close(text,text,boolean); +ALTER EXTENSION dblink ADD function dblink(text,text); +ALTER EXTENSION dblink ADD function dblink(text,text,boolean); +ALTER EXTENSION dblink ADD function dblink(text); +ALTER EXTENSION dblink ADD function dblink(text,boolean); +ALTER EXTENSION dblink ADD function dblink_exec(text,text); +ALTER EXTENSION dblink ADD function dblink_exec(text,text,boolean); +ALTER EXTENSION dblink ADD function dblink_exec(text); +ALTER EXTENSION dblink ADD function dblink_exec(text,boolean); +ALTER EXTENSION dblink ADD type dblink_pkey_results; +ALTER EXTENSION dblink ADD function dblink_get_pkey(text); +ALTER EXTENSION dblink ADD function dblink_build_sql_insert(text,int2vector,integer,text[],text[]); +ALTER EXTENSION dblink ADD function dblink_build_sql_delete(text,int2vector,integer,text[]); +ALTER EXTENSION dblink ADD function dblink_build_sql_update(text,int2vector,integer,text[],text[]); +ALTER EXTENSION dblink ADD function dblink_current_query(); +ALTER EXTENSION dblink ADD function dblink_send_query(text,text); +ALTER EXTENSION dblink ADD function dblink_is_busy(text); +ALTER EXTENSION dblink ADD function dblink_get_result(text); +ALTER EXTENSION dblink ADD function dblink_get_result(text,boolean); +ALTER EXTENSION dblink ADD function dblink_get_connections(); +ALTER EXTENSION dblink ADD function dblink_cancel_query(text); +ALTER EXTENSION dblink ADD function dblink_error_message(text); +ALTER EXTENSION dblink ADD function dblink_get_notify(); +ALTER EXTENSION dblink ADD function dblink_get_notify(text); diff --git a/contrib/dblink/dblink.control b/contrib/dblink/dblink.control new file mode 100644 index 0000000000..4333a9b618 --- /dev/null +++ b/contrib/dblink/dblink.control @@ -0,0 +1,5 @@ +# dblink extension +comment = 'connect to other PostgreSQL databases from within a database' +default_version = '1.0' +module_pathname = '$libdir/dblink' +relocatable = true diff --git a/contrib/dblink/expected/dblink.out b/contrib/dblink/expected/dblink.out index 15848dd922..511dd5efcf 100644 --- a/contrib/dblink/expected/dblink.out +++ b/contrib/dblink/expected/dblink.out @@ -1,14 +1,4 @@ --- Adjust this setting to control where the objects get created. -SET search_path = public; --- --- Define the functions and test data --- therein. --- --- Turn off echoing so that expected file does not depend on --- contents of dblink.sql. -SET client_min_messages = warning; -\set ECHO none -RESET client_min_messages; +CREATE EXTENSION dblink; CREATE TABLE foo(f1 int, f2 text, f3 text[], primary key (f1,f2)); NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "foo_pkey" for table "foo" INSERT INTO foo VALUES (0,'a','{"a0","b0","c0"}'); diff --git a/contrib/dblink/sql/dblink.sql b/contrib/dblink/sql/dblink.sql index 062bc9ee0e..8c8ffe233c 100644 --- a/contrib/dblink/sql/dblink.sql +++ b/contrib/dblink/sql/dblink.sql @@ -1,17 +1,4 @@ --- Adjust this setting to control where the objects get created. -SET search_path = public; - --- --- Define the functions and test data --- therein. --- --- Turn off echoing so that expected file does not depend on --- contents of dblink.sql. -SET client_min_messages = warning; -\set ECHO none -\i dblink.sql -\set ECHO all -RESET client_min_messages; +CREATE EXTENSION dblink; CREATE TABLE foo(f1 int, f2 text, f3 text[], primary key (f1,f2)); INSERT INTO foo VALUES (0,'a','{"a0","b0","c0"}'); diff --git a/contrib/dblink/uninstall_dblink.sql b/contrib/dblink/uninstall_dblink.sql deleted file mode 100644 index 365728a6d7..0000000000 --- a/contrib/dblink/uninstall_dblink.sql +++ /dev/null @@ -1,86 +0,0 @@ -/* contrib/dblink/uninstall_dblink.sql */ - --- Adjust this setting to control where the objects get dropped. -SET search_path = public; - -DROP FUNCTION dblink_current_query (); - -DROP FUNCTION dblink_build_sql_update (text, int2vector, int4, _text, _text); - -DROP FUNCTION dblink_build_sql_delete (text, int2vector, int4, _text); - -DROP FUNCTION dblink_build_sql_insert (text, int2vector, int4, _text, _text); - -DROP FUNCTION dblink_get_pkey (text); - -DROP TYPE dblink_pkey_results; - -DROP FUNCTION dblink_exec (text,bool); - -DROP FUNCTION dblink_exec (text); - -DROP FUNCTION dblink_exec (text,text,bool); - -DROP FUNCTION dblink_exec (text,text); - -DROP FUNCTION dblink (text,bool); - -DROP FUNCTION dblink (text); - -DROP FUNCTION dblink (text,text,bool); - -DROP FUNCTION dblink (text,text); - -DROP FUNCTION dblink_close (text,text,bool); - -DROP FUNCTION dblink_close (text,text); - -DROP FUNCTION dblink_close (text,bool); - -DROP FUNCTION dblink_close (text); - -DROP FUNCTION dblink_fetch (text,text,int,bool); - -DROP FUNCTION dblink_fetch (text,text,int); - -DROP FUNCTION dblink_fetch (text,int,bool); - -DROP FUNCTION dblink_fetch (text,int); - -DROP FUNCTION dblink_open (text,text,text,bool); - -DROP FUNCTION dblink_open (text,text,text); - -DROP FUNCTION dblink_open (text,text,bool); - -DROP FUNCTION dblink_open (text,text); - -DROP FUNCTION dblink_disconnect (text); - -DROP FUNCTION dblink_disconnect (); - -DROP FUNCTION dblink_connect (text, text); - -DROP FUNCTION dblink_connect (text); - -DROP FUNCTION dblink_connect_u (text, text); - -DROP FUNCTION dblink_connect_u (text); - -DROP FUNCTION dblink_cancel_query(text); - -DROP FUNCTION dblink_error_message(text); - -DROP FUNCTION dblink_get_connections(); - -DROP FUNCTION dblink_get_result(text); - -DROP FUNCTION dblink_get_result(text, boolean); - -DROP FUNCTION dblink_is_busy(text); - -DROP FUNCTION dblink_send_query(text, text); - -DROP FUNCTION dblink_get_notify(); - -DROP FUNCTION dblink_get_notify(text); diff --git a/contrib/dict_int/.gitignore b/contrib/dict_int/.gitignore index 932dda6d84..19b6c5ba42 100644 --- a/contrib/dict_int/.gitignore +++ b/contrib/dict_int/.gitignore @@ -1,3 +1,2 @@ -/dict_int.sql # Generated subdirectories /results/ diff --git a/contrib/dict_int/Makefile b/contrib/dict_int/Makefile index 17d9eaa5f7..3a3fc368dc 100644 --- a/contrib/dict_int/Makefile +++ b/contrib/dict_int/Makefile @@ -2,8 +2,10 @@ MODULE_big = dict_int OBJS = dict_int.o -DATA_built = dict_int.sql -DATA = uninstall_dict_int.sql + +EXTENSION = dict_int +DATA = dict_int--1.0.sql dict_int--unpackaged--1.0.sql + REGRESS = dict_int ifdef USE_PGXS diff --git a/contrib/dict_int/dict_int.sql.in b/contrib/dict_int/dict_int--1.0.sql index 9d7ef7d9c1..585a56552d 100644 --- a/contrib/dict_int/dict_int.sql.in +++ b/contrib/dict_int/dict_int--1.0.sql @@ -1,14 +1,11 @@ -/* contrib/dict_int/dict_int.sql.in */ +/* contrib/dict_int/dict_int--1.0.sql */ --- Adjust this setting to control where the objects get created. -SET search_path = public; - -CREATE OR REPLACE FUNCTION dintdict_init(internal) +CREATE FUNCTION dintdict_init(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dintdict_lexize(internal, internal, internal, internal) +CREATE FUNCTION dintdict_lexize(internal, internal, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT; diff --git a/contrib/dict_int/dict_int--unpackaged--1.0.sql b/contrib/dict_int/dict_int--unpackaged--1.0.sql new file mode 100644 index 0000000000..f89218a565 --- /dev/null +++ b/contrib/dict_int/dict_int--unpackaged--1.0.sql @@ -0,0 +1,6 @@ +/* contrib/dict_int/dict_int--unpackaged--1.0.sql */ + +ALTER EXTENSION dict_int ADD function dintdict_init(internal); +ALTER EXTENSION dict_int ADD function dintdict_lexize(internal,internal,internal,internal); +ALTER EXTENSION dict_int ADD text search template intdict_template; +ALTER EXTENSION dict_int ADD text search dictionary intdict; diff --git a/contrib/dict_int/dict_int.control b/contrib/dict_int/dict_int.control new file mode 100644 index 0000000000..6e2d2b351a --- /dev/null +++ b/contrib/dict_int/dict_int.control @@ -0,0 +1,5 @@ +# dict_int extension +comment = 'text search dictionary template for integers' +default_version = '1.0' +module_pathname = '$libdir/dict_int' +relocatable = true diff --git a/contrib/dict_int/expected/dict_int.out b/contrib/dict_int/expected/dict_int.out index 7feb493e15..3b766ec52a 100644 --- a/contrib/dict_int/expected/dict_int.out +++ b/contrib/dict_int/expected/dict_int.out @@ -1,10 +1,4 @@ --- --- first, define the datatype. Turn off echoing so that expected file --- does not depend on contents of this file. --- -SET client_min_messages = warning; -\set ECHO none -RESET client_min_messages; +CREATE EXTENSION dict_int; --lexize select ts_lexize('intdict', '511673'); ts_lexize diff --git a/contrib/dict_int/sql/dict_int.sql b/contrib/dict_int/sql/dict_int.sql index 3a335f8f3d..8ffec6b770 100644 --- a/contrib/dict_int/sql/dict_int.sql +++ b/contrib/dict_int/sql/dict_int.sql @@ -1,12 +1,4 @@ --- --- first, define the datatype. Turn off echoing so that expected file --- does not depend on contents of this file. --- -SET client_min_messages = warning; -\set ECHO none -\i dict_int.sql -\set ECHO all -RESET client_min_messages; +CREATE EXTENSION dict_int; --lexize select ts_lexize('intdict', '511673'); diff --git a/contrib/dict_int/uninstall_dict_int.sql b/contrib/dict_int/uninstall_dict_int.sql deleted file mode 100644 index 0467fa22ba..0000000000 --- a/contrib/dict_int/uninstall_dict_int.sql +++ /dev/null @@ -1,12 +0,0 @@ -/* contrib/dict_int/uninstall_dict_int.sql */ - --- Adjust this setting to control where the objects get dropped. -SET search_path = public; - -DROP TEXT SEARCH DICTIONARY intdict; - -DROP TEXT SEARCH TEMPLATE intdict_template; - -DROP FUNCTION dintdict_init(internal); - -DROP FUNCTION dintdict_lexize(internal,internal,internal,internal); diff --git a/contrib/dict_xsyn/.gitignore b/contrib/dict_xsyn/.gitignore index 0ebd61caaf..19b6c5ba42 100644 --- a/contrib/dict_xsyn/.gitignore +++ b/contrib/dict_xsyn/.gitignore @@ -1,3 +1,2 @@ -/dict_xsyn.sql # Generated subdirectories /results/ diff --git a/contrib/dict_xsyn/Makefile b/contrib/dict_xsyn/Makefile index 8b737f09fc..ce92baa478 100644 --- a/contrib/dict_xsyn/Makefile +++ b/contrib/dict_xsyn/Makefile @@ -2,9 +2,11 @@ MODULE_big = dict_xsyn OBJS = dict_xsyn.o -DATA_built = dict_xsyn.sql -DATA = uninstall_dict_xsyn.sql + +EXTENSION = dict_xsyn +DATA = dict_xsyn--1.0.sql dict_xsyn--unpackaged--1.0.sql DATA_TSEARCH = xsyn_sample.rules + REGRESS = dict_xsyn ifdef USE_PGXS diff --git a/contrib/dict_xsyn/dict_xsyn.sql.in b/contrib/dict_xsyn/dict_xsyn--1.0.sql index 7d48c9209f..30eaff4db5 100644 --- a/contrib/dict_xsyn/dict_xsyn.sql.in +++ b/contrib/dict_xsyn/dict_xsyn--1.0.sql @@ -1,14 +1,11 @@ -/* contrib/dict_xsyn/dict_xsyn.sql.in */ +/* contrib/dict_xsyn/dict_xsyn--1.0.sql */ --- Adjust this setting to control where the objects get created. -SET search_path = public; - -CREATE OR REPLACE FUNCTION dxsyn_init(internal) +CREATE FUNCTION dxsyn_init(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION dxsyn_lexize(internal, internal, internal, internal) +CREATE FUNCTION dxsyn_lexize(internal, internal, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT; diff --git a/contrib/dict_xsyn/dict_xsyn--unpackaged--1.0.sql b/contrib/dict_xsyn/dict_xsyn--unpackaged--1.0.sql new file mode 100644 index 0000000000..6fe0285f79 --- /dev/null +++ b/contrib/dict_xsyn/dict_xsyn--unpackaged--1.0.sql @@ -0,0 +1,6 @@ +/* contrib/dict_xsyn/dict_xsyn--unpackaged--1.0.sql */ + +ALTER EXTENSION dict_xsyn ADD function dxsyn_init(internal); +ALTER EXTENSION dict_xsyn ADD function dxsyn_lexize(internal,internal,internal,internal); +ALTER EXTENSION dict_xsyn ADD text search template xsyn_template; +ALTER EXTENSION dict_xsyn ADD text search dictionary xsyn; diff --git a/contrib/dict_xsyn/dict_xsyn.control b/contrib/dict_xsyn/dict_xsyn.control new file mode 100644 index 0000000000..3fd465a955 --- /dev/null +++ b/contrib/dict_xsyn/dict_xsyn.control @@ -0,0 +1,5 @@ +# dict_xsyn extension +comment = 'text search dictionary template for extended synonym processing' +default_version = '1.0' +module_pathname = '$libdir/dict_xsyn' +relocatable = true diff --git a/contrib/dict_xsyn/expected/dict_xsyn.out b/contrib/dict_xsyn/expected/dict_xsyn.out index d91697a97e..9b95e13559 100644 --- a/contrib/dict_xsyn/expected/dict_xsyn.out +++ b/contrib/dict_xsyn/expected/dict_xsyn.out @@ -1,10 +1,4 @@ --- --- first, define the datatype. Turn off echoing so that expected file --- does not depend on contents of this file. --- -SET client_min_messages = warning; -\set ECHO none -RESET client_min_messages; +CREATE EXTENSION dict_xsyn; -- default configuration - match first word and return it among with all synonyms ALTER TEXT SEARCH DICTIONARY xsyn (RULES='xsyn_sample', KEEPORIG=true, MATCHORIG=true, KEEPSYNONYMS=true, MATCHSYNONYMS=false); --lexize diff --git a/contrib/dict_xsyn/sql/dict_xsyn.sql b/contrib/dict_xsyn/sql/dict_xsyn.sql index 9db0851700..49511061d0 100644 --- a/contrib/dict_xsyn/sql/dict_xsyn.sql +++ b/contrib/dict_xsyn/sql/dict_xsyn.sql @@ -1,12 +1,4 @@ --- --- first, define the datatype. Turn off echoing so that expected file --- does not depend on contents of this file. --- -SET client_min_messages = warning; -\set ECHO none -\i dict_xsyn.sql -\set ECHO all -RESET client_min_messages; +CREATE EXTENSION dict_xsyn; -- default configuration - match first word and return it among with all synonyms ALTER TEXT SEARCH DICTIONARY xsyn (RULES='xsyn_sample', KEEPORIG=true, MATCHORIG=true, KEEPSYNONYMS=true, MATCHSYNONYMS=false); diff --git a/contrib/dict_xsyn/uninstall_dict_xsyn.sql b/contrib/dict_xsyn/uninstall_dict_xsyn.sql deleted file mode 100644 index 68f9579c05..0000000000 --- a/contrib/dict_xsyn/uninstall_dict_xsyn.sql +++ /dev/null @@ -1,12 +0,0 @@ -/* contrib/dict_xsyn/uninstall_dict_xsyn.sql */ - --- Adjust this setting to control where the objects get dropped. -SET search_path = public; - -DROP TEXT SEARCH DICTIONARY xsyn; - -DROP TEXT SEARCH TEMPLATE xsyn_template; - -DROP FUNCTION dxsyn_init(internal); - -DROP FUNCTION dxsyn_lexize(internal,internal,internal,internal); diff --git a/contrib/earthdistance/.gitignore b/contrib/earthdistance/.gitignore index 366a0a399e..19b6c5ba42 100644 --- a/contrib/earthdistance/.gitignore +++ b/contrib/earthdistance/.gitignore @@ -1,3 +1,2 @@ -/earthdistance.sql # Generated subdirectories /results/ diff --git a/contrib/earthdistance/Makefile b/contrib/earthdistance/Makefile index 8328e5f828..49f6e6675f 100644 --- a/contrib/earthdistance/Makefile +++ b/contrib/earthdistance/Makefile @@ -1,8 +1,10 @@ # contrib/earthdistance/Makefile MODULES = earthdistance -DATA_built = earthdistance.sql -DATA = uninstall_earthdistance.sql + +EXTENSION = earthdistance +DATA = earthdistance--1.0.sql earthdistance--unpackaged--1.0.sql + REGRESS = earthdistance LDFLAGS_SL += $(filter -lm, $(LIBS)) diff --git a/contrib/earthdistance/earthdistance.sql.in b/contrib/earthdistance/earthdistance--1.0.sql index a4ce812584..71e4025f6f 100644 --- a/contrib/earthdistance/earthdistance.sql.in +++ b/contrib/earthdistance/earthdistance--1.0.sql @@ -1,15 +1,10 @@ -/* contrib/earthdistance/earthdistance.sql.in */ - --- Adjust this setting to control where the objects get created. -SET search_path = public; - --- The earth functions rely on contrib/cube having been installed and loaded. +/* contrib/earthdistance/earthdistance--1.0.sql */ -- earth() returns the radius of the earth in meters. This is the only -- place you need to change things for the cube base distance functions -- in order to use different units (or a better value for the Earth's radius). -CREATE OR REPLACE FUNCTION earth() RETURNS float8 +CREATE FUNCTION earth() RETURNS float8 LANGUAGE SQL IMMUTABLE AS 'SELECT ''6378168''::float8'; @@ -18,7 +13,7 @@ AS 'SELECT ''6378168''::float8'; -- uncomment the one below. Note that doing this will break the regression -- tests. -- --- CREATE OR REPLACE FUNCTION earth() RETURNS float8 +-- CREATE FUNCTION earth() RETURNS float8 -- LANGUAGE SQL IMMUTABLE -- AS 'SELECT 180/pi()'; @@ -35,43 +30,43 @@ CREATE DOMAIN earth AS cube CONSTRAINT on_surface check(abs(cube_distance(value, '(0)'::cube) / earth() - 1) < '10e-7'::float8); -CREATE OR REPLACE FUNCTION sec_to_gc(float8) +CREATE FUNCTION sec_to_gc(float8) RETURNS float8 LANGUAGE SQL IMMUTABLE STRICT AS 'SELECT CASE WHEN $1 < 0 THEN 0::float8 WHEN $1/(2*earth()) > 1 THEN pi()*earth() ELSE 2*earth()*asin($1/(2*earth())) END'; -CREATE OR REPLACE FUNCTION gc_to_sec(float8) +CREATE FUNCTION gc_to_sec(float8) RETURNS float8 LANGUAGE SQL IMMUTABLE STRICT AS 'SELECT CASE WHEN $1 < 0 THEN 0::float8 WHEN $1/earth() > pi() THEN 2*earth() ELSE 2*earth()*sin($1/(2*earth())) END'; -CREATE OR REPLACE FUNCTION ll_to_earth(float8, float8) +CREATE FUNCTION ll_to_earth(float8, float8) RETURNS earth LANGUAGE SQL IMMUTABLE STRICT AS 'SELECT cube(cube(cube(earth()*cos(radians($1))*cos(radians($2))),earth()*cos(radians($1))*sin(radians($2))),earth()*sin(radians($1)))::earth'; -CREATE OR REPLACE FUNCTION latitude(earth) +CREATE FUNCTION latitude(earth) RETURNS float8 LANGUAGE SQL IMMUTABLE STRICT AS 'SELECT CASE WHEN cube_ll_coord($1, 3)/earth() < -1 THEN -90::float8 WHEN cube_ll_coord($1, 3)/earth() > 1 THEN 90::float8 ELSE degrees(asin(cube_ll_coord($1, 3)/earth())) END'; -CREATE OR REPLACE FUNCTION longitude(earth) +CREATE FUNCTION longitude(earth) RETURNS float8 LANGUAGE SQL IMMUTABLE STRICT AS 'SELECT degrees(atan2(cube_ll_coord($1, 2), cube_ll_coord($1, 1)))'; -CREATE OR REPLACE FUNCTION earth_distance(earth, earth) +CREATE FUNCTION earth_distance(earth, earth) RETURNS float8 LANGUAGE SQL IMMUTABLE STRICT AS 'SELECT sec_to_gc(cube_distance($1, $2))'; -CREATE OR REPLACE FUNCTION earth_box(earth, float8) +CREATE FUNCTION earth_box(earth, float8) RETURNS cube LANGUAGE SQL IMMUTABLE STRICT @@ -79,7 +74,7 @@ AS 'SELECT cube_enlarge($1, gc_to_sec($2), 3)'; --------------- geo_distance -CREATE OR REPLACE FUNCTION geo_distance (point, point) +CREATE FUNCTION geo_distance (point, point) RETURNS float8 LANGUAGE C IMMUTABLE STRICT AS 'MODULE_PATHNAME'; diff --git a/contrib/earthdistance/earthdistance--unpackaged--1.0.sql b/contrib/earthdistance/earthdistance--unpackaged--1.0.sql new file mode 100644 index 0000000000..2d5919cc72 --- /dev/null +++ b/contrib/earthdistance/earthdistance--unpackaged--1.0.sql @@ -0,0 +1,13 @@ +/* contrib/earthdistance/earthdistance--unpackaged--1.0.sql */ + +ALTER EXTENSION earthdistance ADD function earth(); +ALTER EXTENSION earthdistance ADD type earth; +ALTER EXTENSION earthdistance ADD function sec_to_gc(double precision); +ALTER EXTENSION earthdistance ADD function gc_to_sec(double precision); +ALTER EXTENSION earthdistance ADD function ll_to_earth(double precision,double precision); +ALTER EXTENSION earthdistance ADD function latitude(earth); +ALTER EXTENSION earthdistance ADD function longitude(earth); +ALTER EXTENSION earthdistance ADD function earth_distance(earth,earth); +ALTER EXTENSION earthdistance ADD function earth_box(earth,double precision); +ALTER EXTENSION earthdistance ADD function geo_distance(point,point); +ALTER EXTENSION earthdistance ADD operator <@>(point,point); diff --git a/contrib/earthdistance/earthdistance.control b/contrib/earthdistance/earthdistance.control new file mode 100644 index 0000000000..afd2ff4f95 --- /dev/null +++ b/contrib/earthdistance/earthdistance.control @@ -0,0 +1,6 @@ +# earthdistance extension +comment = 'calculate great-circle distances on the surface of the Earth' +default_version = '1.0' +module_pathname = '$libdir/earthdistance' +relocatable = true +requires = 'cube' diff --git a/contrib/earthdistance/expected/earthdistance.out b/contrib/earthdistance/expected/earthdistance.out index 5f5645b700..8a3fc749c1 100644 --- a/contrib/earthdistance/expected/earthdistance.out +++ b/contrib/earthdistance/expected/earthdistance.out @@ -1,13 +1,8 @@ -- -- Test earth distance functions -- --- --- first, define the datatype. Turn off echoing so that expected file --- does not depend on contents of earthdistance.sql or cube.sql. --- -SET client_min_messages = warning; -\set ECHO none -RESET client_min_messages; +CREATE EXTENSION cube; +CREATE EXTENSION earthdistance; -- -- The radius of the Earth we are using. -- diff --git a/contrib/earthdistance/sql/earthdistance.sql b/contrib/earthdistance/sql/earthdistance.sql index ad68b5635d..e494c350ce 100644 --- a/contrib/earthdistance/sql/earthdistance.sql +++ b/contrib/earthdistance/sql/earthdistance.sql @@ -2,16 +2,8 @@ -- Test earth distance functions -- --- --- first, define the datatype. Turn off echoing so that expected file --- does not depend on contents of earthdistance.sql or cube.sql. --- -SET client_min_messages = warning; -\set ECHO none -\i ../cube/cube.sql -\i earthdistance.sql -\set ECHO all -RESET client_min_messages; +CREATE EXTENSION cube; +CREATE EXTENSION earthdistance; -- -- The radius of the Earth we are using. diff --git a/contrib/earthdistance/uninstall_earthdistance.sql b/contrib/earthdistance/uninstall_earthdistance.sql deleted file mode 100644 index dfd7d524ab..0000000000 --- a/contrib/earthdistance/uninstall_earthdistance.sql +++ /dev/null @@ -1,26 +0,0 @@ -/* contrib/earthdistance/uninstall_earthdistance.sql */ - --- Adjust this setting to control where the objects get dropped. -SET search_path = public; - -DROP OPERATOR <@> (point, point); - -DROP FUNCTION geo_distance (point, point); - -DROP FUNCTION earth_box(earth, float8); - -DROP FUNCTION earth_distance(earth, earth); - -DROP FUNCTION longitude(earth); - -DROP FUNCTION latitude(earth); - -DROP FUNCTION ll_to_earth(float8, float8); - -DROP FUNCTION gc_to_sec(float8); - -DROP FUNCTION sec_to_gc(float8); - -DROP DOMAIN earth; - -DROP FUNCTION earth(); diff --git a/contrib/fuzzystrmatch/.gitignore b/contrib/fuzzystrmatch/.gitignore deleted file mode 100644 index f4962c630b..0000000000 --- a/contrib/fuzzystrmatch/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/fuzzystrmatch.sql diff --git a/contrib/fuzzystrmatch/Makefile b/contrib/fuzzystrmatch/Makefile index 9cdf3f87e3..74728a30b5 100644 --- a/contrib/fuzzystrmatch/Makefile +++ b/contrib/fuzzystrmatch/Makefile @@ -2,8 +2,9 @@ MODULE_big = fuzzystrmatch OBJS = fuzzystrmatch.o dmetaphone.o -DATA_built = fuzzystrmatch.sql -DATA = uninstall_fuzzystrmatch.sql + +EXTENSION = fuzzystrmatch +DATA = fuzzystrmatch--1.0.sql fuzzystrmatch--unpackaged--1.0.sql ifdef USE_PGXS PG_CONFIG = pg_config diff --git a/contrib/fuzzystrmatch/fuzzystrmatch--1.0.sql b/contrib/fuzzystrmatch/fuzzystrmatch--1.0.sql new file mode 100644 index 0000000000..d9b8987adf --- /dev/null +++ b/contrib/fuzzystrmatch/fuzzystrmatch--1.0.sql @@ -0,0 +1,41 @@ +/* contrib/fuzzystrmatch/fuzzystrmatch--1.0.sql */ + +CREATE FUNCTION levenshtein (text,text) RETURNS int +AS 'MODULE_PATHNAME','levenshtein' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION levenshtein (text,text,int,int,int) RETURNS int +AS 'MODULE_PATHNAME','levenshtein_with_costs' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION levenshtein_less_equal (text,text,int) RETURNS int +AS 'MODULE_PATHNAME','levenshtein_less_equal' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION levenshtein_less_equal (text,text,int,int,int,int) RETURNS int +AS 'MODULE_PATHNAME','levenshtein_less_equal_with_costs' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION metaphone (text,int) RETURNS text +AS 'MODULE_PATHNAME','metaphone' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION soundex(text) RETURNS text +AS 'MODULE_PATHNAME', 'soundex' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION text_soundex(text) RETURNS text +AS 'MODULE_PATHNAME', 'soundex' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION difference(text,text) RETURNS int +AS 'MODULE_PATHNAME', 'difference' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION dmetaphone (text) RETURNS text +AS 'MODULE_PATHNAME', 'dmetaphone' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION dmetaphone_alt (text) RETURNS text +AS 'MODULE_PATHNAME', 'dmetaphone_alt' +LANGUAGE C IMMUTABLE STRICT; diff --git a/contrib/fuzzystrmatch/fuzzystrmatch--unpackaged--1.0.sql b/contrib/fuzzystrmatch/fuzzystrmatch--unpackaged--1.0.sql new file mode 100644 index 0000000000..b99510bcdd --- /dev/null +++ b/contrib/fuzzystrmatch/fuzzystrmatch--unpackaged--1.0.sql @@ -0,0 +1,20 @@ +/* contrib/fuzzystrmatch/fuzzystrmatch--unpackaged--1.0.sql */ + +ALTER EXTENSION fuzzystrmatch ADD function levenshtein(text,text); +ALTER EXTENSION fuzzystrmatch ADD function levenshtein(text,text,integer,integer,integer); +ALTER EXTENSION fuzzystrmatch ADD function metaphone(text,integer); +ALTER EXTENSION fuzzystrmatch ADD function soundex(text); +ALTER EXTENSION fuzzystrmatch ADD function text_soundex(text); +ALTER EXTENSION fuzzystrmatch ADD function difference(text,text); +ALTER EXTENSION fuzzystrmatch ADD function dmetaphone(text); +ALTER EXTENSION fuzzystrmatch ADD function dmetaphone_alt(text); + +-- these functions were not in 9.0 + +CREATE FUNCTION levenshtein_less_equal (text,text,int) RETURNS int +AS 'MODULE_PATHNAME','levenshtein_less_equal' +LANGUAGE C IMMUTABLE STRICT; + +CREATE FUNCTION levenshtein_less_equal (text,text,int,int,int,int) RETURNS int +AS 'MODULE_PATHNAME','levenshtein_less_equal_with_costs' +LANGUAGE C IMMUTABLE STRICT; diff --git a/contrib/fuzzystrmatch/fuzzystrmatch.control b/contrib/fuzzystrmatch/fuzzystrmatch.control new file mode 100644 index 0000000000..e257f09611 --- /dev/null +++ b/contrib/fuzzystrmatch/fuzzystrmatch.control @@ -0,0 +1,5 @@ +# fuzzystrmatch extension +comment = 'determine similarities and distance between strings' +default_version = '1.0' +module_pathname = '$libdir/fuzzystrmatch' +relocatable = true diff --git a/contrib/fuzzystrmatch/fuzzystrmatch.sql.in b/contrib/fuzzystrmatch/fuzzystrmatch.sql.in deleted file mode 100644 index 0f2ea85e48..0000000000 --- a/contrib/fuzzystrmatch/fuzzystrmatch.sql.in +++ /dev/null @@ -1,44 +0,0 @@ -/* contrib/fuzzystrmatch/fuzzystrmatch.sql.in */ - --- Adjust this setting to control where the objects get created. -SET search_path = public; - -CREATE OR REPLACE FUNCTION levenshtein (text,text) RETURNS int -AS 'MODULE_PATHNAME','levenshtein' -LANGUAGE C IMMUTABLE STRICT; - -CREATE OR REPLACE FUNCTION levenshtein (text,text,int,int,int) RETURNS int -AS 'MODULE_PATHNAME','levenshtein_with_costs' -LANGUAGE C IMMUTABLE STRICT; - -CREATE OR REPLACE FUNCTION levenshtein_less_equal (text,text,int) RETURNS int -AS 'MODULE_PATHNAME','levenshtein_less_equal' -LANGUAGE C IMMUTABLE STRICT; - -CREATE OR REPLACE FUNCTION levenshtein_less_equal (text,text,int,int,int,int) RETURNS int -AS 'MODULE_PATHNAME','levenshtein_less_equal_with_costs' -LANGUAGE C IMMUTABLE STRICT; - -CREATE OR REPLACE FUNCTION metaphone (text,int) RETURNS text -AS 'MODULE_PATHNAME','metaphone' -LANGUAGE C IMMUTABLE STRICT; - -CREATE OR REPLACE FUNCTION soundex(text) RETURNS text -AS 'MODULE_PATHNAME', 'soundex' -LANGUAGE C IMMUTABLE STRICT; - -CREATE OR REPLACE FUNCTION text_soundex(text) RETURNS text -AS 'MODULE_PATHNAME', 'soundex' -LANGUAGE C IMMUTABLE STRICT; - -CREATE OR REPLACE FUNCTION difference(text,text) RETURNS int -AS 'MODULE_PATHNAME', 'difference' -LANGUAGE C IMMUTABLE STRICT; - -CREATE OR REPLACE FUNCTION dmetaphone (text) RETURNS text -AS 'MODULE_PATHNAME', 'dmetaphone' -LANGUAGE C IMMUTABLE STRICT; - -CREATE OR REPLACE FUNCTION dmetaphone_alt (text) RETURNS text -AS 'MODULE_PATHNAME', 'dmetaphone_alt' -LANGUAGE C IMMUTABLE STRICT; diff --git a/contrib/fuzzystrmatch/uninstall_fuzzystrmatch.sql b/contrib/fuzzystrmatch/uninstall_fuzzystrmatch.sql deleted file mode 100644 index a39c7bfc94..0000000000 --- a/contrib/fuzzystrmatch/uninstall_fuzzystrmatch.sql +++ /dev/null @@ -1,24 +0,0 @@ -/* contrib/fuzzystrmatch/uninstall_fuzzystrmatch.sql */ - --- Adjust this setting to control where the objects get dropped. -SET search_path = public; - -DROP FUNCTION dmetaphone_alt (text); - -DROP FUNCTION dmetaphone (text); - -DROP FUNCTION difference(text,text); - -DROP FUNCTION text_soundex(text); - -DROP FUNCTION soundex(text); - -DROP FUNCTION metaphone (text,int); - -DROP FUNCTION levenshtein (text,text,int,int,int); - -DROP FUNCTION levenshtein (text,text); - -DROP FUNCTION levenshtein_less_equal (text,text,int); - -DROP FUNCTION levenshtein_less_equal (text,text,int,int,int,int); diff --git a/contrib/hstore/.gitignore b/contrib/hstore/.gitignore index d7af95330c..19b6c5ba42 100644 --- a/contrib/hstore/.gitignore +++ b/contrib/hstore/.gitignore @@ -1,3 +1,2 @@ -/hstore.sql # Generated subdirectories /results/ diff --git a/contrib/hstore/Makefile b/contrib/hstore/Makefile index 1d533fdd60..fce1a32328 100644 --- a/contrib/hstore/Makefile +++ b/contrib/hstore/Makefile @@ -4,8 +4,9 @@ MODULE_big = hstore OBJS = hstore_io.o hstore_op.o hstore_gist.o hstore_gin.o hstore_compat.o \ crc32.o -DATA_built = hstore.sql -DATA = uninstall_hstore.sql +EXTENSION = hstore +DATA = hstore--1.0.sql hstore--unpackaged--1.0.sql + REGRESS = hstore ifdef USE_PGXS diff --git a/contrib/hstore/expected/hstore.out b/contrib/hstore/expected/hstore.out index 354fff20fe..083faf8d9c 100644 --- a/contrib/hstore/expected/hstore.out +++ b/contrib/hstore/expected/hstore.out @@ -1,12 +1,6 @@ --- --- first, define the datatype. Turn off echoing so that expected file --- does not depend on contents of hstore.sql. --- -SET client_min_messages = warning; -\set ECHO none -psql:hstore.sql:228: WARNING: => is deprecated as an operator name +CREATE EXTENSION hstore; +WARNING: => is deprecated as an operator name DETAIL: This name may be disallowed altogether in future versions of PostgreSQL. -RESET client_min_messages; set escape_string_warning=off; --hstore; select ''::hstore; diff --git a/contrib/hstore/hstore.sql.in b/contrib/hstore/hstore--1.0.sql index 5b39c189e1..247a2773f5 100644 --- a/contrib/hstore/hstore.sql.in +++ b/contrib/hstore/hstore--1.0.sql @@ -1,26 +1,23 @@ -/* contrib/hstore/hstore.sql.in */ - --- Adjust this setting to control where the objects get created. -SET search_path = public; +/* contrib/hstore/hstore--1.0.sql */ CREATE TYPE hstore; -CREATE OR REPLACE FUNCTION hstore_in(cstring) +CREATE FUNCTION hstore_in(cstring) RETURNS hstore AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION hstore_out(hstore) +CREATE FUNCTION hstore_out(hstore) RETURNS cstring AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION hstore_recv(internal) +CREATE FUNCTION hstore_recv(internal) RETURNS hstore AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION hstore_send(hstore) +CREATE FUNCTION hstore_send(hstore) RETURNS bytea AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -34,12 +31,12 @@ CREATE TYPE hstore ( STORAGE = extended ); -CREATE OR REPLACE FUNCTION hstore_version_diag(hstore) +CREATE FUNCTION hstore_version_diag(hstore) RETURNS integer AS 'MODULE_PATHNAME','hstore_version_diag' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION fetchval(hstore,text) +CREATE FUNCTION fetchval(hstore,text) RETURNS text AS 'MODULE_PATHNAME','hstore_fetchval' LANGUAGE C STRICT IMMUTABLE; @@ -50,7 +47,7 @@ CREATE OPERATOR -> ( PROCEDURE = fetchval ); -CREATE OR REPLACE FUNCTION slice_array(hstore,text[]) +CREATE FUNCTION slice_array(hstore,text[]) RETURNS text[] AS 'MODULE_PATHNAME','hstore_slice_to_array' LANGUAGE C STRICT IMMUTABLE; @@ -61,17 +58,17 @@ CREATE OPERATOR -> ( PROCEDURE = slice_array ); -CREATE OR REPLACE FUNCTION slice(hstore,text[]) +CREATE FUNCTION slice(hstore,text[]) RETURNS hstore AS 'MODULE_PATHNAME','hstore_slice_to_hstore' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION isexists(hstore,text) +CREATE FUNCTION isexists(hstore,text) RETURNS bool AS 'MODULE_PATHNAME','hstore_exists' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION exist(hstore,text) +CREATE FUNCTION exist(hstore,text) RETURNS bool AS 'MODULE_PATHNAME','hstore_exists' LANGUAGE C STRICT IMMUTABLE; @@ -84,7 +81,7 @@ CREATE OPERATOR ? ( JOIN = contjoinsel ); -CREATE OR REPLACE FUNCTION exists_any(hstore,text[]) +CREATE FUNCTION exists_any(hstore,text[]) RETURNS bool AS 'MODULE_PATHNAME','hstore_exists_any' LANGUAGE C STRICT IMMUTABLE; @@ -97,7 +94,7 @@ CREATE OPERATOR ?| ( JOIN = contjoinsel ); -CREATE OR REPLACE FUNCTION exists_all(hstore,text[]) +CREATE FUNCTION exists_all(hstore,text[]) RETURNS bool AS 'MODULE_PATHNAME','hstore_exists_all' LANGUAGE C STRICT IMMUTABLE; @@ -110,27 +107,27 @@ CREATE OPERATOR ?& ( JOIN = contjoinsel ); -CREATE OR REPLACE FUNCTION isdefined(hstore,text) +CREATE FUNCTION isdefined(hstore,text) RETURNS bool AS 'MODULE_PATHNAME','hstore_defined' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION defined(hstore,text) +CREATE FUNCTION defined(hstore,text) RETURNS bool AS 'MODULE_PATHNAME','hstore_defined' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION delete(hstore,text) +CREATE FUNCTION delete(hstore,text) RETURNS hstore AS 'MODULE_PATHNAME','hstore_delete' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION delete(hstore,text[]) +CREATE FUNCTION delete(hstore,text[]) RETURNS hstore AS 'MODULE_PATHNAME','hstore_delete_array' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION delete(hstore,hstore) +CREATE FUNCTION delete(hstore,hstore) RETURNS hstore AS 'MODULE_PATHNAME','hstore_delete_hstore' LANGUAGE C STRICT IMMUTABLE; @@ -153,7 +150,7 @@ CREATE OPERATOR - ( PROCEDURE = delete ); -CREATE OR REPLACE FUNCTION hs_concat(hstore,hstore) +CREATE FUNCTION hs_concat(hstore,hstore) RETURNS hstore AS 'MODULE_PATHNAME','hstore_concat' LANGUAGE C STRICT IMMUTABLE; @@ -164,12 +161,12 @@ CREATE OPERATOR || ( PROCEDURE = hs_concat ); -CREATE OR REPLACE FUNCTION hs_contains(hstore,hstore) +CREATE FUNCTION hs_contains(hstore,hstore) RETURNS bool AS 'MODULE_PATHNAME','hstore_contains' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION hs_contained(hstore,hstore) +CREATE FUNCTION hs_contained(hstore,hstore) RETURNS bool AS 'MODULE_PATHNAME','hstore_contained' LANGUAGE C STRICT IMMUTABLE; @@ -211,12 +208,12 @@ CREATE OPERATOR ~ ( JOIN = contjoinsel ); -CREATE OR REPLACE FUNCTION tconvert(text,text) +CREATE FUNCTION tconvert(text,text) RETURNS hstore AS 'MODULE_PATHNAME','hstore_from_text' LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL) -CREATE OR REPLACE FUNCTION hstore(text,text) +CREATE FUNCTION hstore(text,text) RETURNS hstore AS 'MODULE_PATHNAME','hstore_from_text' LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL) @@ -227,7 +224,7 @@ CREATE OPERATOR => ( PROCEDURE = hstore ); -CREATE OR REPLACE FUNCTION hstore(text[],text[]) +CREATE FUNCTION hstore(text[],text[]) RETURNS hstore AS 'MODULE_PATHNAME', 'hstore_from_arrays' LANGUAGE C IMMUTABLE; -- not STRICT; allows (keys,null) @@ -240,12 +237,12 @@ LANGUAGE C IMMUTABLE STRICT; CREATE CAST (text[] AS hstore) WITH FUNCTION hstore(text[]); -CREATE OR REPLACE FUNCTION hstore(record) +CREATE FUNCTION hstore(record) RETURNS hstore AS 'MODULE_PATHNAME', 'hstore_from_record' LANGUAGE C IMMUTABLE; -- not STRICT; allows (null::recordtype) -CREATE OR REPLACE FUNCTION hstore_to_array(hstore) +CREATE FUNCTION hstore_to_array(hstore) RETURNS text[] AS 'MODULE_PATHNAME','hstore_to_array' LANGUAGE C STRICT IMMUTABLE; @@ -255,7 +252,7 @@ CREATE OPERATOR %% ( PROCEDURE = hstore_to_array ); -CREATE OR REPLACE FUNCTION hstore_to_matrix(hstore) +CREATE FUNCTION hstore_to_matrix(hstore) RETURNS text[] AS 'MODULE_PATHNAME','hstore_to_matrix' LANGUAGE C STRICT IMMUTABLE; @@ -265,34 +262,34 @@ CREATE OPERATOR %# ( PROCEDURE = hstore_to_matrix ); -CREATE OR REPLACE FUNCTION akeys(hstore) +CREATE FUNCTION akeys(hstore) RETURNS text[] AS 'MODULE_PATHNAME','hstore_akeys' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION avals(hstore) +CREATE FUNCTION avals(hstore) RETURNS text[] AS 'MODULE_PATHNAME','hstore_avals' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION skeys(hstore) +CREATE FUNCTION skeys(hstore) RETURNS setof text AS 'MODULE_PATHNAME','hstore_skeys' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION svals(hstore) +CREATE FUNCTION svals(hstore) RETURNS setof text AS 'MODULE_PATHNAME','hstore_svals' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION each(IN hs hstore, +CREATE FUNCTION each(IN hs hstore, OUT key text, OUT value text) RETURNS SETOF record AS 'MODULE_PATHNAME','hstore_each' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION populate_record(anyelement,hstore) +CREATE FUNCTION populate_record(anyelement,hstore) RETURNS anyelement AS 'MODULE_PATHNAME', 'hstore_populate_record' LANGUAGE C IMMUTABLE; -- not STRICT; allows (null::rectype,hstore) @@ -305,37 +302,37 @@ CREATE OPERATOR #= ( -- btree support -CREATE OR REPLACE FUNCTION hstore_eq(hstore,hstore) +CREATE FUNCTION hstore_eq(hstore,hstore) RETURNS boolean AS 'MODULE_PATHNAME','hstore_eq' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION hstore_ne(hstore,hstore) +CREATE FUNCTION hstore_ne(hstore,hstore) RETURNS boolean AS 'MODULE_PATHNAME','hstore_ne' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION hstore_gt(hstore,hstore) +CREATE FUNCTION hstore_gt(hstore,hstore) RETURNS boolean AS 'MODULE_PATHNAME','hstore_gt' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION hstore_ge(hstore,hstore) +CREATE FUNCTION hstore_ge(hstore,hstore) RETURNS boolean AS 'MODULE_PATHNAME','hstore_ge' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION hstore_lt(hstore,hstore) +CREATE FUNCTION hstore_lt(hstore,hstore) RETURNS boolean AS 'MODULE_PATHNAME','hstore_lt' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION hstore_le(hstore,hstore) +CREATE FUNCTION hstore_le(hstore,hstore) RETURNS boolean AS 'MODULE_PATHNAME','hstore_le' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION hstore_cmp(hstore,hstore) +CREATE FUNCTION hstore_cmp(hstore,hstore) RETURNS integer AS 'MODULE_PATHNAME','hstore_cmp' LANGUAGE C STRICT IMMUTABLE; @@ -414,7 +411,7 @@ AS -- hash support -CREATE OR REPLACE FUNCTION hstore_hash(hstore) +CREATE FUNCTION hstore_hash(hstore) RETURNS integer AS 'MODULE_PATHNAME','hstore_hash' LANGUAGE C STRICT IMMUTABLE; @@ -429,12 +426,12 @@ AS CREATE TYPE ghstore; -CREATE OR REPLACE FUNCTION ghstore_in(cstring) +CREATE FUNCTION ghstore_in(cstring) RETURNS ghstore AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION ghstore_out(ghstore) +CREATE FUNCTION ghstore_out(ghstore) RETURNS cstring AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -445,37 +442,37 @@ CREATE TYPE ghstore ( OUTPUT = ghstore_out ); -CREATE OR REPLACE FUNCTION ghstore_compress(internal) +CREATE FUNCTION ghstore_compress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION ghstore_decompress(internal) +CREATE FUNCTION ghstore_decompress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION ghstore_penalty(internal,internal,internal) +CREATE FUNCTION ghstore_penalty(internal,internal,internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION ghstore_picksplit(internal, internal) +CREATE FUNCTION ghstore_picksplit(internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION ghstore_union(internal, internal) +CREATE FUNCTION ghstore_union(internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION ghstore_same(internal, internal, internal) +CREATE FUNCTION ghstore_same(internal, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION ghstore_consistent(internal,internal,int,oid,internal) +CREATE FUNCTION ghstore_consistent(internal,internal,int,oid,internal) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; @@ -501,17 +498,17 @@ AS -- GIN support -CREATE OR REPLACE FUNCTION gin_extract_hstore(internal, internal) +CREATE FUNCTION gin_extract_hstore(internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gin_extract_hstore_query(internal, internal, int2, internal, internal) +CREATE FUNCTION gin_extract_hstore_query(internal, internal, int2, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gin_consistent_hstore(internal, int2, internal, int4, internal, internal) +CREATE FUNCTION gin_consistent_hstore(internal, int2, internal, int4, internal, internal) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; diff --git a/contrib/hstore/hstore--unpackaged--1.0.sql b/contrib/hstore/hstore--unpackaged--1.0.sql new file mode 100644 index 0000000000..0eb300ecf5 --- /dev/null +++ b/contrib/hstore/hstore--unpackaged--1.0.sql @@ -0,0 +1,89 @@ +/* contrib/hstore/hstore--unpackaged--1.0.sql */ + +ALTER EXTENSION hstore ADD type hstore; +ALTER EXTENSION hstore ADD function hstore_in(cstring); +ALTER EXTENSION hstore ADD function hstore_out(hstore); +ALTER EXTENSION hstore ADD function hstore_recv(internal); +ALTER EXTENSION hstore ADD function hstore_send(hstore); +ALTER EXTENSION hstore ADD function hstore_version_diag(hstore); +ALTER EXTENSION hstore ADD function fetchval(hstore,text); +ALTER EXTENSION hstore ADD operator ->(hstore,text); +ALTER EXTENSION hstore ADD function slice_array(hstore,text[]); +ALTER EXTENSION hstore ADD operator ->(hstore,text[]); +ALTER EXTENSION hstore ADD function slice(hstore,text[]); +ALTER EXTENSION hstore ADD function isexists(hstore,text); +ALTER EXTENSION hstore ADD function exist(hstore,text); +ALTER EXTENSION hstore ADD operator ?(hstore,text); +ALTER EXTENSION hstore ADD function exists_any(hstore,text[]); +ALTER EXTENSION hstore ADD operator ?|(hstore,text[]); +ALTER EXTENSION hstore ADD function exists_all(hstore,text[]); +ALTER EXTENSION hstore ADD operator ?&(hstore,text[]); +ALTER EXTENSION hstore ADD function isdefined(hstore,text); +ALTER EXTENSION hstore ADD function defined(hstore,text); +ALTER EXTENSION hstore ADD function delete(hstore,text); +ALTER EXTENSION hstore ADD function delete(hstore,text[]); +ALTER EXTENSION hstore ADD function delete(hstore,hstore); +ALTER EXTENSION hstore ADD operator -(hstore,text); +ALTER EXTENSION hstore ADD operator -(hstore,text[]); +ALTER EXTENSION hstore ADD operator -(hstore,hstore); +ALTER EXTENSION hstore ADD function hs_concat(hstore,hstore); +ALTER EXTENSION hstore ADD operator ||(hstore,hstore); +ALTER EXTENSION hstore ADD function hs_contains(hstore,hstore); +ALTER EXTENSION hstore ADD function hs_contained(hstore,hstore); +ALTER EXTENSION hstore ADD operator <@(hstore,hstore); +ALTER EXTENSION hstore ADD operator @>(hstore,hstore); +ALTER EXTENSION hstore ADD operator ~(hstore,hstore); +ALTER EXTENSION hstore ADD operator @(hstore,hstore); +ALTER EXTENSION hstore ADD function tconvert(text,text); +ALTER EXTENSION hstore ADD function hstore(text,text); +ALTER EXTENSION hstore ADD operator =>(text,text); +ALTER EXTENSION hstore ADD function hstore(text[],text[]); +ALTER EXTENSION hstore ADD function hstore(text[]); +ALTER EXTENSION hstore ADD cast (text[] as hstore); +ALTER EXTENSION hstore ADD function hstore(record); +ALTER EXTENSION hstore ADD function hstore_to_array(hstore); +ALTER EXTENSION hstore ADD operator %%(NONE,hstore); +ALTER EXTENSION hstore ADD function hstore_to_matrix(hstore); +ALTER EXTENSION hstore ADD operator %#(NONE,hstore); +ALTER EXTENSION hstore ADD function akeys(hstore); +ALTER EXTENSION hstore ADD function avals(hstore); +ALTER EXTENSION hstore ADD function skeys(hstore); +ALTER EXTENSION hstore ADD function svals(hstore); +ALTER EXTENSION hstore ADD function each(hstore); +ALTER EXTENSION hstore ADD function populate_record(anyelement,hstore); +ALTER EXTENSION hstore ADD operator #=(anyelement,hstore); +ALTER EXTENSION hstore ADD function hstore_eq(hstore,hstore); +ALTER EXTENSION hstore ADD function hstore_ne(hstore,hstore); +ALTER EXTENSION hstore ADD function hstore_gt(hstore,hstore); +ALTER EXTENSION hstore ADD function hstore_ge(hstore,hstore); +ALTER EXTENSION hstore ADD function hstore_lt(hstore,hstore); +ALTER EXTENSION hstore ADD function hstore_le(hstore,hstore); +ALTER EXTENSION hstore ADD function hstore_cmp(hstore,hstore); +ALTER EXTENSION hstore ADD operator <>(hstore,hstore); +ALTER EXTENSION hstore ADD operator =(hstore,hstore); +ALTER EXTENSION hstore ADD operator #>#(hstore,hstore); +ALTER EXTENSION hstore ADD operator #>=#(hstore,hstore); +ALTER EXTENSION hstore ADD operator #<#(hstore,hstore); +ALTER EXTENSION hstore ADD operator #<=#(hstore,hstore); +ALTER EXTENSION hstore ADD operator family btree_hstore_ops using btree; +ALTER EXTENSION hstore ADD operator class btree_hstore_ops using btree; +ALTER EXTENSION hstore ADD function hstore_hash(hstore); +ALTER EXTENSION hstore ADD operator family hash_hstore_ops using hash; +ALTER EXTENSION hstore ADD operator class hash_hstore_ops using hash; +ALTER EXTENSION hstore ADD type ghstore; +ALTER EXTENSION hstore ADD function ghstore_in(cstring); +ALTER EXTENSION hstore ADD function ghstore_out(ghstore); +ALTER EXTENSION hstore ADD function ghstore_compress(internal); +ALTER EXTENSION hstore ADD function ghstore_decompress(internal); +ALTER EXTENSION hstore ADD function ghstore_penalty(internal,internal,internal); +ALTER EXTENSION hstore ADD function ghstore_picksplit(internal,internal); +ALTER EXTENSION hstore ADD function ghstore_union(internal,internal); +ALTER EXTENSION hstore ADD function ghstore_same(internal,internal,internal); +ALTER EXTENSION hstore ADD function ghstore_consistent(internal,internal,integer,oid,internal); +ALTER EXTENSION hstore ADD operator family gist_hstore_ops using gist; +ALTER EXTENSION hstore ADD operator class gist_hstore_ops using gist; +ALTER EXTENSION hstore ADD function gin_extract_hstore(internal,internal); +ALTER EXTENSION hstore ADD function gin_extract_hstore_query(internal,internal,smallint,internal,internal); +ALTER EXTENSION hstore ADD function gin_consistent_hstore(internal,smallint,internal,integer,internal,internal); +ALTER EXTENSION hstore ADD operator family gin_hstore_ops using gin; +ALTER EXTENSION hstore ADD operator class gin_hstore_ops using gin; diff --git a/contrib/hstore/hstore.control b/contrib/hstore/hstore.control new file mode 100644 index 0000000000..38a4851ea8 --- /dev/null +++ b/contrib/hstore/hstore.control @@ -0,0 +1,5 @@ +# hstore extension +comment = 'data type for storing sets of (key, value) pairs' +default_version = '1.0' +module_pathname = '$libdir/hstore' +relocatable = true diff --git a/contrib/hstore/sql/hstore.sql b/contrib/hstore/sql/hstore.sql index 58a7967526..fb6bb59f8a 100644 --- a/contrib/hstore/sql/hstore.sql +++ b/contrib/hstore/sql/hstore.sql @@ -1,12 +1,4 @@ --- --- first, define the datatype. Turn off echoing so that expected file --- does not depend on contents of hstore.sql. --- -SET client_min_messages = warning; -\set ECHO none -\i hstore.sql -\set ECHO all -RESET client_min_messages; +CREATE EXTENSION hstore; set escape_string_warning=off; diff --git a/contrib/hstore/uninstall_hstore.sql b/contrib/hstore/uninstall_hstore.sql deleted file mode 100644 index a03e43164f..0000000000 --- a/contrib/hstore/uninstall_hstore.sql +++ /dev/null @@ -1,86 +0,0 @@ -/* contrib/hstore/uninstall_hstore.sql */ - --- Adjust this setting to control where the objects get dropped. -SET search_path = public; - -DROP OPERATOR CLASS gist_hstore_ops USING gist CASCADE; -DROP OPERATOR CLASS gin_hstore_ops USING gin CASCADE; -DROP OPERATOR CLASS hash_hstore_ops USING hash CASCADE; -DROP OPERATOR CLASS btree_hstore_ops USING btree CASCADE; - -DROP OPERATOR - ( hstore, text ); -DROP OPERATOR - ( hstore, text[] ); -DROP OPERATOR - ( hstore, hstore ); -DROP OPERATOR ? ( hstore, text ); -DROP OPERATOR ?& ( hstore, text[] ); -DROP OPERATOR ?| ( hstore, text[] ); -DROP OPERATOR -> ( hstore, text ); -DROP OPERATOR -> ( hstore, text[] ); -DROP OPERATOR || ( hstore, hstore ); -DROP OPERATOR @> ( hstore, hstore ); -DROP OPERATOR <@ ( hstore, hstore ); -DROP OPERATOR @ ( hstore, hstore ); -DROP OPERATOR ~ ( hstore, hstore ); -DROP OPERATOR => ( text, text ); -DROP OPERATOR #= ( anyelement, hstore ); -DROP OPERATOR %% ( NONE, hstore ); -DROP OPERATOR %# ( NONE, hstore ); -DROP OPERATOR = ( hstore, hstore ); -DROP OPERATOR <> ( hstore, hstore ); -DROP OPERATOR #<# ( hstore, hstore ); -DROP OPERATOR #<=# ( hstore, hstore ); -DROP OPERATOR #># ( hstore, hstore ); -DROP OPERATOR #>=# ( hstore, hstore ); - -DROP CAST (text[] AS hstore); - -DROP FUNCTION hstore_eq(hstore,hstore); -DROP FUNCTION hstore_ne(hstore,hstore); -DROP FUNCTION hstore_gt(hstore,hstore); -DROP FUNCTION hstore_ge(hstore,hstore); -DROP FUNCTION hstore_lt(hstore,hstore); -DROP FUNCTION hstore_le(hstore,hstore); -DROP FUNCTION hstore_cmp(hstore,hstore); -DROP FUNCTION hstore_hash(hstore); -DROP FUNCTION slice_array(hstore,text[]); -DROP FUNCTION slice(hstore,text[]); -DROP FUNCTION fetchval(hstore,text); -DROP FUNCTION isexists(hstore,text); -DROP FUNCTION exist(hstore,text); -DROP FUNCTION exists_any(hstore,text[]); -DROP FUNCTION exists_all(hstore,text[]); -DROP FUNCTION isdefined(hstore,text); -DROP FUNCTION defined(hstore,text); -DROP FUNCTION delete(hstore,text); -DROP FUNCTION delete(hstore,text[]); -DROP FUNCTION delete(hstore,hstore); -DROP FUNCTION hs_concat(hstore,hstore); -DROP FUNCTION hs_contains(hstore,hstore); -DROP FUNCTION hs_contained(hstore,hstore); -DROP FUNCTION tconvert(text,text); -DROP FUNCTION hstore(text,text); -DROP FUNCTION hstore(text[],text[]); -DROP FUNCTION hstore_to_array(hstore); -DROP FUNCTION hstore_to_matrix(hstore); -DROP FUNCTION hstore(record); -DROP FUNCTION hstore(text[]); -DROP FUNCTION akeys(hstore); -DROP FUNCTION avals(hstore); -DROP FUNCTION skeys(hstore); -DROP FUNCTION svals(hstore); -DROP FUNCTION each(hstore); -DROP FUNCTION populate_record(anyelement,hstore); -DROP FUNCTION ghstore_compress(internal); -DROP FUNCTION ghstore_decompress(internal); -DROP FUNCTION ghstore_penalty(internal,internal,internal); -DROP FUNCTION ghstore_picksplit(internal, internal); -DROP FUNCTION ghstore_union(internal, internal); -DROP FUNCTION ghstore_same(internal, internal, internal); -DROP FUNCTION ghstore_consistent(internal,internal,int,oid,internal); -DROP FUNCTION gin_consistent_hstore(internal, int2, internal, int4, internal, internal); -DROP FUNCTION gin_extract_hstore(internal, internal); -DROP FUNCTION gin_extract_hstore_query(internal, internal, smallint, internal, internal); -DROP FUNCTION hstore_version_diag(hstore); - -DROP TYPE hstore CASCADE; -DROP TYPE ghstore CASCADE; diff --git a/contrib/intagg/Makefile b/contrib/intagg/Makefile index 9bb1866e78..2cfe9978e2 100644 --- a/contrib/intagg/Makefile +++ b/contrib/intagg/Makefile @@ -1,6 +1,7 @@ # contrib/intagg/Makefile -DATA = int_aggregate.sql uninstall_int_aggregate.sql +EXTENSION = intagg +DATA = intagg--1.0.sql intagg--unpackaged--1.0.sql ifdef USE_PGXS PG_CONFIG = pg_config diff --git a/contrib/intagg/int_aggregate.sql b/contrib/intagg/intagg--1.0.sql index 289e41b671..19a57c079b 100644 --- a/contrib/intagg/int_aggregate.sql +++ b/contrib/intagg/intagg--1.0.sql @@ -1,18 +1,15 @@ -/* contrib/intagg/int_aggregate.sql */ - --- Adjust this setting to control where the objects get created. -SET search_path = public; +/* contrib/intagg/intagg--1.0.sql */ -- Internal function for the aggregate -- Is called for each item in an aggregation -CREATE OR REPLACE FUNCTION int_agg_state (internal, int4) +CREATE FUNCTION int_agg_state (internal, int4) RETURNS internal AS 'array_agg_transfn' LANGUAGE INTERNAL; -- Internal function for the aggregate -- Is called at the end of the aggregation, and returns an array. -CREATE OR REPLACE FUNCTION int_agg_final_array (internal) +CREATE FUNCTION int_agg_final_array (internal) RETURNS int4[] AS 'array_agg_finalfn' LANGUAGE INTERNAL; @@ -29,7 +26,7 @@ CREATE AGGREGATE int_array_aggregate ( -- The enumeration function -- returns each element in a one dimensional integer array -- as a row. -CREATE OR REPLACE FUNCTION int_array_enum(int4[]) +CREATE FUNCTION int_array_enum(int4[]) RETURNS setof integer AS 'array_unnest' LANGUAGE INTERNAL IMMUTABLE STRICT; diff --git a/contrib/intagg/intagg--unpackaged--1.0.sql b/contrib/intagg/intagg--unpackaged--1.0.sql new file mode 100644 index 0000000000..95238d9c67 --- /dev/null +++ b/contrib/intagg/intagg--unpackaged--1.0.sql @@ -0,0 +1,6 @@ +/* contrib/intagg/intagg--unpackaged--1.0.sql */ + +ALTER EXTENSION intagg ADD function int_agg_state(internal,integer); +ALTER EXTENSION intagg ADD function int_agg_final_array(internal); +ALTER EXTENSION intagg ADD function int_array_aggregate(integer); +ALTER EXTENSION intagg ADD function int_array_enum(integer[]); diff --git a/contrib/intagg/intagg.control b/contrib/intagg/intagg.control new file mode 100644 index 0000000000..f11fb11940 --- /dev/null +++ b/contrib/intagg/intagg.control @@ -0,0 +1,4 @@ +# intagg extension +comment = 'integer aggregator and enumerator (obsolete)' +default_version = '1.0' +relocatable = true diff --git a/contrib/intagg/uninstall_int_aggregate.sql b/contrib/intagg/uninstall_int_aggregate.sql deleted file mode 100644 index 2e55345325..0000000000 --- a/contrib/intagg/uninstall_int_aggregate.sql +++ /dev/null @@ -1,12 +0,0 @@ -/* contrib/intagg/uninstall_int_aggregate.sql */ - --- Adjust this setting to control where the objects get dropped. -SET search_path = public; - -DROP FUNCTION int_array_enum(int4[]); - -DROP AGGREGATE int_array_aggregate (int4); - -DROP FUNCTION int_agg_final_array (internal); - -DROP FUNCTION int_agg_state (internal, int4); diff --git a/contrib/intarray/.gitignore b/contrib/intarray/.gitignore index 761a9b2607..19b6c5ba42 100644 --- a/contrib/intarray/.gitignore +++ b/contrib/intarray/.gitignore @@ -1,3 +1,2 @@ -/_int.sql # Generated subdirectories /results/ diff --git a/contrib/intarray/Makefile b/contrib/intarray/Makefile index a10d7c6b1f..71f820ec4a 100644 --- a/contrib/intarray/Makefile +++ b/contrib/intarray/Makefile @@ -2,8 +2,10 @@ MODULE_big = _int OBJS = _int_bool.o _int_gist.o _int_op.o _int_tool.o _intbig_gist.o _int_gin.o -DATA_built = _int.sql -DATA = uninstall__int.sql + +EXTENSION = intarray +DATA = intarray--1.0.sql intarray--unpackaged--1.0.sql + REGRESS = _int ifdef USE_PGXS diff --git a/contrib/intarray/expected/_int.out b/contrib/intarray/expected/_int.out index 596439d314..6ed3cc6ced 100644 --- a/contrib/intarray/expected/_int.out +++ b/contrib/intarray/expected/_int.out @@ -1,10 +1,4 @@ --- --- first, define the datatype. Turn off echoing so that expected file --- does not depend on contents of _int.sql. --- -SET client_min_messages = warning; -\set ECHO none -RESET client_min_messages; +CREATE EXTENSION intarray; SELECT intset(1234); intset -------- diff --git a/contrib/intarray/_int.sql.in b/contrib/intarray/intarray--1.0.sql index ad224e319b..3b77c516cb 100644 --- a/contrib/intarray/_int.sql.in +++ b/contrib/intarray/intarray--1.0.sql @@ -1,19 +1,16 @@ -/* contrib/intarray/_int.sql.in */ - --- Adjust this setting to control where the objects get created. -SET search_path = public; +/* contrib/intarray/intarray--1.0.sql */ -- -- Create the user-defined type for the 1-D integer arrays (_int4) -- -- Query type -CREATE OR REPLACE FUNCTION bqarr_in(cstring) +CREATE FUNCTION bqarr_in(cstring) RETURNS query_int AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION bqarr_out(query_int) +CREATE FUNCTION bqarr_out(query_int) RETURNS cstring AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -25,20 +22,20 @@ CREATE TYPE query_int ( ); --only for debug -CREATE OR REPLACE FUNCTION querytree(query_int) +CREATE FUNCTION querytree(query_int) RETURNS text AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION boolop(_int4, query_int) +CREATE FUNCTION boolop(_int4, query_int) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; COMMENT ON FUNCTION boolop(_int4, query_int) IS 'boolean operation with array'; -CREATE OR REPLACE FUNCTION rboolop(query_int, _int4) +CREATE FUNCTION rboolop(query_int, _int4) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -70,35 +67,35 @@ CREATE OPERATOR ~~ ( -- Comparison methods -CREATE OR REPLACE FUNCTION _int_contains(_int4, _int4) +CREATE FUNCTION _int_contains(_int4, _int4) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; COMMENT ON FUNCTION _int_contains(_int4, _int4) IS 'contains'; -CREATE OR REPLACE FUNCTION _int_contained(_int4, _int4) +CREATE FUNCTION _int_contained(_int4, _int4) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; COMMENT ON FUNCTION _int_contained(_int4, _int4) IS 'contained in'; -CREATE OR REPLACE FUNCTION _int_overlap(_int4, _int4) +CREATE FUNCTION _int_overlap(_int4, _int4) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; COMMENT ON FUNCTION _int_overlap(_int4, _int4) IS 'overlaps'; -CREATE OR REPLACE FUNCTION _int_same(_int4, _int4) +CREATE FUNCTION _int_same(_int4, _int4) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; COMMENT ON FUNCTION _int_same(_int4, _int4) IS 'same as'; -CREATE OR REPLACE FUNCTION _int_different(_int4, _int4) +CREATE FUNCTION _int_different(_int4, _int4) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -107,12 +104,12 @@ COMMENT ON FUNCTION _int_different(_int4, _int4) IS 'different'; -- support routines for indexing -CREATE OR REPLACE FUNCTION _int_union(_int4, _int4) +CREATE FUNCTION _int_union(_int4, _int4) RETURNS _int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION _int_inter(_int4, _int4) +CREATE FUNCTION _int_inter(_int4, _int4) RETURNS _int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -190,12 +187,12 @@ CREATE OPERATOR ~ ( ); -------------- -CREATE OR REPLACE FUNCTION intset(int4) +CREATE FUNCTION intset(int4) RETURNS _int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION icount(_int4) +CREATE FUNCTION icount(_int4) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -205,32 +202,32 @@ CREATE OPERATOR # ( PROCEDURE = icount ); -CREATE OR REPLACE FUNCTION sort(_int4, text) +CREATE FUNCTION sort(_int4, text) RETURNS _int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION sort(_int4) +CREATE FUNCTION sort(_int4) RETURNS _int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION sort_asc(_int4) +CREATE FUNCTION sort_asc(_int4) RETURNS _int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION sort_desc(_int4) +CREATE FUNCTION sort_desc(_int4) RETURNS _int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION uniq(_int4) +CREATE FUNCTION uniq(_int4) RETURNS _int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION idx(_int4, int4) +CREATE FUNCTION idx(_int4, int4) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -241,17 +238,17 @@ CREATE OPERATOR # ( PROCEDURE = idx ); -CREATE OR REPLACE FUNCTION subarray(_int4, int4, int4) +CREATE FUNCTION subarray(_int4, int4, int4) RETURNS _int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION subarray(_int4, int4) +CREATE FUNCTION subarray(_int4, int4) RETURNS _int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION intarray_push_elem(_int4, int4) +CREATE FUNCTION intarray_push_elem(_int4, int4) RETURNS _int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -262,7 +259,7 @@ CREATE OPERATOR + ( PROCEDURE = intarray_push_elem ); -CREATE OR REPLACE FUNCTION intarray_push_array(_int4, _int4) +CREATE FUNCTION intarray_push_array(_int4, _int4) RETURNS _int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -274,7 +271,7 @@ CREATE OPERATOR + ( PROCEDURE = intarray_push_array ); -CREATE OR REPLACE FUNCTION intarray_del_elem(_int4, int4) +CREATE FUNCTION intarray_del_elem(_int4, int4) RETURNS _int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -285,7 +282,7 @@ CREATE OPERATOR - ( PROCEDURE = intarray_del_elem ); -CREATE OR REPLACE FUNCTION intset_union_elem(_int4, int4) +CREATE FUNCTION intset_union_elem(_int4, int4) RETURNS _int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -303,7 +300,7 @@ CREATE OPERATOR | ( PROCEDURE = _int_union ); -CREATE OR REPLACE FUNCTION intset_subtract(_int4, _int4) +CREATE FUNCTION intset_subtract(_int4, _int4) RETURNS _int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -323,37 +320,37 @@ CREATE OPERATOR & ( -------------- -- define the GiST support methods -CREATE OR REPLACE FUNCTION g_int_consistent(internal,_int4,int,oid,internal) +CREATE FUNCTION g_int_consistent(internal,_int4,int,oid,internal) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION g_int_compress(internal) +CREATE FUNCTION g_int_compress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION g_int_decompress(internal) +CREATE FUNCTION g_int_decompress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION g_int_penalty(internal,internal,internal) +CREATE FUNCTION g_int_penalty(internal,internal,internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION g_int_picksplit(internal, internal) +CREATE FUNCTION g_int_picksplit(internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION g_int_union(internal, internal) +CREATE FUNCTION g_int_union(internal, internal) RETURNS _int4 AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION g_int_same(_int4, _int4, internal) +CREATE FUNCTION g_int_same(_int4, _int4, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; @@ -384,12 +381,12 @@ DEFAULT FOR TYPE _int4 USING gist AS --------------------------------------------- -- define the GiST support methods -CREATE OR REPLACE FUNCTION _intbig_in(cstring) +CREATE FUNCTION _intbig_in(cstring) RETURNS intbig_gkey AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION _intbig_out(intbig_gkey) +CREATE FUNCTION _intbig_out(intbig_gkey) RETURNS cstring AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -400,37 +397,37 @@ CREATE TYPE intbig_gkey ( OUTPUT = _intbig_out ); -CREATE OR REPLACE FUNCTION g_intbig_consistent(internal,internal,int,oid,internal) +CREATE FUNCTION g_intbig_consistent(internal,internal,int,oid,internal) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION g_intbig_compress(internal) +CREATE FUNCTION g_intbig_compress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION g_intbig_decompress(internal) +CREATE FUNCTION g_intbig_decompress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION g_intbig_penalty(internal,internal,internal) +CREATE FUNCTION g_intbig_penalty(internal,internal,internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION g_intbig_picksplit(internal, internal) +CREATE FUNCTION g_intbig_picksplit(internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION g_intbig_union(internal, internal) +CREATE FUNCTION g_intbig_union(internal, internal) RETURNS _int4 AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION g_intbig_same(internal, internal, internal) +CREATE FUNCTION g_intbig_same(internal, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; @@ -458,12 +455,12 @@ AS --GIN -CREATE OR REPLACE FUNCTION ginint4_queryextract(internal, internal, int2, internal, internal, internal, internal) +CREATE FUNCTION ginint4_queryextract(internal, internal, int2, internal, internal, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION ginint4_consistent(internal, int2, internal, int4, internal, internal, internal, internal) +CREATE FUNCTION ginint4_consistent(internal, int2, internal, int4, internal, internal, internal, internal) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; diff --git a/contrib/intarray/intarray--unpackaged--1.0.sql b/contrib/intarray/intarray--unpackaged--1.0.sql new file mode 100644 index 0000000000..69bdc589b6 --- /dev/null +++ b/contrib/intarray/intarray--unpackaged--1.0.sql @@ -0,0 +1,74 @@ +/* contrib/intarray/intarray--unpackaged--1.0.sql */ + +ALTER EXTENSION intarray ADD type query_int; +ALTER EXTENSION intarray ADD function bqarr_in(cstring); +ALTER EXTENSION intarray ADD function bqarr_out(query_int); +ALTER EXTENSION intarray ADD function querytree(query_int); +ALTER EXTENSION intarray ADD function boolop(integer[],query_int); +ALTER EXTENSION intarray ADD function rboolop(query_int,integer[]); +ALTER EXTENSION intarray ADD operator ~~(query_int,integer[]); +ALTER EXTENSION intarray ADD operator @@(integer[],query_int); +ALTER EXTENSION intarray ADD function _int_contains(integer[],integer[]); +ALTER EXTENSION intarray ADD function _int_contained(integer[],integer[]); +ALTER EXTENSION intarray ADD function _int_overlap(integer[],integer[]); +ALTER EXTENSION intarray ADD function _int_same(integer[],integer[]); +ALTER EXTENSION intarray ADD function _int_different(integer[],integer[]); +ALTER EXTENSION intarray ADD function _int_union(integer[],integer[]); +ALTER EXTENSION intarray ADD function _int_inter(integer[],integer[]); +ALTER EXTENSION intarray ADD operator &&(integer[],integer[]); +ALTER EXTENSION intarray ADD operator <@(integer[],integer[]); +ALTER EXTENSION intarray ADD operator @>(integer[],integer[]); +ALTER EXTENSION intarray ADD operator ~(integer[],integer[]); +ALTER EXTENSION intarray ADD operator @(integer[],integer[]); +ALTER EXTENSION intarray ADD function intset(integer); +ALTER EXTENSION intarray ADD function icount(integer[]); +ALTER EXTENSION intarray ADD operator #(NONE,integer[]); +ALTER EXTENSION intarray ADD function sort(integer[],text); +ALTER EXTENSION intarray ADD function sort(integer[]); +ALTER EXTENSION intarray ADD function sort_asc(integer[]); +ALTER EXTENSION intarray ADD function sort_desc(integer[]); +ALTER EXTENSION intarray ADD function uniq(integer[]); +ALTER EXTENSION intarray ADD function idx(integer[],integer); +ALTER EXTENSION intarray ADD operator #(integer[],integer); +ALTER EXTENSION intarray ADD function subarray(integer[],integer,integer); +ALTER EXTENSION intarray ADD function subarray(integer[],integer); +ALTER EXTENSION intarray ADD function intarray_push_elem(integer[],integer); +ALTER EXTENSION intarray ADD operator +(integer[],integer); +ALTER EXTENSION intarray ADD function intarray_push_array(integer[],integer[]); +ALTER EXTENSION intarray ADD operator +(integer[],integer[]); +ALTER EXTENSION intarray ADD function intarray_del_elem(integer[],integer); +ALTER EXTENSION intarray ADD operator -(integer[],integer); +ALTER EXTENSION intarray ADD function intset_union_elem(integer[],integer); +ALTER EXTENSION intarray ADD operator |(integer[],integer); +ALTER EXTENSION intarray ADD operator |(integer[],integer[]); +ALTER EXTENSION intarray ADD function intset_subtract(integer[],integer[]); +ALTER EXTENSION intarray ADD operator -(integer[],integer[]); +ALTER EXTENSION intarray ADD operator &(integer[],integer[]); +ALTER EXTENSION intarray ADD function g_int_consistent(internal,integer[],integer,oid,internal); +ALTER EXTENSION intarray ADD function g_int_compress(internal); +ALTER EXTENSION intarray ADD function g_int_decompress(internal); +ALTER EXTENSION intarray ADD function g_int_penalty(internal,internal,internal); +ALTER EXTENSION intarray ADD function g_int_picksplit(internal,internal); +ALTER EXTENSION intarray ADD function g_int_union(internal,internal); +ALTER EXTENSION intarray ADD function g_int_same(integer[],integer[],internal); +ALTER EXTENSION intarray ADD operator family gist__int_ops using gist; +ALTER EXTENSION intarray ADD operator class gist__int_ops using gist; +ALTER EXTENSION intarray ADD type intbig_gkey; +ALTER EXTENSION intarray ADD function _intbig_in(cstring); +ALTER EXTENSION intarray ADD function _intbig_out(intbig_gkey); +ALTER EXTENSION intarray ADD function g_intbig_consistent(internal,internal,integer,oid,internal); +ALTER EXTENSION intarray ADD function g_intbig_compress(internal); +ALTER EXTENSION intarray ADD function g_intbig_decompress(internal); +ALTER EXTENSION intarray ADD function g_intbig_penalty(internal,internal,internal); +ALTER EXTENSION intarray ADD function g_intbig_picksplit(internal,internal); +ALTER EXTENSION intarray ADD function g_intbig_union(internal,internal); +ALTER EXTENSION intarray ADD function g_intbig_same(internal,internal,internal); +ALTER EXTENSION intarray ADD operator family gist__intbig_ops using gist; +ALTER EXTENSION intarray ADD operator class gist__intbig_ops using gist; +ALTER EXTENSION intarray ADD operator family gin__int_ops using gin; +ALTER EXTENSION intarray ADD operator class gin__int_ops using gin; + +-- these two functions have different signatures in 9.1, but we don't +-- bother trying to fix them because GIN doesn't care much +ALTER EXTENSION intarray ADD function ginint4_queryextract(internal,internal,smallint,internal,internal); +ALTER EXTENSION intarray ADD function ginint4_consistent(internal,smallint,internal,integer,internal,internal); diff --git a/contrib/intarray/intarray.control b/contrib/intarray/intarray.control new file mode 100644 index 0000000000..7b3d4f78f0 --- /dev/null +++ b/contrib/intarray/intarray.control @@ -0,0 +1,5 @@ +# intarray extension +comment = 'functions, operators, and index support for 1-D arrays of integers' +default_version = '1.0' +module_pathname = '$libdir/_int' +relocatable = true diff --git a/contrib/intarray/sql/_int.sql b/contrib/intarray/sql/_int.sql index 1588e3b514..b60e936dc5 100644 --- a/contrib/intarray/sql/_int.sql +++ b/contrib/intarray/sql/_int.sql @@ -1,12 +1,4 @@ --- --- first, define the datatype. Turn off echoing so that expected file --- does not depend on contents of _int.sql. --- -SET client_min_messages = warning; -\set ECHO none -\i _int.sql -\set ECHO all -RESET client_min_messages; +CREATE EXTENSION intarray; SELECT intset(1234); SELECT icount('{1234234,234234}'); diff --git a/contrib/intarray/uninstall__int.sql b/contrib/intarray/uninstall__int.sql deleted file mode 100644 index 345ad4464b..0000000000 --- a/contrib/intarray/uninstall__int.sql +++ /dev/null @@ -1,128 +0,0 @@ -/* contrib/intarray/uninstall__int.sql */ - --- Adjust this setting to control where the objects get created. -SET search_path = public; - -DROP OPERATOR CLASS gin__int_ops USING gin; - -DROP FUNCTION ginint4_queryextract(internal, internal, int2, internal, internal, internal, internal); - -DROP FUNCTION ginint4_consistent(internal, int2, internal, int4, internal, internal, internal, internal); - -DROP OPERATOR CLASS gist__intbig_ops USING gist; - -DROP FUNCTION g_intbig_same(internal, internal, internal); - -DROP FUNCTION g_intbig_union(internal, internal); - -DROP FUNCTION g_intbig_picksplit(internal, internal); - -DROP FUNCTION g_intbig_penalty(internal,internal,internal); - -DROP FUNCTION g_intbig_decompress(internal); - -DROP FUNCTION g_intbig_compress(internal); - -DROP FUNCTION g_intbig_consistent(internal,internal,int,oid,internal); - -DROP TYPE intbig_gkey CASCADE; - -DROP OPERATOR CLASS gist__int_ops USING gist; - -DROP FUNCTION g_int_same(_int4, _int4, internal); - -DROP FUNCTION g_int_union(internal, internal); - -DROP FUNCTION g_int_picksplit(internal, internal); - -DROP FUNCTION g_int_penalty(internal,internal,internal); - -DROP FUNCTION g_int_decompress(internal); - -DROP FUNCTION g_int_compress(internal); - -DROP FUNCTION g_int_consistent(internal,_int4,int,oid,internal); - -DROP OPERATOR & (_int4, _int4); - -DROP OPERATOR - (_int4, _int4); - -DROP FUNCTION intset_subtract(_int4, _int4); - -DROP OPERATOR | (_int4, _int4); - -DROP OPERATOR | (_int4, int4); - -DROP FUNCTION intset_union_elem(_int4, int4); - -DROP OPERATOR - (_int4, int4); - -DROP FUNCTION intarray_del_elem(_int4, int4); - -DROP OPERATOR + (_int4, _int4); - -DROP FUNCTION intarray_push_array(_int4, _int4); - -DROP OPERATOR + (_int4, int4); - -DROP FUNCTION intarray_push_elem(_int4, int4); - -DROP FUNCTION subarray(_int4, int4); - -DROP FUNCTION subarray(_int4, int4, int4); - -DROP OPERATOR # (_int4, int4); - -DROP FUNCTION idx(_int4, int4); - -DROP FUNCTION uniq(_int4); - -DROP FUNCTION sort_desc(_int4); - -DROP FUNCTION sort_asc(_int4); - -DROP FUNCTION sort(_int4); - -DROP FUNCTION sort(_int4, text); - -DROP OPERATOR # (NONE, _int4); - -DROP FUNCTION icount(_int4); - -DROP FUNCTION intset(int4); - -DROP OPERATOR <@ (_int4, _int4); - -DROP OPERATOR @> (_int4, _int4); - -DROP OPERATOR ~ (_int4, _int4); - -DROP OPERATOR @ (_int4, _int4); - -DROP OPERATOR && (_int4, _int4); - -DROP FUNCTION _int_inter(_int4, _int4); - -DROP FUNCTION _int_union(_int4, _int4); - -DROP FUNCTION _int_different(_int4, _int4); - -DROP FUNCTION _int_same(_int4, _int4); - -DROP FUNCTION _int_overlap(_int4, _int4); - -DROP FUNCTION _int_contained(_int4, _int4); - -DROP FUNCTION _int_contains(_int4, _int4); - -DROP OPERATOR ~~ (query_int, _int4); - -DROP OPERATOR @@ (_int4, query_int); - -DROP FUNCTION rboolop(query_int, _int4); - -DROP FUNCTION boolop(_int4, query_int); - -DROP FUNCTION querytree(query_int); - -DROP TYPE query_int CASCADE; diff --git a/contrib/isn/.gitignore b/contrib/isn/.gitignore deleted file mode 100644 index 1df12e3b75..0000000000 --- a/contrib/isn/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/isn.sql diff --git a/contrib/isn/Makefile b/contrib/isn/Makefile index ae33b758fb..bd8f193e93 100644 --- a/contrib/isn/Makefile +++ b/contrib/isn/Makefile @@ -1,8 +1,9 @@ # contrib/isn/Makefile MODULES = isn -DATA_built = isn.sql -DATA = uninstall_isn.sql + +EXTENSION = isn +DATA = isn--1.0.sql isn--unpackaged--1.0.sql ifdef USE_PGXS PG_CONFIG = pg_config diff --git a/contrib/isn/isn.sql.in b/contrib/isn/isn--1.0.sql index 8f73c5c497..336ad1db3c 100644 --- a/contrib/isn/isn.sql.in +++ b/contrib/isn/isn--1.0.sql @@ -1,7 +1,4 @@ -/* contrib/isn/isn.sql.in */ - --- Adjust this setting to control where the objects get created. -SET search_path = public; +/* contrib/isn/isn--1.0.sql */ -- Example: -- create table test ( id isbn ); @@ -15,12 +12,12 @@ SET search_path = public; -- Input and output functions and data types: -- --------------------------------------------------- -CREATE OR REPLACE FUNCTION ean13_in(cstring) +CREATE FUNCTION ean13_in(cstring) RETURNS ean13 AS 'MODULE_PATHNAME' LANGUAGE 'C' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION ean13_out(ean13) +CREATE FUNCTION ean13_out(ean13) RETURNS cstring AS 'MODULE_PATHNAME' LANGUAGE 'C' @@ -33,12 +30,12 @@ CREATE TYPE ean13 ( COMMENT ON TYPE ean13 IS 'International European Article Number (EAN13)'; -CREATE OR REPLACE FUNCTION isbn13_in(cstring) +CREATE FUNCTION isbn13_in(cstring) RETURNS isbn13 AS 'MODULE_PATHNAME', 'isbn_in' LANGUAGE 'C' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION ean13_out(isbn13) +CREATE FUNCTION ean13_out(isbn13) RETURNS cstring AS 'MODULE_PATHNAME' LANGUAGE 'C' @@ -51,12 +48,12 @@ CREATE TYPE isbn13 ( COMMENT ON TYPE isbn13 IS 'International Standard Book Number 13 (ISBN13)'; -CREATE OR REPLACE FUNCTION ismn13_in(cstring) +CREATE FUNCTION ismn13_in(cstring) RETURNS ismn13 AS 'MODULE_PATHNAME', 'ismn_in' LANGUAGE 'C' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION ean13_out(ismn13) +CREATE FUNCTION ean13_out(ismn13) RETURNS cstring AS 'MODULE_PATHNAME' LANGUAGE 'C' @@ -69,12 +66,12 @@ CREATE TYPE ismn13 ( COMMENT ON TYPE ismn13 IS 'International Standard Music Number 13 (ISMN13)'; -CREATE OR REPLACE FUNCTION issn13_in(cstring) +CREATE FUNCTION issn13_in(cstring) RETURNS issn13 AS 'MODULE_PATHNAME', 'issn_in' LANGUAGE 'C' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION ean13_out(issn13) +CREATE FUNCTION ean13_out(issn13) RETURNS cstring AS 'MODULE_PATHNAME' LANGUAGE 'C' @@ -89,12 +86,12 @@ COMMENT ON TYPE issn13 -- Short format: -CREATE OR REPLACE FUNCTION isbn_in(cstring) +CREATE FUNCTION isbn_in(cstring) RETURNS isbn AS 'MODULE_PATHNAME' LANGUAGE 'C' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isn_out(isbn) +CREATE FUNCTION isn_out(isbn) RETURNS cstring AS 'MODULE_PATHNAME' LANGUAGE 'C' @@ -107,12 +104,12 @@ CREATE TYPE isbn ( COMMENT ON TYPE isbn IS 'International Standard Book Number (ISBN)'; -CREATE OR REPLACE FUNCTION ismn_in(cstring) +CREATE FUNCTION ismn_in(cstring) RETURNS ismn AS 'MODULE_PATHNAME' LANGUAGE 'C' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isn_out(ismn) +CREATE FUNCTION isn_out(ismn) RETURNS cstring AS 'MODULE_PATHNAME' LANGUAGE 'C' @@ -125,12 +122,12 @@ CREATE TYPE ismn ( COMMENT ON TYPE ismn IS 'International Standard Music Number (ISMN)'; -CREATE OR REPLACE FUNCTION issn_in(cstring) +CREATE FUNCTION issn_in(cstring) RETURNS issn AS 'MODULE_PATHNAME' LANGUAGE 'C' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isn_out(issn) +CREATE FUNCTION isn_out(issn) RETURNS cstring AS 'MODULE_PATHNAME' LANGUAGE 'C' @@ -143,12 +140,12 @@ CREATE TYPE issn ( COMMENT ON TYPE issn IS 'International Standard Serial Number (ISSN)'; -CREATE OR REPLACE FUNCTION upc_in(cstring) +CREATE FUNCTION upc_in(cstring) RETURNS upc AS 'MODULE_PATHNAME' LANGUAGE 'C' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isn_out(upc) +CREATE FUNCTION isn_out(upc) RETURNS cstring AS 'MODULE_PATHNAME' LANGUAGE 'C' @@ -166,249 +163,249 @@ COMMENT ON TYPE upc -- --------------------------------------------------- -- EAN13: -CREATE OR REPLACE FUNCTION isnlt(ean13, ean13) +CREATE FUNCTION isnlt(ean13, ean13) RETURNS boolean AS 'int8lt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnle(ean13, ean13) +CREATE FUNCTION isnle(ean13, ean13) RETURNS boolean AS 'int8le' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isneq(ean13, ean13) +CREATE FUNCTION isneq(ean13, ean13) RETURNS boolean AS 'int8eq' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnge(ean13, ean13) +CREATE FUNCTION isnge(ean13, ean13) RETURNS boolean AS 'int8ge' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isngt(ean13, ean13) +CREATE FUNCTION isngt(ean13, ean13) RETURNS boolean AS 'int8gt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnne(ean13, ean13) +CREATE FUNCTION isnne(ean13, ean13) RETURNS boolean AS 'int8ne' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnlt(ean13, isbn13) +CREATE FUNCTION isnlt(ean13, isbn13) RETURNS boolean AS 'int8lt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnle(ean13, isbn13) +CREATE FUNCTION isnle(ean13, isbn13) RETURNS boolean AS 'int8le' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isneq(ean13, isbn13) +CREATE FUNCTION isneq(ean13, isbn13) RETURNS boolean AS 'int8eq' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnge(ean13, isbn13) +CREATE FUNCTION isnge(ean13, isbn13) RETURNS boolean AS 'int8ge' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isngt(ean13, isbn13) +CREATE FUNCTION isngt(ean13, isbn13) RETURNS boolean AS 'int8gt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnne(ean13, isbn13) +CREATE FUNCTION isnne(ean13, isbn13) RETURNS boolean AS 'int8ne' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnlt(ean13, ismn13) +CREATE FUNCTION isnlt(ean13, ismn13) RETURNS boolean AS 'int8lt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnle(ean13, ismn13) +CREATE FUNCTION isnle(ean13, ismn13) RETURNS boolean AS 'int8le' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isneq(ean13, ismn13) +CREATE FUNCTION isneq(ean13, ismn13) RETURNS boolean AS 'int8eq' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnge(ean13, ismn13) +CREATE FUNCTION isnge(ean13, ismn13) RETURNS boolean AS 'int8ge' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isngt(ean13, ismn13) +CREATE FUNCTION isngt(ean13, ismn13) RETURNS boolean AS 'int8gt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnne(ean13, ismn13) +CREATE FUNCTION isnne(ean13, ismn13) RETURNS boolean AS 'int8ne' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnlt(ean13, issn13) +CREATE FUNCTION isnlt(ean13, issn13) RETURNS boolean AS 'int8lt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnle(ean13, issn13) +CREATE FUNCTION isnle(ean13, issn13) RETURNS boolean AS 'int8le' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isneq(ean13, issn13) +CREATE FUNCTION isneq(ean13, issn13) RETURNS boolean AS 'int8eq' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnge(ean13, issn13) +CREATE FUNCTION isnge(ean13, issn13) RETURNS boolean AS 'int8ge' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isngt(ean13, issn13) +CREATE FUNCTION isngt(ean13, issn13) RETURNS boolean AS 'int8gt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnne(ean13, issn13) +CREATE FUNCTION isnne(ean13, issn13) RETURNS boolean AS 'int8ne' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnlt(ean13, isbn) +CREATE FUNCTION isnlt(ean13, isbn) RETURNS boolean AS 'int8lt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnle(ean13, isbn) +CREATE FUNCTION isnle(ean13, isbn) RETURNS boolean AS 'int8le' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isneq(ean13, isbn) +CREATE FUNCTION isneq(ean13, isbn) RETURNS boolean AS 'int8eq' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnge(ean13, isbn) +CREATE FUNCTION isnge(ean13, isbn) RETURNS boolean AS 'int8ge' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isngt(ean13, isbn) +CREATE FUNCTION isngt(ean13, isbn) RETURNS boolean AS 'int8gt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnne(ean13, isbn) +CREATE FUNCTION isnne(ean13, isbn) RETURNS boolean AS 'int8ne' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnlt(ean13, ismn) +CREATE FUNCTION isnlt(ean13, ismn) RETURNS boolean AS 'int8lt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnle(ean13, ismn) +CREATE FUNCTION isnle(ean13, ismn) RETURNS boolean AS 'int8le' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isneq(ean13, ismn) +CREATE FUNCTION isneq(ean13, ismn) RETURNS boolean AS 'int8eq' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnge(ean13, ismn) +CREATE FUNCTION isnge(ean13, ismn) RETURNS boolean AS 'int8ge' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isngt(ean13, ismn) +CREATE FUNCTION isngt(ean13, ismn) RETURNS boolean AS 'int8gt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnne(ean13, ismn) +CREATE FUNCTION isnne(ean13, ismn) RETURNS boolean AS 'int8ne' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnlt(ean13, issn) +CREATE FUNCTION isnlt(ean13, issn) RETURNS boolean AS 'int8lt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnle(ean13, issn) +CREATE FUNCTION isnle(ean13, issn) RETURNS boolean AS 'int8le' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isneq(ean13, issn) +CREATE FUNCTION isneq(ean13, issn) RETURNS boolean AS 'int8eq' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnge(ean13, issn) +CREATE FUNCTION isnge(ean13, issn) RETURNS boolean AS 'int8ge' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isngt(ean13, issn) +CREATE FUNCTION isngt(ean13, issn) RETURNS boolean AS 'int8gt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnne(ean13, issn) +CREATE FUNCTION isnne(ean13, issn) RETURNS boolean AS 'int8ne' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnlt(ean13, upc) +CREATE FUNCTION isnlt(ean13, upc) RETURNS boolean AS 'int8lt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnle(ean13, upc) +CREATE FUNCTION isnle(ean13, upc) RETURNS boolean AS 'int8le' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isneq(ean13, upc) +CREATE FUNCTION isneq(ean13, upc) RETURNS boolean AS 'int8eq' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnge(ean13, upc) +CREATE FUNCTION isnge(ean13, upc) RETURNS boolean AS 'int8ge' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isngt(ean13, upc) +CREATE FUNCTION isngt(ean13, upc) RETURNS boolean AS 'int8gt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnne(ean13, upc) +CREATE FUNCTION isnne(ean13, upc) RETURNS boolean AS 'int8ne' LANGUAGE 'internal' @@ -416,94 +413,94 @@ CREATE OR REPLACE FUNCTION isnne(ean13, upc) --------------------------------------------------- -- ISBN13: -CREATE OR REPLACE FUNCTION isnlt(isbn13, isbn13) +CREATE FUNCTION isnlt(isbn13, isbn13) RETURNS boolean AS 'int8lt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnle(isbn13, isbn13) +CREATE FUNCTION isnle(isbn13, isbn13) RETURNS boolean AS 'int8le' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isneq(isbn13, isbn13) +CREATE FUNCTION isneq(isbn13, isbn13) RETURNS boolean AS 'int8eq' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnge(isbn13, isbn13) +CREATE FUNCTION isnge(isbn13, isbn13) RETURNS boolean AS 'int8ge' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isngt(isbn13, isbn13) +CREATE FUNCTION isngt(isbn13, isbn13) RETURNS boolean AS 'int8gt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnne(isbn13, isbn13) +CREATE FUNCTION isnne(isbn13, isbn13) RETURNS boolean AS 'int8ne' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnlt(isbn13, isbn) +CREATE FUNCTION isnlt(isbn13, isbn) RETURNS boolean AS 'int8lt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnle(isbn13, isbn) +CREATE FUNCTION isnle(isbn13, isbn) RETURNS boolean AS 'int8le' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isneq(isbn13, isbn) +CREATE FUNCTION isneq(isbn13, isbn) RETURNS boolean AS 'int8eq' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnge(isbn13, isbn) +CREATE FUNCTION isnge(isbn13, isbn) RETURNS boolean AS 'int8ge' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isngt(isbn13, isbn) +CREATE FUNCTION isngt(isbn13, isbn) RETURNS boolean AS 'int8gt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnne(isbn13, isbn) +CREATE FUNCTION isnne(isbn13, isbn) RETURNS boolean AS 'int8ne' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnlt(isbn13, ean13) +CREATE FUNCTION isnlt(isbn13, ean13) RETURNS boolean AS 'int8lt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnle(isbn13, ean13) +CREATE FUNCTION isnle(isbn13, ean13) RETURNS boolean AS 'int8le' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isneq(isbn13, ean13) +CREATE FUNCTION isneq(isbn13, ean13) RETURNS boolean AS 'int8eq' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnge(isbn13, ean13) +CREATE FUNCTION isnge(isbn13, ean13) RETURNS boolean AS 'int8ge' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isngt(isbn13, ean13) +CREATE FUNCTION isngt(isbn13, ean13) RETURNS boolean AS 'int8gt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnne(isbn13, ean13) +CREATE FUNCTION isnne(isbn13, ean13) RETURNS boolean AS 'int8ne' LANGUAGE 'internal' @@ -511,94 +508,94 @@ CREATE OR REPLACE FUNCTION isnne(isbn13, ean13) --------------------------------------------------- -- ISBN: -CREATE OR REPLACE FUNCTION isnlt(isbn, isbn) +CREATE FUNCTION isnlt(isbn, isbn) RETURNS boolean AS 'int8lt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnle(isbn, isbn) +CREATE FUNCTION isnle(isbn, isbn) RETURNS boolean AS 'int8le' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isneq(isbn, isbn) +CREATE FUNCTION isneq(isbn, isbn) RETURNS boolean AS 'int8eq' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnge(isbn, isbn) +CREATE FUNCTION isnge(isbn, isbn) RETURNS boolean AS 'int8ge' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isngt(isbn, isbn) +CREATE FUNCTION isngt(isbn, isbn) RETURNS boolean AS 'int8gt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnne(isbn, isbn) +CREATE FUNCTION isnne(isbn, isbn) RETURNS boolean AS 'int8ne' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnlt(isbn, isbn13) +CREATE FUNCTION isnlt(isbn, isbn13) RETURNS boolean AS 'int8lt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnle(isbn, isbn13) +CREATE FUNCTION isnle(isbn, isbn13) RETURNS boolean AS 'int8le' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isneq(isbn, isbn13) +CREATE FUNCTION isneq(isbn, isbn13) RETURNS boolean AS 'int8eq' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnge(isbn, isbn13) +CREATE FUNCTION isnge(isbn, isbn13) RETURNS boolean AS 'int8ge' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isngt(isbn, isbn13) +CREATE FUNCTION isngt(isbn, isbn13) RETURNS boolean AS 'int8gt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnne(isbn, isbn13) +CREATE FUNCTION isnne(isbn, isbn13) RETURNS boolean AS 'int8ne' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnlt(isbn, ean13) +CREATE FUNCTION isnlt(isbn, ean13) RETURNS boolean AS 'int8lt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnle(isbn, ean13) +CREATE FUNCTION isnle(isbn, ean13) RETURNS boolean AS 'int8le' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isneq(isbn, ean13) +CREATE FUNCTION isneq(isbn, ean13) RETURNS boolean AS 'int8eq' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnge(isbn, ean13) +CREATE FUNCTION isnge(isbn, ean13) RETURNS boolean AS 'int8ge' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isngt(isbn, ean13) +CREATE FUNCTION isngt(isbn, ean13) RETURNS boolean AS 'int8gt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnne(isbn, ean13) +CREATE FUNCTION isnne(isbn, ean13) RETURNS boolean AS 'int8ne' LANGUAGE 'internal' @@ -606,94 +603,94 @@ CREATE OR REPLACE FUNCTION isnne(isbn, ean13) --------------------------------------------------- -- ISMN13: -CREATE OR REPLACE FUNCTION isnlt(ismn13, ismn13) +CREATE FUNCTION isnlt(ismn13, ismn13) RETURNS boolean AS 'int8lt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnle(ismn13, ismn13) +CREATE FUNCTION isnle(ismn13, ismn13) RETURNS boolean AS 'int8le' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isneq(ismn13, ismn13) +CREATE FUNCTION isneq(ismn13, ismn13) RETURNS boolean AS 'int8eq' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnge(ismn13, ismn13) +CREATE FUNCTION isnge(ismn13, ismn13) RETURNS boolean AS 'int8ge' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isngt(ismn13, ismn13) +CREATE FUNCTION isngt(ismn13, ismn13) RETURNS boolean AS 'int8gt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnne(ismn13, ismn13) +CREATE FUNCTION isnne(ismn13, ismn13) RETURNS boolean AS 'int8ne' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnlt(ismn13, ismn) +CREATE FUNCTION isnlt(ismn13, ismn) RETURNS boolean AS 'int8lt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnle(ismn13, ismn) +CREATE FUNCTION isnle(ismn13, ismn) RETURNS boolean AS 'int8le' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isneq(ismn13, ismn) +CREATE FUNCTION isneq(ismn13, ismn) RETURNS boolean AS 'int8eq' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnge(ismn13, ismn) +CREATE FUNCTION isnge(ismn13, ismn) RETURNS boolean AS 'int8ge' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isngt(ismn13, ismn) +CREATE FUNCTION isngt(ismn13, ismn) RETURNS boolean AS 'int8gt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnne(ismn13, ismn) +CREATE FUNCTION isnne(ismn13, ismn) RETURNS boolean AS 'int8ne' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnlt(ismn13, ean13) +CREATE FUNCTION isnlt(ismn13, ean13) RETURNS boolean AS 'int8lt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnle(ismn13, ean13) +CREATE FUNCTION isnle(ismn13, ean13) RETURNS boolean AS 'int8le' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isneq(ismn13, ean13) +CREATE FUNCTION isneq(ismn13, ean13) RETURNS boolean AS 'int8eq' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnge(ismn13, ean13) +CREATE FUNCTION isnge(ismn13, ean13) RETURNS boolean AS 'int8ge' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isngt(ismn13, ean13) +CREATE FUNCTION isngt(ismn13, ean13) RETURNS boolean AS 'int8gt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnne(ismn13, ean13) +CREATE FUNCTION isnne(ismn13, ean13) RETURNS boolean AS 'int8ne' LANGUAGE 'internal' @@ -701,94 +698,94 @@ CREATE OR REPLACE FUNCTION isnne(ismn13, ean13) --------------------------------------------------- -- ISMN: -CREATE OR REPLACE FUNCTION isnlt(ismn, ismn) +CREATE FUNCTION isnlt(ismn, ismn) RETURNS boolean AS 'int8lt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnle(ismn, ismn) +CREATE FUNCTION isnle(ismn, ismn) RETURNS boolean AS 'int8le' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isneq(ismn, ismn) +CREATE FUNCTION isneq(ismn, ismn) RETURNS boolean AS 'int8eq' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnge(ismn, ismn) +CREATE FUNCTION isnge(ismn, ismn) RETURNS boolean AS 'int8ge' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isngt(ismn, ismn) +CREATE FUNCTION isngt(ismn, ismn) RETURNS boolean AS 'int8gt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnne(ismn, ismn) +CREATE FUNCTION isnne(ismn, ismn) RETURNS boolean AS 'int8ne' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnlt(ismn, ismn13) +CREATE FUNCTION isnlt(ismn, ismn13) RETURNS boolean AS 'int8lt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnle(ismn, ismn13) +CREATE FUNCTION isnle(ismn, ismn13) RETURNS boolean AS 'int8le' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isneq(ismn, ismn13) +CREATE FUNCTION isneq(ismn, ismn13) RETURNS boolean AS 'int8eq' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnge(ismn, ismn13) +CREATE FUNCTION isnge(ismn, ismn13) RETURNS boolean AS 'int8ge' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isngt(ismn, ismn13) +CREATE FUNCTION isngt(ismn, ismn13) RETURNS boolean AS 'int8gt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnne(ismn, ismn13) +CREATE FUNCTION isnne(ismn, ismn13) RETURNS boolean AS 'int8ne' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnlt(ismn, ean13) +CREATE FUNCTION isnlt(ismn, ean13) RETURNS boolean AS 'int8lt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnle(ismn, ean13) +CREATE FUNCTION isnle(ismn, ean13) RETURNS boolean AS 'int8le' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isneq(ismn, ean13) +CREATE FUNCTION isneq(ismn, ean13) RETURNS boolean AS 'int8eq' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnge(ismn, ean13) +CREATE FUNCTION isnge(ismn, ean13) RETURNS boolean AS 'int8ge' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isngt(ismn, ean13) +CREATE FUNCTION isngt(ismn, ean13) RETURNS boolean AS 'int8gt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnne(ismn, ean13) +CREATE FUNCTION isnne(ismn, ean13) RETURNS boolean AS 'int8ne' LANGUAGE 'internal' @@ -796,94 +793,94 @@ CREATE OR REPLACE FUNCTION isnne(ismn, ean13) --------------------------------------------------- -- ISSN13: -CREATE OR REPLACE FUNCTION isnlt(issn13, issn13) +CREATE FUNCTION isnlt(issn13, issn13) RETURNS boolean AS 'int8lt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnle(issn13, issn13) +CREATE FUNCTION isnle(issn13, issn13) RETURNS boolean AS 'int8le' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isneq(issn13, issn13) +CREATE FUNCTION isneq(issn13, issn13) RETURNS boolean AS 'int8eq' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnge(issn13, issn13) +CREATE FUNCTION isnge(issn13, issn13) RETURNS boolean AS 'int8ge' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isngt(issn13, issn13) +CREATE FUNCTION isngt(issn13, issn13) RETURNS boolean AS 'int8gt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnne(issn13, issn13) +CREATE FUNCTION isnne(issn13, issn13) RETURNS boolean AS 'int8ne' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnlt(issn13, issn) +CREATE FUNCTION isnlt(issn13, issn) RETURNS boolean AS 'int8lt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnle(issn13, issn) +CREATE FUNCTION isnle(issn13, issn) RETURNS boolean AS 'int8le' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isneq(issn13, issn) +CREATE FUNCTION isneq(issn13, issn) RETURNS boolean AS 'int8eq' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnge(issn13, issn) +CREATE FUNCTION isnge(issn13, issn) RETURNS boolean AS 'int8ge' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isngt(issn13, issn) +CREATE FUNCTION isngt(issn13, issn) RETURNS boolean AS 'int8gt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnne(issn13, issn) +CREATE FUNCTION isnne(issn13, issn) RETURNS boolean AS 'int8ne' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnlt(issn13, ean13) +CREATE FUNCTION isnlt(issn13, ean13) RETURNS boolean AS 'int8lt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnle(issn13, ean13) +CREATE FUNCTION isnle(issn13, ean13) RETURNS boolean AS 'int8le' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isneq(issn13, ean13) +CREATE FUNCTION isneq(issn13, ean13) RETURNS boolean AS 'int8eq' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnge(issn13, ean13) +CREATE FUNCTION isnge(issn13, ean13) RETURNS boolean AS 'int8ge' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isngt(issn13, ean13) +CREATE FUNCTION isngt(issn13, ean13) RETURNS boolean AS 'int8gt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnne(issn13, ean13) +CREATE FUNCTION isnne(issn13, ean13) RETURNS boolean AS 'int8ne' LANGUAGE 'internal' @@ -891,94 +888,94 @@ CREATE OR REPLACE FUNCTION isnne(issn13, ean13) --------------------------------------------------- -- ISSN: -CREATE OR REPLACE FUNCTION isnlt(issn, issn) +CREATE FUNCTION isnlt(issn, issn) RETURNS boolean AS 'int8lt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnle(issn, issn) +CREATE FUNCTION isnle(issn, issn) RETURNS boolean AS 'int8le' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isneq(issn, issn) +CREATE FUNCTION isneq(issn, issn) RETURNS boolean AS 'int8eq' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnge(issn, issn) +CREATE FUNCTION isnge(issn, issn) RETURNS boolean AS 'int8ge' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isngt(issn, issn) +CREATE FUNCTION isngt(issn, issn) RETURNS boolean AS 'int8gt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnne(issn, issn) +CREATE FUNCTION isnne(issn, issn) RETURNS boolean AS 'int8ne' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnlt(issn, issn13) +CREATE FUNCTION isnlt(issn, issn13) RETURNS boolean AS 'int8lt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnle(issn, issn13) +CREATE FUNCTION isnle(issn, issn13) RETURNS boolean AS 'int8le' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isneq(issn, issn13) +CREATE FUNCTION isneq(issn, issn13) RETURNS boolean AS 'int8eq' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnge(issn, issn13) +CREATE FUNCTION isnge(issn, issn13) RETURNS boolean AS 'int8ge' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isngt(issn, issn13) +CREATE FUNCTION isngt(issn, issn13) RETURNS boolean AS 'int8gt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnne(issn, issn13) +CREATE FUNCTION isnne(issn, issn13) RETURNS boolean AS 'int8ne' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnlt(issn, ean13) +CREATE FUNCTION isnlt(issn, ean13) RETURNS boolean AS 'int8lt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnle(issn, ean13) +CREATE FUNCTION isnle(issn, ean13) RETURNS boolean AS 'int8le' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isneq(issn, ean13) +CREATE FUNCTION isneq(issn, ean13) RETURNS boolean AS 'int8eq' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnge(issn, ean13) +CREATE FUNCTION isnge(issn, ean13) RETURNS boolean AS 'int8ge' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isngt(issn, ean13) +CREATE FUNCTION isngt(issn, ean13) RETURNS boolean AS 'int8gt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnne(issn, ean13) +CREATE FUNCTION isnne(issn, ean13) RETURNS boolean AS 'int8ne' LANGUAGE 'internal' @@ -986,63 +983,63 @@ CREATE OR REPLACE FUNCTION isnne(issn, ean13) --------------------------------------------------- -- UPC: -CREATE OR REPLACE FUNCTION isnlt(upc, upc) +CREATE FUNCTION isnlt(upc, upc) RETURNS boolean AS 'int8lt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnle(upc, upc) +CREATE FUNCTION isnle(upc, upc) RETURNS boolean AS 'int8le' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isneq(upc, upc) +CREATE FUNCTION isneq(upc, upc) RETURNS boolean AS 'int8eq' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnge(upc, upc) +CREATE FUNCTION isnge(upc, upc) RETURNS boolean AS 'int8ge' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isngt(upc, upc) +CREATE FUNCTION isngt(upc, upc) RETURNS boolean AS 'int8gt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnne(upc, upc) +CREATE FUNCTION isnne(upc, upc) RETURNS boolean AS 'int8ne' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnlt(upc, ean13) +CREATE FUNCTION isnlt(upc, ean13) RETURNS boolean AS 'int8lt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnle(upc, ean13) +CREATE FUNCTION isnle(upc, ean13) RETURNS boolean AS 'int8le' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isneq(upc, ean13) +CREATE FUNCTION isneq(upc, ean13) RETURNS boolean AS 'int8eq' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnge(upc, ean13) +CREATE FUNCTION isnge(upc, ean13) RETURNS boolean AS 'int8ge' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isngt(upc, ean13) +CREATE FUNCTION isngt(upc, ean13) RETURNS boolean AS 'int8gt' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isnne(upc, ean13) +CREATE FUNCTION isnne(upc, ean13) RETURNS boolean AS 'int8ne' LANGUAGE 'internal' @@ -2525,7 +2522,7 @@ CREATE OPERATOR FAMILY isn_ops USING hash; -- --------------------------------------------------- -- EAN13: -CREATE OR REPLACE FUNCTION btean13cmp(ean13, ean13) +CREATE FUNCTION btean13cmp(ean13, ean13) RETURNS int4 AS 'btint8cmp' LANGUAGE 'internal' @@ -2540,7 +2537,7 @@ CREATE OPERATOR CLASS ean13_ops DEFAULT OPERATOR 5 >, FUNCTION 1 btean13cmp(ean13, ean13); -CREATE OR REPLACE FUNCTION hashean13(ean13) +CREATE FUNCTION hashean13(ean13) RETURNS int4 AS 'hashint8' LANGUAGE 'internal' IMMUTABLE STRICT; @@ -2551,37 +2548,37 @@ CREATE OPERATOR CLASS ean13_ops DEFAULT FUNCTION 1 hashean13(ean13); -- EAN13 vs other types: -CREATE OR REPLACE FUNCTION btean13cmp(ean13, isbn13) +CREATE FUNCTION btean13cmp(ean13, isbn13) RETURNS int4 AS 'btint8cmp' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION btean13cmp(ean13, ismn13) +CREATE FUNCTION btean13cmp(ean13, ismn13) RETURNS int4 AS 'btint8cmp' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION btean13cmp(ean13, issn13) +CREATE FUNCTION btean13cmp(ean13, issn13) RETURNS int4 AS 'btint8cmp' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION btean13cmp(ean13, isbn) +CREATE FUNCTION btean13cmp(ean13, isbn) RETURNS int4 AS 'btint8cmp' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION btean13cmp(ean13, ismn) +CREATE FUNCTION btean13cmp(ean13, ismn) RETURNS int4 AS 'btint8cmp' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION btean13cmp(ean13, issn) +CREATE FUNCTION btean13cmp(ean13, issn) RETURNS int4 AS 'btint8cmp' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION btean13cmp(ean13, upc) +CREATE FUNCTION btean13cmp(ean13, upc) RETURNS int4 AS 'btint8cmp' LANGUAGE 'internal' @@ -2642,7 +2639,7 @@ ALTER OPERATOR FAMILY isn_ops USING hash ADD --------------------------------------------------- -- ISBN13: -CREATE OR REPLACE FUNCTION btisbn13cmp(isbn13, isbn13) +CREATE FUNCTION btisbn13cmp(isbn13, isbn13) RETURNS int4 AS 'btint8cmp' LANGUAGE 'internal' @@ -2657,7 +2654,7 @@ CREATE OPERATOR CLASS isbn13_ops DEFAULT OPERATOR 5 >, FUNCTION 1 btisbn13cmp(isbn13, isbn13); -CREATE OR REPLACE FUNCTION hashisbn13(isbn13) +CREATE FUNCTION hashisbn13(isbn13) RETURNS int4 AS 'hashint8' LANGUAGE 'internal' @@ -2669,12 +2666,12 @@ CREATE OPERATOR CLASS isbn13_ops DEFAULT FUNCTION 1 hashisbn13(isbn13); -- ISBN13 vs other types: -CREATE OR REPLACE FUNCTION btisbn13cmp(isbn13, ean13) +CREATE FUNCTION btisbn13cmp(isbn13, ean13) RETURNS int4 AS 'btint8cmp' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION btisbn13cmp(isbn13, isbn) +CREATE FUNCTION btisbn13cmp(isbn13, isbn) RETURNS int4 AS 'btint8cmp' LANGUAGE 'internal' @@ -2700,7 +2697,7 @@ ALTER OPERATOR FAMILY isn_ops USING hash ADD --------------------------------------------------- -- ISBN: -CREATE OR REPLACE FUNCTION btisbncmp(isbn, isbn) +CREATE FUNCTION btisbncmp(isbn, isbn) RETURNS int4 AS 'btint8cmp' LANGUAGE 'internal' @@ -2715,7 +2712,7 @@ CREATE OPERATOR CLASS isbn_ops DEFAULT OPERATOR 5 >, FUNCTION 1 btisbncmp(isbn, isbn); -CREATE OR REPLACE FUNCTION hashisbn(isbn) +CREATE FUNCTION hashisbn(isbn) RETURNS int4 AS 'hashint8' LANGUAGE 'internal' @@ -2727,12 +2724,12 @@ CREATE OPERATOR CLASS isbn_ops DEFAULT FUNCTION 1 hashisbn(isbn); -- ISBN vs other types: -CREATE OR REPLACE FUNCTION btisbncmp(isbn, ean13) +CREATE FUNCTION btisbncmp(isbn, ean13) RETURNS int4 AS 'btint8cmp' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION btisbncmp(isbn, isbn13) +CREATE FUNCTION btisbncmp(isbn, isbn13) RETURNS int4 AS 'btint8cmp' LANGUAGE 'internal' @@ -2758,7 +2755,7 @@ ALTER OPERATOR FAMILY isn_ops USING hash ADD --------------------------------------------------- -- ISMN13: -CREATE OR REPLACE FUNCTION btismn13cmp(ismn13, ismn13) +CREATE FUNCTION btismn13cmp(ismn13, ismn13) RETURNS int4 AS 'btint8cmp' LANGUAGE 'internal' @@ -2773,7 +2770,7 @@ CREATE OPERATOR CLASS ismn13_ops DEFAULT OPERATOR 5 >, FUNCTION 1 btismn13cmp(ismn13, ismn13); -CREATE OR REPLACE FUNCTION hashismn13(ismn13) +CREATE FUNCTION hashismn13(ismn13) RETURNS int4 AS 'hashint8' LANGUAGE 'internal' @@ -2785,12 +2782,12 @@ CREATE OPERATOR CLASS ismn13_ops DEFAULT FUNCTION 1 hashismn13(ismn13); -- ISMN13 vs other types: -CREATE OR REPLACE FUNCTION btismn13cmp(ismn13, ean13) +CREATE FUNCTION btismn13cmp(ismn13, ean13) RETURNS int4 AS 'btint8cmp' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION btismn13cmp(ismn13, ismn) +CREATE FUNCTION btismn13cmp(ismn13, ismn) RETURNS int4 AS 'btint8cmp' LANGUAGE 'internal' @@ -2816,7 +2813,7 @@ ALTER OPERATOR FAMILY isn_ops USING hash ADD --------------------------------------------------- -- ISMN: -CREATE OR REPLACE FUNCTION btismncmp(ismn, ismn) +CREATE FUNCTION btismncmp(ismn, ismn) RETURNS int4 AS 'btint8cmp' LANGUAGE 'internal' @@ -2831,7 +2828,7 @@ CREATE OPERATOR CLASS ismn_ops DEFAULT OPERATOR 5 >, FUNCTION 1 btismncmp(ismn, ismn); -CREATE OR REPLACE FUNCTION hashismn(ismn) +CREATE FUNCTION hashismn(ismn) RETURNS int4 AS 'hashint8' LANGUAGE 'internal' @@ -2843,12 +2840,12 @@ CREATE OPERATOR CLASS ismn_ops DEFAULT FUNCTION 1 hashismn(ismn); -- ISMN vs other types: -CREATE OR REPLACE FUNCTION btismncmp(ismn, ean13) +CREATE FUNCTION btismncmp(ismn, ean13) RETURNS int4 AS 'btint8cmp' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION btismncmp(ismn, ismn13) +CREATE FUNCTION btismncmp(ismn, ismn13) RETURNS int4 AS 'btint8cmp' LANGUAGE 'internal' @@ -2874,7 +2871,7 @@ ALTER OPERATOR FAMILY isn_ops USING hash ADD --------------------------------------------------- -- ISSN13: -CREATE OR REPLACE FUNCTION btissn13cmp(issn13, issn13) +CREATE FUNCTION btissn13cmp(issn13, issn13) RETURNS int4 AS 'btint8cmp' LANGUAGE 'internal' @@ -2889,7 +2886,7 @@ CREATE OPERATOR CLASS issn13_ops DEFAULT OPERATOR 5 >, FUNCTION 1 btissn13cmp(issn13, issn13); -CREATE OR REPLACE FUNCTION hashissn13(issn13) +CREATE FUNCTION hashissn13(issn13) RETURNS int4 AS 'hashint8' LANGUAGE 'internal' @@ -2901,12 +2898,12 @@ CREATE OPERATOR CLASS issn13_ops DEFAULT FUNCTION 1 hashissn13(issn13); -- ISSN13 vs other types: -CREATE OR REPLACE FUNCTION btissn13cmp(issn13, ean13) +CREATE FUNCTION btissn13cmp(issn13, ean13) RETURNS int4 AS 'btint8cmp' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION btissn13cmp(issn13, issn) +CREATE FUNCTION btissn13cmp(issn13, issn) RETURNS int4 AS 'btint8cmp' LANGUAGE 'internal' @@ -2932,7 +2929,7 @@ ALTER OPERATOR FAMILY isn_ops USING hash ADD --------------------------------------------------- -- ISSN: -CREATE OR REPLACE FUNCTION btissncmp(issn, issn) +CREATE FUNCTION btissncmp(issn, issn) RETURNS int4 AS 'btint8cmp' LANGUAGE 'internal' @@ -2947,7 +2944,7 @@ CREATE OPERATOR CLASS issn_ops DEFAULT OPERATOR 5 >, FUNCTION 1 btissncmp(issn, issn); -CREATE OR REPLACE FUNCTION hashissn(issn) +CREATE FUNCTION hashissn(issn) RETURNS int4 AS 'hashint8' LANGUAGE 'internal' @@ -2959,12 +2956,12 @@ CREATE OPERATOR CLASS issn_ops DEFAULT FUNCTION 1 hashissn(issn); -- ISSN vs other types: -CREATE OR REPLACE FUNCTION btissncmp(issn, ean13) +CREATE FUNCTION btissncmp(issn, ean13) RETURNS int4 AS 'btint8cmp' LANGUAGE 'internal' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION btissncmp(issn, issn13) +CREATE FUNCTION btissncmp(issn, issn13) RETURNS int4 AS 'btint8cmp' LANGUAGE 'internal' @@ -2990,7 +2987,7 @@ ALTER OPERATOR FAMILY isn_ops USING hash ADD --------------------------------------------------- -- UPC: -CREATE OR REPLACE FUNCTION btupccmp(upc, upc) +CREATE FUNCTION btupccmp(upc, upc) RETURNS int4 AS 'btint8cmp' LANGUAGE 'internal' @@ -3005,7 +3002,7 @@ CREATE OPERATOR CLASS upc_ops DEFAULT OPERATOR 5 >, FUNCTION 1 btupccmp(upc, upc); -CREATE OR REPLACE FUNCTION hashupc(upc) +CREATE FUNCTION hashupc(upc) RETURNS int4 AS 'hashint8' LANGUAGE 'internal' @@ -3017,7 +3014,7 @@ CREATE OPERATOR CLASS upc_ops DEFAULT FUNCTION 1 hashupc(upc); -- UPC vs other types: -CREATE OR REPLACE FUNCTION btupccmp(upc, ean13) +CREATE FUNCTION btupccmp(upc, ean13) RETURNS int4 AS 'btint8cmp' LANGUAGE 'internal' @@ -3038,31 +3035,31 @@ ALTER OPERATOR FAMILY isn_ops USING hash ADD -- Type casts: -- --------------------------------------------------- -CREATE OR REPLACE FUNCTION isbn13(ean13) +CREATE FUNCTION isbn13(ean13) RETURNS isbn13 AS 'MODULE_PATHNAME', 'isbn_cast_from_ean13' LANGUAGE 'C' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION ismn13(ean13) +CREATE FUNCTION ismn13(ean13) RETURNS ismn13 AS 'MODULE_PATHNAME', 'ismn_cast_from_ean13' LANGUAGE 'C' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION issn13(ean13) +CREATE FUNCTION issn13(ean13) RETURNS issn13 AS 'MODULE_PATHNAME', 'issn_cast_from_ean13' LANGUAGE 'C' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION isbn(ean13) +CREATE FUNCTION isbn(ean13) RETURNS isbn AS 'MODULE_PATHNAME', 'isbn_cast_from_ean13' LANGUAGE 'C' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION ismn(ean13) +CREATE FUNCTION ismn(ean13) RETURNS ismn AS 'MODULE_PATHNAME', 'ismn_cast_from_ean13' LANGUAGE 'C' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION issn(ean13) +CREATE FUNCTION issn(ean13) RETURNS issn AS 'MODULE_PATHNAME', 'issn_cast_from_ean13' LANGUAGE 'C' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION upc(ean13) +CREATE FUNCTION upc(ean13) RETURNS upc AS 'MODULE_PATHNAME', 'upc_cast_from_ean13' LANGUAGE 'C' IMMUTABLE STRICT; @@ -3094,83 +3091,83 @@ CREATE CAST (issn13 AS issn) WITHOUT FUNCTION AS ASSIGNMENT; -- -- Validation stuff for lose types: -- -CREATE OR REPLACE FUNCTION make_valid(ean13) +CREATE FUNCTION make_valid(ean13) RETURNS ean13 AS 'MODULE_PATHNAME' LANGUAGE 'C' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION make_valid(isbn13) +CREATE FUNCTION make_valid(isbn13) RETURNS isbn13 AS 'MODULE_PATHNAME' LANGUAGE 'C' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION make_valid(ismn13) +CREATE FUNCTION make_valid(ismn13) RETURNS ismn13 AS 'MODULE_PATHNAME' LANGUAGE 'C' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION make_valid(issn13) +CREATE FUNCTION make_valid(issn13) RETURNS issn13 AS 'MODULE_PATHNAME' LANGUAGE 'C' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION make_valid(isbn) +CREATE FUNCTION make_valid(isbn) RETURNS isbn AS 'MODULE_PATHNAME' LANGUAGE 'C' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION make_valid(ismn) +CREATE FUNCTION make_valid(ismn) RETURNS ismn AS 'MODULE_PATHNAME' LANGUAGE 'C' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION make_valid(issn) +CREATE FUNCTION make_valid(issn) RETURNS issn AS 'MODULE_PATHNAME' LANGUAGE 'C' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION make_valid(upc) +CREATE FUNCTION make_valid(upc) RETURNS upc AS 'MODULE_PATHNAME' LANGUAGE 'C' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION is_valid(ean13) +CREATE FUNCTION is_valid(ean13) RETURNS boolean AS 'MODULE_PATHNAME' LANGUAGE 'C' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION is_valid(isbn13) +CREATE FUNCTION is_valid(isbn13) RETURNS boolean AS 'MODULE_PATHNAME' LANGUAGE 'C' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION is_valid(ismn13) +CREATE FUNCTION is_valid(ismn13) RETURNS boolean AS 'MODULE_PATHNAME' LANGUAGE 'C' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION is_valid(issn13) +CREATE FUNCTION is_valid(issn13) RETURNS boolean AS 'MODULE_PATHNAME' LANGUAGE 'C' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION is_valid(isbn) +CREATE FUNCTION is_valid(isbn) RETURNS boolean AS 'MODULE_PATHNAME' LANGUAGE 'C' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION is_valid(ismn) +CREATE FUNCTION is_valid(ismn) RETURNS boolean AS 'MODULE_PATHNAME' LANGUAGE 'C' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION is_valid(issn) +CREATE FUNCTION is_valid(issn) RETURNS boolean AS 'MODULE_PATHNAME' LANGUAGE 'C' IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION is_valid(upc) +CREATE FUNCTION is_valid(upc) RETURNS boolean AS 'MODULE_PATHNAME' LANGUAGE 'C' @@ -3180,7 +3177,7 @@ CREATE OR REPLACE FUNCTION is_valid(upc) -- isn_weak(boolean) - Sets the weak input mode. -- This function is intended for testing use only! -- -CREATE OR REPLACE FUNCTION isn_weak(boolean) +CREATE FUNCTION isn_weak(boolean) RETURNS boolean AS 'MODULE_PATHNAME', 'accept_weak_input' LANGUAGE 'C' @@ -3189,7 +3186,7 @@ CREATE OR REPLACE FUNCTION isn_weak(boolean) -- -- isn_weak() - Gets the weak input mode status -- -CREATE OR REPLACE FUNCTION isn_weak() +CREATE FUNCTION isn_weak() RETURNS boolean AS 'MODULE_PATHNAME', 'weak_input_status' LANGUAGE 'C' diff --git a/contrib/isn/isn--unpackaged--1.0.sql b/contrib/isn/isn--unpackaged--1.0.sql new file mode 100644 index 0000000000..6130a43e51 --- /dev/null +++ b/contrib/isn/isn--unpackaged--1.0.sql @@ -0,0 +1,461 @@ +/* contrib/isn/isn--unpackaged--1.0.sql */ + +ALTER EXTENSION isn ADD type ean13; +ALTER EXTENSION isn ADD function ean13_in(cstring); +ALTER EXTENSION isn ADD function ean13_out(ean13); +ALTER EXTENSION isn ADD type isbn13; +ALTER EXTENSION isn ADD function isbn13_in(cstring); +ALTER EXTENSION isn ADD function ean13_out(isbn13); +ALTER EXTENSION isn ADD type ismn13; +ALTER EXTENSION isn ADD function ismn13_in(cstring); +ALTER EXTENSION isn ADD function ean13_out(ismn13); +ALTER EXTENSION isn ADD type issn13; +ALTER EXTENSION isn ADD function issn13_in(cstring); +ALTER EXTENSION isn ADD function ean13_out(issn13); +ALTER EXTENSION isn ADD type isbn; +ALTER EXTENSION isn ADD function isbn_in(cstring); +ALTER EXTENSION isn ADD function isn_out(isbn); +ALTER EXTENSION isn ADD type ismn; +ALTER EXTENSION isn ADD function ismn_in(cstring); +ALTER EXTENSION isn ADD function isn_out(ismn); +ALTER EXTENSION isn ADD type issn; +ALTER EXTENSION isn ADD function issn_in(cstring); +ALTER EXTENSION isn ADD function isn_out(issn); +ALTER EXTENSION isn ADD type upc; +ALTER EXTENSION isn ADD function upc_in(cstring); +ALTER EXTENSION isn ADD function isn_out(upc); +ALTER EXTENSION isn ADD function isnlt(ean13,ean13); +ALTER EXTENSION isn ADD function isnle(ean13,ean13); +ALTER EXTENSION isn ADD function isneq(ean13,ean13); +ALTER EXTENSION isn ADD function isnge(ean13,ean13); +ALTER EXTENSION isn ADD function isngt(ean13,ean13); +ALTER EXTENSION isn ADD function isnne(ean13,ean13); +ALTER EXTENSION isn ADD function isnlt(ean13,isbn13); +ALTER EXTENSION isn ADD function isnle(ean13,isbn13); +ALTER EXTENSION isn ADD function isneq(ean13,isbn13); +ALTER EXTENSION isn ADD function isnge(ean13,isbn13); +ALTER EXTENSION isn ADD function isngt(ean13,isbn13); +ALTER EXTENSION isn ADD function isnne(ean13,isbn13); +ALTER EXTENSION isn ADD function isnlt(ean13,ismn13); +ALTER EXTENSION isn ADD function isnle(ean13,ismn13); +ALTER EXTENSION isn ADD function isneq(ean13,ismn13); +ALTER EXTENSION isn ADD function isnge(ean13,ismn13); +ALTER EXTENSION isn ADD function isngt(ean13,ismn13); +ALTER EXTENSION isn ADD function isnne(ean13,ismn13); +ALTER EXTENSION isn ADD function isnlt(ean13,issn13); +ALTER EXTENSION isn ADD function isnle(ean13,issn13); +ALTER EXTENSION isn ADD function isneq(ean13,issn13); +ALTER EXTENSION isn ADD function isnge(ean13,issn13); +ALTER EXTENSION isn ADD function isngt(ean13,issn13); +ALTER EXTENSION isn ADD function isnne(ean13,issn13); +ALTER EXTENSION isn ADD function isnlt(ean13,isbn); +ALTER EXTENSION isn ADD function isnle(ean13,isbn); +ALTER EXTENSION isn ADD function isneq(ean13,isbn); +ALTER EXTENSION isn ADD function isnge(ean13,isbn); +ALTER EXTENSION isn ADD function isngt(ean13,isbn); +ALTER EXTENSION isn ADD function isnne(ean13,isbn); +ALTER EXTENSION isn ADD function isnlt(ean13,ismn); +ALTER EXTENSION isn ADD function isnle(ean13,ismn); +ALTER EXTENSION isn ADD function isneq(ean13,ismn); +ALTER EXTENSION isn ADD function isnge(ean13,ismn); +ALTER EXTENSION isn ADD function isngt(ean13,ismn); +ALTER EXTENSION isn ADD function isnne(ean13,ismn); +ALTER EXTENSION isn ADD function isnlt(ean13,issn); +ALTER EXTENSION isn ADD function isnle(ean13,issn); +ALTER EXTENSION isn ADD function isneq(ean13,issn); +ALTER EXTENSION isn ADD function isnge(ean13,issn); +ALTER EXTENSION isn ADD function isngt(ean13,issn); +ALTER EXTENSION isn ADD function isnne(ean13,issn); +ALTER EXTENSION isn ADD function isnlt(ean13,upc); +ALTER EXTENSION isn ADD function isnle(ean13,upc); +ALTER EXTENSION isn ADD function isneq(ean13,upc); +ALTER EXTENSION isn ADD function isnge(ean13,upc); +ALTER EXTENSION isn ADD function isngt(ean13,upc); +ALTER EXTENSION isn ADD function isnne(ean13,upc); +ALTER EXTENSION isn ADD function isnlt(isbn13,isbn13); +ALTER EXTENSION isn ADD function isnle(isbn13,isbn13); +ALTER EXTENSION isn ADD function isneq(isbn13,isbn13); +ALTER EXTENSION isn ADD function isnge(isbn13,isbn13); +ALTER EXTENSION isn ADD function isngt(isbn13,isbn13); +ALTER EXTENSION isn ADD function isnne(isbn13,isbn13); +ALTER EXTENSION isn ADD function isnlt(isbn13,isbn); +ALTER EXTENSION isn ADD function isnle(isbn13,isbn); +ALTER EXTENSION isn ADD function isneq(isbn13,isbn); +ALTER EXTENSION isn ADD function isnge(isbn13,isbn); +ALTER EXTENSION isn ADD function isngt(isbn13,isbn); +ALTER EXTENSION isn ADD function isnne(isbn13,isbn); +ALTER EXTENSION isn ADD function isnlt(isbn13,ean13); +ALTER EXTENSION isn ADD function isnle(isbn13,ean13); +ALTER EXTENSION isn ADD function isneq(isbn13,ean13); +ALTER EXTENSION isn ADD function isnge(isbn13,ean13); +ALTER EXTENSION isn ADD function isngt(isbn13,ean13); +ALTER EXTENSION isn ADD function isnne(isbn13,ean13); +ALTER EXTENSION isn ADD function isnlt(isbn,isbn); +ALTER EXTENSION isn ADD function isnle(isbn,isbn); +ALTER EXTENSION isn ADD function isneq(isbn,isbn); +ALTER EXTENSION isn ADD function isnge(isbn,isbn); +ALTER EXTENSION isn ADD function isngt(isbn,isbn); +ALTER EXTENSION isn ADD function isnne(isbn,isbn); +ALTER EXTENSION isn ADD function isnlt(isbn,isbn13); +ALTER EXTENSION isn ADD function isnle(isbn,isbn13); +ALTER EXTENSION isn ADD function isneq(isbn,isbn13); +ALTER EXTENSION isn ADD function isnge(isbn,isbn13); +ALTER EXTENSION isn ADD function isngt(isbn,isbn13); +ALTER EXTENSION isn ADD function isnne(isbn,isbn13); +ALTER EXTENSION isn ADD function isnlt(isbn,ean13); +ALTER EXTENSION isn ADD function isnle(isbn,ean13); +ALTER EXTENSION isn ADD function isneq(isbn,ean13); +ALTER EXTENSION isn ADD function isnge(isbn,ean13); +ALTER EXTENSION isn ADD function isngt(isbn,ean13); +ALTER EXTENSION isn ADD function isnne(isbn,ean13); +ALTER EXTENSION isn ADD function isnlt(ismn13,ismn13); +ALTER EXTENSION isn ADD function isnle(ismn13,ismn13); +ALTER EXTENSION isn ADD function isneq(ismn13,ismn13); +ALTER EXTENSION isn ADD function isnge(ismn13,ismn13); +ALTER EXTENSION isn ADD function isngt(ismn13,ismn13); +ALTER EXTENSION isn ADD function isnne(ismn13,ismn13); +ALTER EXTENSION isn ADD function isnlt(ismn13,ismn); +ALTER EXTENSION isn ADD function isnle(ismn13,ismn); +ALTER EXTENSION isn ADD function isneq(ismn13,ismn); +ALTER EXTENSION isn ADD function isnge(ismn13,ismn); +ALTER EXTENSION isn ADD function isngt(ismn13,ismn); +ALTER EXTENSION isn ADD function isnne(ismn13,ismn); +ALTER EXTENSION isn ADD function isnlt(ismn13,ean13); +ALTER EXTENSION isn ADD function isnle(ismn13,ean13); +ALTER EXTENSION isn ADD function isneq(ismn13,ean13); +ALTER EXTENSION isn ADD function isnge(ismn13,ean13); +ALTER EXTENSION isn ADD function isngt(ismn13,ean13); +ALTER EXTENSION isn ADD function isnne(ismn13,ean13); +ALTER EXTENSION isn ADD function isnlt(ismn,ismn); +ALTER EXTENSION isn ADD function isnle(ismn,ismn); +ALTER EXTENSION isn ADD function isneq(ismn,ismn); +ALTER EXTENSION isn ADD function isnge(ismn,ismn); +ALTER EXTENSION isn ADD function isngt(ismn,ismn); +ALTER EXTENSION isn ADD function isnne(ismn,ismn); +ALTER EXTENSION isn ADD function isnlt(ismn,ismn13); +ALTER EXTENSION isn ADD function isnle(ismn,ismn13); +ALTER EXTENSION isn ADD function isneq(ismn,ismn13); +ALTER EXTENSION isn ADD function isnge(ismn,ismn13); +ALTER EXTENSION isn ADD function isngt(ismn,ismn13); +ALTER EXTENSION isn ADD function isnne(ismn,ismn13); +ALTER EXTENSION isn ADD function isnlt(ismn,ean13); +ALTER EXTENSION isn ADD function isnle(ismn,ean13); +ALTER EXTENSION isn ADD function isneq(ismn,ean13); +ALTER EXTENSION isn ADD function isnge(ismn,ean13); +ALTER EXTENSION isn ADD function isngt(ismn,ean13); +ALTER EXTENSION isn ADD function isnne(ismn,ean13); +ALTER EXTENSION isn ADD function isnlt(issn13,issn13); +ALTER EXTENSION isn ADD function isnle(issn13,issn13); +ALTER EXTENSION isn ADD function isneq(issn13,issn13); +ALTER EXTENSION isn ADD function isnge(issn13,issn13); +ALTER EXTENSION isn ADD function isngt(issn13,issn13); +ALTER EXTENSION isn ADD function isnne(issn13,issn13); +ALTER EXTENSION isn ADD function isnlt(issn13,issn); +ALTER EXTENSION isn ADD function isnle(issn13,issn); +ALTER EXTENSION isn ADD function isneq(issn13,issn); +ALTER EXTENSION isn ADD function isnge(issn13,issn); +ALTER EXTENSION isn ADD function isngt(issn13,issn); +ALTER EXTENSION isn ADD function isnne(issn13,issn); +ALTER EXTENSION isn ADD function isnlt(issn13,ean13); +ALTER EXTENSION isn ADD function isnle(issn13,ean13); +ALTER EXTENSION isn ADD function isneq(issn13,ean13); +ALTER EXTENSION isn ADD function isnge(issn13,ean13); +ALTER EXTENSION isn ADD function isngt(issn13,ean13); +ALTER EXTENSION isn ADD function isnne(issn13,ean13); +ALTER EXTENSION isn ADD function isnlt(issn,issn); +ALTER EXTENSION isn ADD function isnle(issn,issn); +ALTER EXTENSION isn ADD function isneq(issn,issn); +ALTER EXTENSION isn ADD function isnge(issn,issn); +ALTER EXTENSION isn ADD function isngt(issn,issn); +ALTER EXTENSION isn ADD function isnne(issn,issn); +ALTER EXTENSION isn ADD function isnlt(issn,issn13); +ALTER EXTENSION isn ADD function isnle(issn,issn13); +ALTER EXTENSION isn ADD function isneq(issn,issn13); +ALTER EXTENSION isn ADD function isnge(issn,issn13); +ALTER EXTENSION isn ADD function isngt(issn,issn13); +ALTER EXTENSION isn ADD function isnne(issn,issn13); +ALTER EXTENSION isn ADD function isnlt(issn,ean13); +ALTER EXTENSION isn ADD function isnle(issn,ean13); +ALTER EXTENSION isn ADD function isneq(issn,ean13); +ALTER EXTENSION isn ADD function isnge(issn,ean13); +ALTER EXTENSION isn ADD function isngt(issn,ean13); +ALTER EXTENSION isn ADD function isnne(issn,ean13); +ALTER EXTENSION isn ADD function isnlt(upc,upc); +ALTER EXTENSION isn ADD function isnle(upc,upc); +ALTER EXTENSION isn ADD function isneq(upc,upc); +ALTER EXTENSION isn ADD function isnge(upc,upc); +ALTER EXTENSION isn ADD function isngt(upc,upc); +ALTER EXTENSION isn ADD function isnne(upc,upc); +ALTER EXTENSION isn ADD function isnlt(upc,ean13); +ALTER EXTENSION isn ADD function isnle(upc,ean13); +ALTER EXTENSION isn ADD function isneq(upc,ean13); +ALTER EXTENSION isn ADD function isnge(upc,ean13); +ALTER EXTENSION isn ADD function isngt(upc,ean13); +ALTER EXTENSION isn ADD function isnne(upc,ean13); +ALTER EXTENSION isn ADD operator >(ean13,ean13); +ALTER EXTENSION isn ADD operator >=(ean13,ean13); +ALTER EXTENSION isn ADD operator <(ean13,ean13); +ALTER EXTENSION isn ADD operator <=(ean13,ean13); +ALTER EXTENSION isn ADD operator <>(ean13,ean13); +ALTER EXTENSION isn ADD operator =(ean13,ean13); +ALTER EXTENSION isn ADD operator >(isbn13,ean13); +ALTER EXTENSION isn ADD operator >=(ean13,isbn13); +ALTER EXTENSION isn ADD operator <(ean13,isbn13); +ALTER EXTENSION isn ADD operator >=(isbn13,ean13); +ALTER EXTENSION isn ADD operator >(ean13,isbn13); +ALTER EXTENSION isn ADD operator <=(ean13,isbn13); +ALTER EXTENSION isn ADD operator =(isbn13,ean13); +ALTER EXTENSION isn ADD operator <>(ean13,isbn13); +ALTER EXTENSION isn ADD operator =(ean13,isbn13); +ALTER EXTENSION isn ADD operator <=(isbn13,ean13); +ALTER EXTENSION isn ADD operator <(isbn13,ean13); +ALTER EXTENSION isn ADD operator <>(isbn13,ean13); +ALTER EXTENSION isn ADD operator >(ismn13,ean13); +ALTER EXTENSION isn ADD operator >=(ean13,ismn13); +ALTER EXTENSION isn ADD operator <(ean13,ismn13); +ALTER EXTENSION isn ADD operator >=(ismn13,ean13); +ALTER EXTENSION isn ADD operator >(ean13,ismn13); +ALTER EXTENSION isn ADD operator <=(ean13,ismn13); +ALTER EXTENSION isn ADD operator =(ismn13,ean13); +ALTER EXTENSION isn ADD operator <>(ean13,ismn13); +ALTER EXTENSION isn ADD operator =(ean13,ismn13); +ALTER EXTENSION isn ADD operator <=(ismn13,ean13); +ALTER EXTENSION isn ADD operator <(ismn13,ean13); +ALTER EXTENSION isn ADD operator <>(ismn13,ean13); +ALTER EXTENSION isn ADD operator >(issn13,ean13); +ALTER EXTENSION isn ADD operator >=(ean13,issn13); +ALTER EXTENSION isn ADD operator <(ean13,issn13); +ALTER EXTENSION isn ADD operator >=(issn13,ean13); +ALTER EXTENSION isn ADD operator >(ean13,issn13); +ALTER EXTENSION isn ADD operator <=(ean13,issn13); +ALTER EXTENSION isn ADD operator =(issn13,ean13); +ALTER EXTENSION isn ADD operator <>(ean13,issn13); +ALTER EXTENSION isn ADD operator =(ean13,issn13); +ALTER EXTENSION isn ADD operator <=(issn13,ean13); +ALTER EXTENSION isn ADD operator <(issn13,ean13); +ALTER EXTENSION isn ADD operator <>(issn13,ean13); +ALTER EXTENSION isn ADD operator >(isbn,ean13); +ALTER EXTENSION isn ADD operator >=(ean13,isbn); +ALTER EXTENSION isn ADD operator <(ean13,isbn); +ALTER EXTENSION isn ADD operator >=(isbn,ean13); +ALTER EXTENSION isn ADD operator >(ean13,isbn); +ALTER EXTENSION isn ADD operator <=(ean13,isbn); +ALTER EXTENSION isn ADD operator =(isbn,ean13); +ALTER EXTENSION isn ADD operator <>(ean13,isbn); +ALTER EXTENSION isn ADD operator =(ean13,isbn); +ALTER EXTENSION isn ADD operator <=(isbn,ean13); +ALTER EXTENSION isn ADD operator <(isbn,ean13); +ALTER EXTENSION isn ADD operator <>(isbn,ean13); +ALTER EXTENSION isn ADD operator >(ismn,ean13); +ALTER EXTENSION isn ADD operator >=(ean13,ismn); +ALTER EXTENSION isn ADD operator <(ean13,ismn); +ALTER EXTENSION isn ADD operator >=(ismn,ean13); +ALTER EXTENSION isn ADD operator >(ean13,ismn); +ALTER EXTENSION isn ADD operator <=(ean13,ismn); +ALTER EXTENSION isn ADD operator =(ismn,ean13); +ALTER EXTENSION isn ADD operator <>(ean13,ismn); +ALTER EXTENSION isn ADD operator =(ean13,ismn); +ALTER EXTENSION isn ADD operator <=(ismn,ean13); +ALTER EXTENSION isn ADD operator <(ismn,ean13); +ALTER EXTENSION isn ADD operator <>(ismn,ean13); +ALTER EXTENSION isn ADD operator >(issn,ean13); +ALTER EXTENSION isn ADD operator >=(ean13,issn); +ALTER EXTENSION isn ADD operator <(ean13,issn); +ALTER EXTENSION isn ADD operator >=(issn,ean13); +ALTER EXTENSION isn ADD operator >(ean13,issn); +ALTER EXTENSION isn ADD operator <=(ean13,issn); +ALTER EXTENSION isn ADD operator =(issn,ean13); +ALTER EXTENSION isn ADD operator <>(ean13,issn); +ALTER EXTENSION isn ADD operator =(ean13,issn); +ALTER EXTENSION isn ADD operator <=(issn,ean13); +ALTER EXTENSION isn ADD operator <(issn,ean13); +ALTER EXTENSION isn ADD operator <>(issn,ean13); +ALTER EXTENSION isn ADD operator >(upc,ean13); +ALTER EXTENSION isn ADD operator >=(ean13,upc); +ALTER EXTENSION isn ADD operator <(ean13,upc); +ALTER EXTENSION isn ADD operator >=(upc,ean13); +ALTER EXTENSION isn ADD operator >(ean13,upc); +ALTER EXTENSION isn ADD operator <=(ean13,upc); +ALTER EXTENSION isn ADD operator =(upc,ean13); +ALTER EXTENSION isn ADD operator <>(ean13,upc); +ALTER EXTENSION isn ADD operator =(ean13,upc); +ALTER EXTENSION isn ADD operator <=(upc,ean13); +ALTER EXTENSION isn ADD operator <(upc,ean13); +ALTER EXTENSION isn ADD operator <>(upc,ean13); +ALTER EXTENSION isn ADD operator >(isbn13,isbn13); +ALTER EXTENSION isn ADD operator >=(isbn13,isbn13); +ALTER EXTENSION isn ADD operator <(isbn13,isbn13); +ALTER EXTENSION isn ADD operator <=(isbn13,isbn13); +ALTER EXTENSION isn ADD operator <>(isbn13,isbn13); +ALTER EXTENSION isn ADD operator =(isbn13,isbn13); +ALTER EXTENSION isn ADD operator >(isbn,isbn13); +ALTER EXTENSION isn ADD operator >=(isbn13,isbn); +ALTER EXTENSION isn ADD operator <(isbn13,isbn); +ALTER EXTENSION isn ADD operator >=(isbn,isbn13); +ALTER EXTENSION isn ADD operator >(isbn13,isbn); +ALTER EXTENSION isn ADD operator <=(isbn13,isbn); +ALTER EXTENSION isn ADD operator =(isbn,isbn13); +ALTER EXTENSION isn ADD operator <>(isbn13,isbn); +ALTER EXTENSION isn ADD operator =(isbn13,isbn); +ALTER EXTENSION isn ADD operator <=(isbn,isbn13); +ALTER EXTENSION isn ADD operator <(isbn,isbn13); +ALTER EXTENSION isn ADD operator <>(isbn,isbn13); +ALTER EXTENSION isn ADD operator >(isbn,isbn); +ALTER EXTENSION isn ADD operator >=(isbn,isbn); +ALTER EXTENSION isn ADD operator <(isbn,isbn); +ALTER EXTENSION isn ADD operator <=(isbn,isbn); +ALTER EXTENSION isn ADD operator <>(isbn,isbn); +ALTER EXTENSION isn ADD operator =(isbn,isbn); +ALTER EXTENSION isn ADD operator >(ismn13,ismn13); +ALTER EXTENSION isn ADD operator >=(ismn13,ismn13); +ALTER EXTENSION isn ADD operator <(ismn13,ismn13); +ALTER EXTENSION isn ADD operator <=(ismn13,ismn13); +ALTER EXTENSION isn ADD operator <>(ismn13,ismn13); +ALTER EXTENSION isn ADD operator =(ismn13,ismn13); +ALTER EXTENSION isn ADD operator >(ismn,ismn13); +ALTER EXTENSION isn ADD operator >=(ismn13,ismn); +ALTER EXTENSION isn ADD operator <(ismn13,ismn); +ALTER EXTENSION isn ADD operator >=(ismn,ismn13); +ALTER EXTENSION isn ADD operator >(ismn13,ismn); +ALTER EXTENSION isn ADD operator <=(ismn13,ismn); +ALTER EXTENSION isn ADD operator =(ismn,ismn13); +ALTER EXTENSION isn ADD operator <>(ismn13,ismn); +ALTER EXTENSION isn ADD operator =(ismn13,ismn); +ALTER EXTENSION isn ADD operator <=(ismn,ismn13); +ALTER EXTENSION isn ADD operator <(ismn,ismn13); +ALTER EXTENSION isn ADD operator <>(ismn,ismn13); +ALTER EXTENSION isn ADD operator >(ismn,ismn); +ALTER EXTENSION isn ADD operator >=(ismn,ismn); +ALTER EXTENSION isn ADD operator <(ismn,ismn); +ALTER EXTENSION isn ADD operator <=(ismn,ismn); +ALTER EXTENSION isn ADD operator <>(ismn,ismn); +ALTER EXTENSION isn ADD operator =(ismn,ismn); +ALTER EXTENSION isn ADD operator >(issn13,issn13); +ALTER EXTENSION isn ADD operator >=(issn13,issn13); +ALTER EXTENSION isn ADD operator <(issn13,issn13); +ALTER EXTENSION isn ADD operator <=(issn13,issn13); +ALTER EXTENSION isn ADD operator <>(issn13,issn13); +ALTER EXTENSION isn ADD operator =(issn13,issn13); +ALTER EXTENSION isn ADD operator >(issn,issn13); +ALTER EXTENSION isn ADD operator >=(issn13,issn); +ALTER EXTENSION isn ADD operator <(issn13,issn); +ALTER EXTENSION isn ADD operator >=(issn,issn13); +ALTER EXTENSION isn ADD operator >(issn13,issn); +ALTER EXTENSION isn ADD operator <=(issn13,issn); +ALTER EXTENSION isn ADD operator =(issn,issn13); +ALTER EXTENSION isn ADD operator <>(issn13,issn); +ALTER EXTENSION isn ADD operator =(issn13,issn); +ALTER EXTENSION isn ADD operator <=(issn,issn13); +ALTER EXTENSION isn ADD operator <(issn,issn13); +ALTER EXTENSION isn ADD operator <>(issn,issn13); +ALTER EXTENSION isn ADD operator >(issn,issn); +ALTER EXTENSION isn ADD operator >=(issn,issn); +ALTER EXTENSION isn ADD operator <(issn,issn); +ALTER EXTENSION isn ADD operator <=(issn,issn); +ALTER EXTENSION isn ADD operator <>(issn,issn); +ALTER EXTENSION isn ADD operator =(issn,issn); +ALTER EXTENSION isn ADD operator >(upc,upc); +ALTER EXTENSION isn ADD operator >=(upc,upc); +ALTER EXTENSION isn ADD operator <(upc,upc); +ALTER EXTENSION isn ADD operator <=(upc,upc); +ALTER EXTENSION isn ADD operator <>(upc,upc); +ALTER EXTENSION isn ADD operator =(upc,upc); +ALTER EXTENSION isn ADD operator family isn_ops using btree; +ALTER EXTENSION isn ADD operator family isn_ops using hash; +ALTER EXTENSION isn ADD function btean13cmp(ean13,ean13); +ALTER EXTENSION isn ADD operator class ean13_ops using btree; +ALTER EXTENSION isn ADD function hashean13(ean13); +ALTER EXTENSION isn ADD operator class ean13_ops using hash; +ALTER EXTENSION isn ADD function btean13cmp(ean13,isbn13); +ALTER EXTENSION isn ADD function btean13cmp(ean13,ismn13); +ALTER EXTENSION isn ADD function btean13cmp(ean13,issn13); +ALTER EXTENSION isn ADD function btean13cmp(ean13,isbn); +ALTER EXTENSION isn ADD function btean13cmp(ean13,ismn); +ALTER EXTENSION isn ADD function btean13cmp(ean13,issn); +ALTER EXTENSION isn ADD function btean13cmp(ean13,upc); +ALTER EXTENSION isn ADD function btisbn13cmp(isbn13,isbn13); +ALTER EXTENSION isn ADD operator class isbn13_ops using btree; +ALTER EXTENSION isn ADD function hashisbn13(isbn13); +ALTER EXTENSION isn ADD operator class isbn13_ops using hash; +ALTER EXTENSION isn ADD function btisbn13cmp(isbn13,ean13); +ALTER EXTENSION isn ADD function btisbn13cmp(isbn13,isbn); +ALTER EXTENSION isn ADD function btisbncmp(isbn,isbn); +ALTER EXTENSION isn ADD operator class isbn_ops using btree; +ALTER EXTENSION isn ADD function hashisbn(isbn); +ALTER EXTENSION isn ADD operator class isbn_ops using hash; +ALTER EXTENSION isn ADD function btisbncmp(isbn,ean13); +ALTER EXTENSION isn ADD function btisbncmp(isbn,isbn13); +ALTER EXTENSION isn ADD function btismn13cmp(ismn13,ismn13); +ALTER EXTENSION isn ADD operator class ismn13_ops using btree; +ALTER EXTENSION isn ADD function hashismn13(ismn13); +ALTER EXTENSION isn ADD operator class ismn13_ops using hash; +ALTER EXTENSION isn ADD function btismn13cmp(ismn13,ean13); +ALTER EXTENSION isn ADD function btismn13cmp(ismn13,ismn); +ALTER EXTENSION isn ADD function btismncmp(ismn,ismn); +ALTER EXTENSION isn ADD operator class ismn_ops using btree; +ALTER EXTENSION isn ADD function hashismn(ismn); +ALTER EXTENSION isn ADD operator class ismn_ops using hash; +ALTER EXTENSION isn ADD function btismncmp(ismn,ean13); +ALTER EXTENSION isn ADD function btismncmp(ismn,ismn13); +ALTER EXTENSION isn ADD function btissn13cmp(issn13,issn13); +ALTER EXTENSION isn ADD operator class issn13_ops using btree; +ALTER EXTENSION isn ADD function hashissn13(issn13); +ALTER EXTENSION isn ADD operator class issn13_ops using hash; +ALTER EXTENSION isn ADD function btissn13cmp(issn13,ean13); +ALTER EXTENSION isn ADD function btissn13cmp(issn13,issn); +ALTER EXTENSION isn ADD function btissncmp(issn,issn); +ALTER EXTENSION isn ADD operator class issn_ops using btree; +ALTER EXTENSION isn ADD function hashissn(issn); +ALTER EXTENSION isn ADD operator class issn_ops using hash; +ALTER EXTENSION isn ADD function btissncmp(issn,ean13); +ALTER EXTENSION isn ADD function btissncmp(issn,issn13); +ALTER EXTENSION isn ADD function btupccmp(upc,upc); +ALTER EXTENSION isn ADD operator class upc_ops using btree; +ALTER EXTENSION isn ADD function hashupc(upc); +ALTER EXTENSION isn ADD operator class upc_ops using hash; +ALTER EXTENSION isn ADD function btupccmp(upc,ean13); +ALTER EXTENSION isn ADD function isbn13(ean13); +ALTER EXTENSION isn ADD function ismn13(ean13); +ALTER EXTENSION isn ADD function issn13(ean13); +ALTER EXTENSION isn ADD function isbn(ean13); +ALTER EXTENSION isn ADD function ismn(ean13); +ALTER EXTENSION isn ADD function issn(ean13); +ALTER EXTENSION isn ADD function upc(ean13); +ALTER EXTENSION isn ADD cast (ean13 as isbn13); +ALTER EXTENSION isn ADD cast (ean13 as isbn); +ALTER EXTENSION isn ADD cast (ean13 as ismn13); +ALTER EXTENSION isn ADD cast (ean13 as ismn); +ALTER EXTENSION isn ADD cast (ean13 as issn13); +ALTER EXTENSION isn ADD cast (ean13 as issn); +ALTER EXTENSION isn ADD cast (ean13 as upc); +ALTER EXTENSION isn ADD cast (isbn13 as ean13); +ALTER EXTENSION isn ADD cast (isbn as ean13); +ALTER EXTENSION isn ADD cast (ismn13 as ean13); +ALTER EXTENSION isn ADD cast (ismn as ean13); +ALTER EXTENSION isn ADD cast (issn13 as ean13); +ALTER EXTENSION isn ADD cast (issn as ean13); +ALTER EXTENSION isn ADD cast (upc as ean13); +ALTER EXTENSION isn ADD cast (isbn as isbn13); +ALTER EXTENSION isn ADD cast (isbn13 as isbn); +ALTER EXTENSION isn ADD cast (ismn as ismn13); +ALTER EXTENSION isn ADD cast (ismn13 as ismn); +ALTER EXTENSION isn ADD cast (issn as issn13); +ALTER EXTENSION isn ADD cast (issn13 as issn); +ALTER EXTENSION isn ADD function make_valid(ean13); +ALTER EXTENSION isn ADD function make_valid(isbn13); +ALTER EXTENSION isn ADD function make_valid(ismn13); +ALTER EXTENSION isn ADD function make_valid(issn13); +ALTER EXTENSION isn ADD function make_valid(isbn); +ALTER EXTENSION isn ADD function make_valid(ismn); +ALTER EXTENSION isn ADD function make_valid(issn); +ALTER EXTENSION isn ADD function make_valid(upc); +ALTER EXTENSION isn ADD function is_valid(ean13); +ALTER EXTENSION isn ADD function is_valid(isbn13); +ALTER EXTENSION isn ADD function is_valid(ismn13); +ALTER EXTENSION isn ADD function is_valid(issn13); +ALTER EXTENSION isn ADD function is_valid(isbn); +ALTER EXTENSION isn ADD function is_valid(ismn); +ALTER EXTENSION isn ADD function is_valid(issn); +ALTER EXTENSION isn ADD function is_valid(upc); +ALTER EXTENSION isn ADD function isn_weak(boolean); +ALTER EXTENSION isn ADD function isn_weak(); diff --git a/contrib/isn/isn.control b/contrib/isn/isn.control new file mode 100644 index 0000000000..cf0b2ebe38 --- /dev/null +++ b/contrib/isn/isn.control @@ -0,0 +1,5 @@ +# isn extension +comment = 'data types for international product numbering standards' +default_version = '1.0' +module_pathname = '$libdir/isn' +relocatable = true diff --git a/contrib/isn/uninstall_isn.sql b/contrib/isn/uninstall_isn.sql deleted file mode 100644 index bf866b4748..0000000000 --- a/contrib/isn/uninstall_isn.sql +++ /dev/null @@ -1,24 +0,0 @@ -/* contrib/isn/uninstall_isn.sql */ - --- Adjust this setting to control where the objects get dropped. -SET search_path = public; - --- Drop the operator families (which don't depend on the types) -DROP OPERATOR FAMILY isn_ops USING btree CASCADE; -DROP OPERATOR FAMILY isn_ops USING hash CASCADE; - --- --- Drop the actual types (in cascade): --- -DROP TYPE ean13 CASCADE; -DROP TYPE isbn13 CASCADE; -DROP TYPE ismn13 CASCADE; -DROP TYPE issn13 CASCADE; -DROP TYPE isbn CASCADE; -DROP TYPE ismn CASCADE; -DROP TYPE issn CASCADE; -DROP TYPE upc CASCADE; - --- and clean up a couple miscellaneous functions -DROP FUNCTION isn_weak(); -DROP FUNCTION isn_weak(boolean); diff --git a/contrib/lo/.gitignore b/contrib/lo/.gitignore deleted file mode 100644 index 979347bd00..0000000000 --- a/contrib/lo/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/lo.sql diff --git a/contrib/lo/Makefile b/contrib/lo/Makefile index 43c01f57c0..66b337c17a 100644 --- a/contrib/lo/Makefile +++ b/contrib/lo/Makefile @@ -1,8 +1,9 @@ # contrib/lo/Makefile MODULES = lo -DATA_built = lo.sql -DATA = uninstall_lo.sql + +EXTENSION = lo +DATA = lo--1.0.sql lo--unpackaged--1.0.sql ifdef USE_PGXS PG_CONFIG = pg_config diff --git a/contrib/lo/lo.sql.in b/contrib/lo/lo--1.0.sql index 8c7afbe5e3..4b9a7dee32 100644 --- a/contrib/lo/lo.sql.in +++ b/contrib/lo/lo--1.0.sql @@ -1,7 +1,4 @@ -/* contrib/lo/lo.sql.in */ - --- Adjust this setting to control where the objects get created. -SET search_path = public; +/* contrib/lo/lo--1.0.sql */ -- -- Create the data type ... now just a domain over OID @@ -15,11 +12,11 @@ CREATE DOMAIN lo AS pg_catalog.oid; -- The other functions that formerly existed are not needed because -- the implicit casts between a domain and its underlying type handle them. -- -CREATE OR REPLACE FUNCTION lo_oid(lo) RETURNS pg_catalog.oid AS +CREATE FUNCTION lo_oid(lo) RETURNS pg_catalog.oid AS 'SELECT $1::pg_catalog.oid' LANGUAGE SQL STRICT IMMUTABLE; -- This is used in triggers -CREATE OR REPLACE FUNCTION lo_manage() +CREATE FUNCTION lo_manage() RETURNS pg_catalog.trigger AS 'MODULE_PATHNAME' LANGUAGE C; diff --git a/contrib/lo/lo--unpackaged--1.0.sql b/contrib/lo/lo--unpackaged--1.0.sql new file mode 100644 index 0000000000..54de61686e --- /dev/null +++ b/contrib/lo/lo--unpackaged--1.0.sql @@ -0,0 +1,5 @@ +/* contrib/lo/lo--unpackaged--1.0.sql */ + +ALTER EXTENSION lo ADD domain lo; +ALTER EXTENSION lo ADD function lo_oid(lo); +ALTER EXTENSION lo ADD function lo_manage(); diff --git a/contrib/lo/lo.control b/contrib/lo/lo.control new file mode 100644 index 0000000000..849dfb5803 --- /dev/null +++ b/contrib/lo/lo.control @@ -0,0 +1,5 @@ +# lo extension +comment = 'Large Object maintenance' +default_version = '1.0' +module_pathname = '$libdir/lo' +relocatable = true diff --git a/contrib/lo/uninstall_lo.sql b/contrib/lo/uninstall_lo.sql deleted file mode 100644 index 77deb1d550..0000000000 --- a/contrib/lo/uninstall_lo.sql +++ /dev/null @@ -1,17 +0,0 @@ -/* contrib/lo/uninstall_lo.sql */ - --- Adjust this setting to control where the objects get dropped. -SET search_path = public; - --- --- This removes the LO type --- It's used just for development --- - --- drop the type and associated functions -DROP TYPE lo CASCADE; - --- the trigger function has no dependency on the type, so drop separately -DROP FUNCTION lo_manage(); - --- the lo stuff is now removed from the system diff --git a/contrib/ltree/.gitignore b/contrib/ltree/.gitignore index 49883e82a3..19b6c5ba42 100644 --- a/contrib/ltree/.gitignore +++ b/contrib/ltree/.gitignore @@ -1,3 +1,2 @@ -/ltree.sql # Generated subdirectories /results/ diff --git a/contrib/ltree/Makefile b/contrib/ltree/Makefile index bad3cbfe85..65d42f875f 100644 --- a/contrib/ltree/Makefile +++ b/contrib/ltree/Makefile @@ -1,11 +1,13 @@ # contrib/ltree/Makefile -PG_CPPFLAGS = -DLOWER_NODE MODULE_big = ltree OBJS = ltree_io.o ltree_op.o lquery_op.o _ltree_op.o crc32.o \ ltxtquery_io.o ltxtquery_op.o ltree_gist.o _ltree_gist.o -DATA_built = ltree.sql -DATA = uninstall_ltree.sql +PG_CPPFLAGS = -DLOWER_NODE + +EXTENSION = ltree +DATA = ltree--1.0.sql ltree--unpackaged--1.0.sql + REGRESS = ltree ifdef USE_PGXS diff --git a/contrib/ltree/expected/ltree.out b/contrib/ltree/expected/ltree.out index 7f61e569cf..da6e39a785 100644 --- a/contrib/ltree/expected/ltree.out +++ b/contrib/ltree/expected/ltree.out @@ -1,10 +1,4 @@ --- --- first, define the datatype. Turn off echoing so that expected file --- does not depend on contents of ltree.sql. --- -SET client_min_messages = warning; -\set ECHO none -RESET client_min_messages; +CREATE EXTENSION ltree; SELECT ''::ltree; ltree ------- diff --git a/contrib/ltree/lquery_op.c b/contrib/ltree/lquery_op.c index fe9ecb118a..da1086a8eb 100644 --- a/contrib/ltree/lquery_op.c +++ b/contrib/ltree/lquery_op.c @@ -7,6 +7,7 @@ #include <ctype.h> +#include "catalog/pg_collation.h" #include "utils/array.h" #include "utils/formatting.h" #include "ltree.h" @@ -90,8 +91,8 @@ bool int ltree_strncasecmp(const char *a, const char *b, size_t s) { - char *al = str_tolower(a, s); - char *bl = str_tolower(b, s); + char *al = str_tolower(a, s, DEFAULT_COLLATION_OID); + char *bl = str_tolower(b, s, DEFAULT_COLLATION_OID); int res; res = strncmp(al, bl, s); diff --git a/contrib/ltree/ltree.sql.in b/contrib/ltree/ltree--1.0.sql index 1b985a7a99..32969538a0 100644 --- a/contrib/ltree/ltree.sql.in +++ b/contrib/ltree/ltree--1.0.sql @@ -1,14 +1,11 @@ -/* contrib/ltree/ltree.sql.in */ +/* contrib/ltree/ltree--1.0.sql */ --- Adjust this setting to control where the objects get created. -SET search_path = public; - -CREATE OR REPLACE FUNCTION ltree_in(cstring) +CREATE FUNCTION ltree_in(cstring) RETURNS ltree AS 'MODULE_PATHNAME' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION ltree_out(ltree) +CREATE FUNCTION ltree_out(ltree) RETURNS cstring AS 'MODULE_PATHNAME' LANGUAGE C STRICT; @@ -22,37 +19,37 @@ CREATE TYPE ltree ( --Compare function for ltree -CREATE OR REPLACE FUNCTION ltree_cmp(ltree,ltree) +CREATE FUNCTION ltree_cmp(ltree,ltree) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION ltree_lt(ltree,ltree) +CREATE FUNCTION ltree_lt(ltree,ltree) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION ltree_le(ltree,ltree) +CREATE FUNCTION ltree_le(ltree,ltree) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION ltree_eq(ltree,ltree) +CREATE FUNCTION ltree_eq(ltree,ltree) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION ltree_ge(ltree,ltree) +CREATE FUNCTION ltree_ge(ltree,ltree) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION ltree_gt(ltree,ltree) +CREATE FUNCTION ltree_gt(ltree,ltree) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION ltree_ne(ltree,ltree) +CREATE FUNCTION ltree_ne(ltree,ltree) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -122,112 +119,112 @@ CREATE OPERATOR <> ( --util functions -CREATE OR REPLACE FUNCTION subltree(ltree,int4,int4) +CREATE FUNCTION subltree(ltree,int4,int4) RETURNS ltree AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION subpath(ltree,int4,int4) +CREATE FUNCTION subpath(ltree,int4,int4) RETURNS ltree AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION subpath(ltree,int4) +CREATE FUNCTION subpath(ltree,int4) RETURNS ltree AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION index(ltree,ltree) +CREATE FUNCTION index(ltree,ltree) RETURNS int4 AS 'MODULE_PATHNAME', 'ltree_index' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION index(ltree,ltree,int4) +CREATE FUNCTION index(ltree,ltree,int4) RETURNS int4 AS 'MODULE_PATHNAME', 'ltree_index' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION nlevel(ltree) +CREATE FUNCTION nlevel(ltree) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION ltree2text(ltree) +CREATE FUNCTION ltree2text(ltree) RETURNS text AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION text2ltree(text) +CREATE FUNCTION text2ltree(text) RETURNS ltree AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION lca(_ltree) +CREATE FUNCTION lca(_ltree) RETURNS ltree AS 'MODULE_PATHNAME','_lca' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION lca(ltree,ltree) +CREATE FUNCTION lca(ltree,ltree) RETURNS ltree AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION lca(ltree,ltree,ltree) +CREATE FUNCTION lca(ltree,ltree,ltree) RETURNS ltree AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION lca(ltree,ltree,ltree,ltree) +CREATE FUNCTION lca(ltree,ltree,ltree,ltree) RETURNS ltree AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION lca(ltree,ltree,ltree,ltree,ltree) +CREATE FUNCTION lca(ltree,ltree,ltree,ltree,ltree) RETURNS ltree AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION lca(ltree,ltree,ltree,ltree,ltree,ltree) +CREATE FUNCTION lca(ltree,ltree,ltree,ltree,ltree,ltree) RETURNS ltree AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION lca(ltree,ltree,ltree,ltree,ltree,ltree,ltree) +CREATE FUNCTION lca(ltree,ltree,ltree,ltree,ltree,ltree,ltree) RETURNS ltree AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION lca(ltree,ltree,ltree,ltree,ltree,ltree,ltree,ltree) +CREATE FUNCTION lca(ltree,ltree,ltree,ltree,ltree,ltree,ltree,ltree) RETURNS ltree AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION ltree_isparent(ltree,ltree) +CREATE FUNCTION ltree_isparent(ltree,ltree) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION ltree_risparent(ltree,ltree) +CREATE FUNCTION ltree_risparent(ltree,ltree) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION ltree_addltree(ltree,ltree) +CREATE FUNCTION ltree_addltree(ltree,ltree) RETURNS ltree AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION ltree_addtext(ltree,text) +CREATE FUNCTION ltree_addtext(ltree,text) RETURNS ltree AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION ltree_textadd(text,ltree) +CREATE FUNCTION ltree_textadd(text,ltree) RETURNS ltree AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION ltreeparentsel(internal, oid, internal, integer) +CREATE FUNCTION ltreeparentsel(internal, oid, internal, integer) RETURNS float8 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -300,12 +297,12 @@ CREATE OPERATOR CLASS ltree_ops --lquery type -CREATE OR REPLACE FUNCTION lquery_in(cstring) +CREATE FUNCTION lquery_in(cstring) RETURNS lquery AS 'MODULE_PATHNAME' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION lquery_out(lquery) +CREATE FUNCTION lquery_out(lquery) RETURNS cstring AS 'MODULE_PATHNAME' LANGUAGE C STRICT; @@ -317,12 +314,12 @@ CREATE TYPE lquery ( STORAGE = extended ); -CREATE OR REPLACE FUNCTION ltq_regex(ltree,lquery) +CREATE FUNCTION ltq_regex(ltree,lquery) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION ltq_rregex(lquery,ltree) +CREATE FUNCTION ltq_rregex(lquery,ltree) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -364,12 +361,12 @@ CREATE OPERATOR ^~ ( JOIN = contjoinsel ); -CREATE OR REPLACE FUNCTION lt_q_regex(ltree,_lquery) +CREATE FUNCTION lt_q_regex(ltree,_lquery) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION lt_q_rregex(_lquery,ltree) +CREATE FUNCTION lt_q_rregex(_lquery,ltree) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -411,12 +408,12 @@ CREATE OPERATOR ^? ( JOIN = contjoinsel ); -CREATE OR REPLACE FUNCTION ltxtq_in(cstring) +CREATE FUNCTION ltxtq_in(cstring) RETURNS ltxtquery AS 'MODULE_PATHNAME' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION ltxtq_out(ltxtquery) +CREATE FUNCTION ltxtq_out(ltxtquery) RETURNS cstring AS 'MODULE_PATHNAME' LANGUAGE C STRICT; @@ -430,12 +427,12 @@ CREATE TYPE ltxtquery ( -- operations WITH ltxtquery -CREATE OR REPLACE FUNCTION ltxtq_exec(ltree, ltxtquery) +CREATE FUNCTION ltxtq_exec(ltree, ltxtquery) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION ltxtq_rexec(ltxtquery, ltree) +CREATE FUNCTION ltxtq_rexec(ltxtquery, ltree) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -478,12 +475,12 @@ CREATE OPERATOR ^@ ( ); --GiST support for ltree -CREATE OR REPLACE FUNCTION ltree_gist_in(cstring) +CREATE FUNCTION ltree_gist_in(cstring) RETURNS ltree_gist AS 'MODULE_PATHNAME' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION ltree_gist_out(ltree_gist) +CREATE FUNCTION ltree_gist_out(ltree_gist) RETURNS cstring AS 'MODULE_PATHNAME' LANGUAGE C STRICT; @@ -496,25 +493,25 @@ CREATE TYPE ltree_gist ( ); -CREATE OR REPLACE FUNCTION ltree_consistent(internal,internal,int2,oid,internal) +CREATE FUNCTION ltree_consistent(internal,internal,int2,oid,internal) RETURNS bool as 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION ltree_compress(internal) +CREATE FUNCTION ltree_compress(internal) RETURNS internal as 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION ltree_decompress(internal) +CREATE FUNCTION ltree_decompress(internal) RETURNS internal as 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION ltree_penalty(internal,internal,internal) +CREATE FUNCTION ltree_penalty(internal,internal,internal) RETURNS internal as 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION ltree_picksplit(internal, internal) +CREATE FUNCTION ltree_picksplit(internal, internal) RETURNS internal as 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION ltree_union(internal, internal) +CREATE FUNCTION ltree_union(internal, internal) RETURNS int4 as 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION ltree_same(internal, internal, internal) +CREATE FUNCTION ltree_same(internal, internal, internal) RETURNS internal as 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; CREATE OPERATOR CLASS gist_ltree_ops @@ -544,52 +541,52 @@ CREATE OPERATOR CLASS gist_ltree_ops -- arrays of ltree -CREATE OR REPLACE FUNCTION _ltree_isparent(_ltree,ltree) +CREATE FUNCTION _ltree_isparent(_ltree,ltree) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION _ltree_r_isparent(ltree,_ltree) +CREATE FUNCTION _ltree_r_isparent(ltree,_ltree) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION _ltree_risparent(_ltree,ltree) +CREATE FUNCTION _ltree_risparent(_ltree,ltree) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION _ltree_r_risparent(ltree,_ltree) +CREATE FUNCTION _ltree_r_risparent(ltree,_ltree) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION _ltq_regex(_ltree,lquery) +CREATE FUNCTION _ltq_regex(_ltree,lquery) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION _ltq_rregex(lquery,_ltree) +CREATE FUNCTION _ltq_rregex(lquery,_ltree) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION _lt_q_regex(_ltree,_lquery) +CREATE FUNCTION _lt_q_regex(_ltree,_lquery) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION _lt_q_rregex(_lquery,_ltree) +CREATE FUNCTION _lt_q_rregex(_lquery,_ltree) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION _ltxtq_exec(_ltree, ltxtquery) +CREATE FUNCTION _ltxtq_exec(_ltree, ltxtquery) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION _ltxtq_rexec(ltxtquery, _ltree) +CREATE FUNCTION _ltxtq_rexec(ltxtquery, _ltree) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -777,7 +774,7 @@ CREATE OPERATOR ^@ ( ); --extractors -CREATE OR REPLACE FUNCTION _ltree_extract_isparent(_ltree,ltree) +CREATE FUNCTION _ltree_extract_isparent(_ltree,ltree) RETURNS ltree AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -788,7 +785,7 @@ CREATE OPERATOR ?@> ( PROCEDURE = _ltree_extract_isparent ); -CREATE OR REPLACE FUNCTION _ltree_extract_risparent(_ltree,ltree) +CREATE FUNCTION _ltree_extract_risparent(_ltree,ltree) RETURNS ltree AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -799,7 +796,7 @@ CREATE OPERATOR ?<@ ( PROCEDURE = _ltree_extract_risparent ); -CREATE OR REPLACE FUNCTION _ltq_extract_regex(_ltree,lquery) +CREATE FUNCTION _ltq_extract_regex(_ltree,lquery) RETURNS ltree AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -810,7 +807,7 @@ CREATE OPERATOR ?~ ( PROCEDURE = _ltq_extract_regex ); -CREATE OR REPLACE FUNCTION _ltxtq_extract_exec(_ltree,ltxtquery) +CREATE FUNCTION _ltxtq_extract_exec(_ltree,ltxtquery) RETURNS ltree AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -822,32 +819,32 @@ CREATE OPERATOR ?@ ( ); --GiST support for ltree[] -CREATE OR REPLACE FUNCTION _ltree_consistent(internal,internal,int2,oid,internal) +CREATE FUNCTION _ltree_consistent(internal,internal,int2,oid,internal) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION _ltree_compress(internal) +CREATE FUNCTION _ltree_compress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION _ltree_penalty(internal,internal,internal) +CREATE FUNCTION _ltree_penalty(internal,internal,internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION _ltree_picksplit(internal, internal) +CREATE FUNCTION _ltree_picksplit(internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION _ltree_union(internal, internal) +CREATE FUNCTION _ltree_union(internal, internal) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION _ltree_same(internal, internal, internal) +CREATE FUNCTION _ltree_same(internal, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; diff --git a/contrib/ltree/ltree--unpackaged--1.0.sql b/contrib/ltree/ltree--unpackaged--1.0.sql new file mode 100644 index 0000000000..f483725b4a --- /dev/null +++ b/contrib/ltree/ltree--unpackaged--1.0.sql @@ -0,0 +1,131 @@ +/* contrib/ltree/ltree--unpackaged--1.0.sql */ + +ALTER EXTENSION ltree ADD type ltree; +ALTER EXTENSION ltree ADD function ltree_in(cstring); +ALTER EXTENSION ltree ADD function ltree_out(ltree); +ALTER EXTENSION ltree ADD function ltree_cmp(ltree,ltree); +ALTER EXTENSION ltree ADD function ltree_lt(ltree,ltree); +ALTER EXTENSION ltree ADD function ltree_le(ltree,ltree); +ALTER EXTENSION ltree ADD function ltree_eq(ltree,ltree); +ALTER EXTENSION ltree ADD function ltree_ge(ltree,ltree); +ALTER EXTENSION ltree ADD function ltree_gt(ltree,ltree); +ALTER EXTENSION ltree ADD function ltree_ne(ltree,ltree); +ALTER EXTENSION ltree ADD operator >(ltree,ltree); +ALTER EXTENSION ltree ADD operator >=(ltree,ltree); +ALTER EXTENSION ltree ADD operator <(ltree,ltree); +ALTER EXTENSION ltree ADD operator <=(ltree,ltree); +ALTER EXTENSION ltree ADD operator <>(ltree,ltree); +ALTER EXTENSION ltree ADD operator =(ltree,ltree); +ALTER EXTENSION ltree ADD function subltree(ltree,integer,integer); +ALTER EXTENSION ltree ADD function subpath(ltree,integer,integer); +ALTER EXTENSION ltree ADD function subpath(ltree,integer); +ALTER EXTENSION ltree ADD function index(ltree,ltree); +ALTER EXTENSION ltree ADD function index(ltree,ltree,integer); +ALTER EXTENSION ltree ADD function nlevel(ltree); +ALTER EXTENSION ltree ADD function ltree2text(ltree); +ALTER EXTENSION ltree ADD function text2ltree(text); +ALTER EXTENSION ltree ADD function lca(ltree[]); +ALTER EXTENSION ltree ADD function lca(ltree,ltree); +ALTER EXTENSION ltree ADD function lca(ltree,ltree,ltree); +ALTER EXTENSION ltree ADD function lca(ltree,ltree,ltree,ltree); +ALTER EXTENSION ltree ADD function lca(ltree,ltree,ltree,ltree,ltree); +ALTER EXTENSION ltree ADD function lca(ltree,ltree,ltree,ltree,ltree,ltree); +ALTER EXTENSION ltree ADD function lca(ltree,ltree,ltree,ltree,ltree,ltree,ltree); +ALTER EXTENSION ltree ADD function lca(ltree,ltree,ltree,ltree,ltree,ltree,ltree,ltree); +ALTER EXTENSION ltree ADD function ltree_isparent(ltree,ltree); +ALTER EXTENSION ltree ADD function ltree_risparent(ltree,ltree); +ALTER EXTENSION ltree ADD function ltree_addltree(ltree,ltree); +ALTER EXTENSION ltree ADD function ltree_addtext(ltree,text); +ALTER EXTENSION ltree ADD function ltree_textadd(text,ltree); +ALTER EXTENSION ltree ADD function ltreeparentsel(internal,oid,internal,integer); +ALTER EXTENSION ltree ADD operator <@(ltree,ltree); +ALTER EXTENSION ltree ADD operator @>(ltree,ltree); +ALTER EXTENSION ltree ADD operator ^<@(ltree,ltree); +ALTER EXTENSION ltree ADD operator ^@>(ltree,ltree); +ALTER EXTENSION ltree ADD operator ||(ltree,ltree); +ALTER EXTENSION ltree ADD operator ||(ltree,text); +ALTER EXTENSION ltree ADD operator ||(text,ltree); +ALTER EXTENSION ltree ADD operator family ltree_ops using btree; +ALTER EXTENSION ltree ADD operator class ltree_ops using btree; +ALTER EXTENSION ltree ADD type lquery; +ALTER EXTENSION ltree ADD function lquery_in(cstring); +ALTER EXTENSION ltree ADD function lquery_out(lquery); +ALTER EXTENSION ltree ADD function ltq_regex(ltree,lquery); +ALTER EXTENSION ltree ADD function ltq_rregex(lquery,ltree); +ALTER EXTENSION ltree ADD operator ~(lquery,ltree); +ALTER EXTENSION ltree ADD operator ~(ltree,lquery); +ALTER EXTENSION ltree ADD operator ^~(lquery,ltree); +ALTER EXTENSION ltree ADD operator ^~(ltree,lquery); +ALTER EXTENSION ltree ADD function lt_q_regex(ltree,lquery[]); +ALTER EXTENSION ltree ADD function lt_q_rregex(lquery[],ltree); +ALTER EXTENSION ltree ADD operator ?(lquery[],ltree); +ALTER EXTENSION ltree ADD operator ?(ltree,lquery[]); +ALTER EXTENSION ltree ADD operator ^?(lquery[],ltree); +ALTER EXTENSION ltree ADD operator ^?(ltree,lquery[]); +ALTER EXTENSION ltree ADD type ltxtquery; +ALTER EXTENSION ltree ADD function ltxtq_in(cstring); +ALTER EXTENSION ltree ADD function ltxtq_out(ltxtquery); +ALTER EXTENSION ltree ADD function ltxtq_exec(ltree,ltxtquery); +ALTER EXTENSION ltree ADD function ltxtq_rexec(ltxtquery,ltree); +ALTER EXTENSION ltree ADD operator @(ltxtquery,ltree); +ALTER EXTENSION ltree ADD operator @(ltree,ltxtquery); +ALTER EXTENSION ltree ADD operator ^@(ltxtquery,ltree); +ALTER EXTENSION ltree ADD operator ^@(ltree,ltxtquery); +ALTER EXTENSION ltree ADD type ltree_gist; +ALTER EXTENSION ltree ADD function ltree_gist_in(cstring); +ALTER EXTENSION ltree ADD function ltree_gist_out(ltree_gist); +ALTER EXTENSION ltree ADD function ltree_consistent(internal,internal,smallint,oid,internal); +ALTER EXTENSION ltree ADD function ltree_compress(internal); +ALTER EXTENSION ltree ADD function ltree_decompress(internal); +ALTER EXTENSION ltree ADD function ltree_penalty(internal,internal,internal); +ALTER EXTENSION ltree ADD function ltree_picksplit(internal,internal); +ALTER EXTENSION ltree ADD function ltree_union(internal,internal); +ALTER EXTENSION ltree ADD function ltree_same(internal,internal,internal); +ALTER EXTENSION ltree ADD operator family gist_ltree_ops using gist; +ALTER EXTENSION ltree ADD operator class gist_ltree_ops using gist; +ALTER EXTENSION ltree ADD function _ltree_isparent(ltree[],ltree); +ALTER EXTENSION ltree ADD function _ltree_r_isparent(ltree,ltree[]); +ALTER EXTENSION ltree ADD function _ltree_risparent(ltree[],ltree); +ALTER EXTENSION ltree ADD function _ltree_r_risparent(ltree,ltree[]); +ALTER EXTENSION ltree ADD function _ltq_regex(ltree[],lquery); +ALTER EXTENSION ltree ADD function _ltq_rregex(lquery,ltree[]); +ALTER EXTENSION ltree ADD function _lt_q_regex(ltree[],lquery[]); +ALTER EXTENSION ltree ADD function _lt_q_rregex(lquery[],ltree[]); +ALTER EXTENSION ltree ADD function _ltxtq_exec(ltree[],ltxtquery); +ALTER EXTENSION ltree ADD function _ltxtq_rexec(ltxtquery,ltree[]); +ALTER EXTENSION ltree ADD operator <@(ltree,ltree[]); +ALTER EXTENSION ltree ADD operator @>(ltree[],ltree); +ALTER EXTENSION ltree ADD operator @>(ltree,ltree[]); +ALTER EXTENSION ltree ADD operator <@(ltree[],ltree); +ALTER EXTENSION ltree ADD operator ~(lquery,ltree[]); +ALTER EXTENSION ltree ADD operator ~(ltree[],lquery); +ALTER EXTENSION ltree ADD operator ?(lquery[],ltree[]); +ALTER EXTENSION ltree ADD operator ?(ltree[],lquery[]); +ALTER EXTENSION ltree ADD operator @(ltxtquery,ltree[]); +ALTER EXTENSION ltree ADD operator @(ltree[],ltxtquery); +ALTER EXTENSION ltree ADD operator ^<@(ltree,ltree[]); +ALTER EXTENSION ltree ADD operator ^@>(ltree[],ltree); +ALTER EXTENSION ltree ADD operator ^@>(ltree,ltree[]); +ALTER EXTENSION ltree ADD operator ^<@(ltree[],ltree); +ALTER EXTENSION ltree ADD operator ^~(lquery,ltree[]); +ALTER EXTENSION ltree ADD operator ^~(ltree[],lquery); +ALTER EXTENSION ltree ADD operator ^?(lquery[],ltree[]); +ALTER EXTENSION ltree ADD operator ^?(ltree[],lquery[]); +ALTER EXTENSION ltree ADD operator ^@(ltxtquery,ltree[]); +ALTER EXTENSION ltree ADD operator ^@(ltree[],ltxtquery); +ALTER EXTENSION ltree ADD function _ltree_extract_isparent(ltree[],ltree); +ALTER EXTENSION ltree ADD operator ?@>(ltree[],ltree); +ALTER EXTENSION ltree ADD function _ltree_extract_risparent(ltree[],ltree); +ALTER EXTENSION ltree ADD operator ?<@(ltree[],ltree); +ALTER EXTENSION ltree ADD function _ltq_extract_regex(ltree[],lquery); +ALTER EXTENSION ltree ADD operator ?~(ltree[],lquery); +ALTER EXTENSION ltree ADD function _ltxtq_extract_exec(ltree[],ltxtquery); +ALTER EXTENSION ltree ADD operator ?@(ltree[],ltxtquery); +ALTER EXTENSION ltree ADD function _ltree_consistent(internal,internal,smallint,oid,internal); +ALTER EXTENSION ltree ADD function _ltree_compress(internal); +ALTER EXTENSION ltree ADD function _ltree_penalty(internal,internal,internal); +ALTER EXTENSION ltree ADD function _ltree_picksplit(internal,internal); +ALTER EXTENSION ltree ADD function _ltree_union(internal,internal); +ALTER EXTENSION ltree ADD function _ltree_same(internal,internal,internal); +ALTER EXTENSION ltree ADD operator family gist__ltree_ops using gist; +ALTER EXTENSION ltree ADD operator class gist__ltree_ops using gist; diff --git a/contrib/ltree/ltree.control b/contrib/ltree/ltree.control new file mode 100644 index 0000000000..d879fd618c --- /dev/null +++ b/contrib/ltree/ltree.control @@ -0,0 +1,5 @@ +# ltree extension +comment = 'data type for hierarchical tree-like structures' +default_version = '1.0' +module_pathname = '$libdir/ltree' +relocatable = true diff --git a/contrib/ltree/sql/ltree.sql b/contrib/ltree/sql/ltree.sql index 50aad78d3c..46cfa41a41 100644 --- a/contrib/ltree/sql/ltree.sql +++ b/contrib/ltree/sql/ltree.sql @@ -1,12 +1,4 @@ --- --- first, define the datatype. Turn off echoing so that expected file --- does not depend on contents of ltree.sql. --- -SET client_min_messages = warning; -\set ECHO none -\i ltree.sql -\set ECHO all -RESET client_min_messages; +CREATE EXTENSION ltree; SELECT ''::ltree; SELECT '1'::ltree; diff --git a/contrib/ltree/uninstall_ltree.sql b/contrib/ltree/uninstall_ltree.sql deleted file mode 100644 index 2e10b10e97..0000000000 --- a/contrib/ltree/uninstall_ltree.sql +++ /dev/null @@ -1,240 +0,0 @@ -/* contrib/ltree/uninstall_ltree.sql */ - --- Adjust this setting to control where the objects get dropped. -SET search_path = public; - -DROP OPERATOR CLASS gist__ltree_ops USING gist; - -DROP FUNCTION _ltree_same(internal, internal, internal); - -DROP FUNCTION _ltree_union(internal, internal); - -DROP FUNCTION _ltree_picksplit(internal, internal); - -DROP FUNCTION _ltree_penalty(internal,internal,internal); - -DROP FUNCTION _ltree_compress(internal); - -DROP FUNCTION _ltree_consistent(internal,internal,int2,oid,internal); - -DROP OPERATOR ?@ (_ltree, ltxtquery); - -DROP FUNCTION _ltxtq_extract_exec(_ltree,ltxtquery); - -DROP OPERATOR ?~ (_ltree, lquery); - -DROP FUNCTION _ltq_extract_regex(_ltree,lquery); - -DROP OPERATOR ?<@ (_ltree, ltree); - -DROP FUNCTION _ltree_extract_risparent(_ltree,ltree); - -DROP OPERATOR ?@> (_ltree, ltree); - -DROP FUNCTION _ltree_extract_isparent(_ltree,ltree); - -DROP OPERATOR ^@ (ltxtquery, _ltree); - -DROP OPERATOR ^@ (_ltree, ltxtquery); - -DROP OPERATOR ^? (_lquery, _ltree); - -DROP OPERATOR ^? (_ltree, _lquery); - -DROP OPERATOR ^~ (lquery, _ltree); - -DROP OPERATOR ^~ (_ltree, lquery); - -DROP OPERATOR ^@> (ltree, _ltree); - -DROP OPERATOR ^<@ (_ltree, ltree); - -DROP OPERATOR ^<@ (ltree, _ltree); - -DROP OPERATOR ^@> (_ltree, ltree); - -DROP OPERATOR @ (ltxtquery, _ltree); - -DROP OPERATOR @ (_ltree, ltxtquery); - -DROP OPERATOR ? (_lquery, _ltree); - -DROP OPERATOR ? (_ltree, _lquery); - -DROP OPERATOR ~ (lquery, _ltree); - -DROP OPERATOR ~ (_ltree, lquery); - -DROP OPERATOR @> (ltree, _ltree); - -DROP OPERATOR <@ (_ltree, ltree); - -DROP OPERATOR <@ (ltree, _ltree); - -DROP OPERATOR @> (_ltree, ltree); - -DROP FUNCTION _ltxtq_rexec(ltxtquery, _ltree); - -DROP FUNCTION _ltxtq_exec(_ltree, ltxtquery); - -DROP FUNCTION _lt_q_rregex(_lquery,_ltree); - -DROP FUNCTION _lt_q_regex(_ltree,_lquery); - -DROP FUNCTION _ltq_rregex(lquery,_ltree); - -DROP FUNCTION _ltq_regex(_ltree,lquery); - -DROP FUNCTION _ltree_r_risparent(ltree,_ltree); - -DROP FUNCTION _ltree_risparent(_ltree,ltree); - -DROP FUNCTION _ltree_r_isparent(ltree,_ltree); - -DROP FUNCTION _ltree_isparent(_ltree,ltree); - -DROP OPERATOR CLASS gist_ltree_ops USING gist; - -DROP FUNCTION ltree_same(internal, internal, internal); - -DROP FUNCTION ltree_union(internal, internal); - -DROP FUNCTION ltree_picksplit(internal, internal); - -DROP FUNCTION ltree_penalty(internal,internal,internal); - -DROP FUNCTION ltree_decompress(internal); - -DROP FUNCTION ltree_compress(internal); - -DROP FUNCTION ltree_consistent(internal,internal,int2,oid,internal); - -DROP TYPE ltree_gist CASCADE; - -DROP OPERATOR ^@ (ltxtquery, ltree); - -DROP OPERATOR ^@ (ltree, ltxtquery); - -DROP OPERATOR @ (ltxtquery, ltree); - -DROP OPERATOR @ (ltree, ltxtquery); - -DROP FUNCTION ltxtq_rexec(ltxtquery, ltree); - -DROP FUNCTION ltxtq_exec(ltree, ltxtquery); - -DROP TYPE ltxtquery CASCADE; - -DROP OPERATOR ^? (_lquery, ltree); - -DROP OPERATOR ^? (ltree, _lquery); - -DROP OPERATOR ? (_lquery, ltree); - -DROP OPERATOR ? (ltree, _lquery); - -DROP FUNCTION lt_q_rregex(_lquery,ltree); - -DROP FUNCTION lt_q_regex(ltree,_lquery); - -DROP OPERATOR ^~ (lquery, ltree); - -DROP OPERATOR ^~ (ltree, lquery); - -DROP OPERATOR ~ (lquery, ltree); - -DROP OPERATOR ~ (ltree, lquery); - -DROP FUNCTION ltq_rregex(lquery,ltree); - -DROP FUNCTION ltq_regex(ltree,lquery); - -DROP TYPE lquery CASCADE; - -DROP OPERATOR CLASS ltree_ops USING btree; - -DROP OPERATOR || (text, ltree); - -DROP OPERATOR || (ltree, text); - -DROP OPERATOR || (ltree, ltree); - -DROP OPERATOR ^<@ (ltree, ltree); - -DROP OPERATOR <@ (ltree, ltree); - -DROP OPERATOR ^@> (ltree, ltree); - -DROP OPERATOR @> (ltree, ltree); - -DROP FUNCTION ltreeparentsel(internal, oid, internal, integer); - -DROP FUNCTION ltree_textadd(text,ltree); - -DROP FUNCTION ltree_addtext(ltree,text); - -DROP FUNCTION ltree_addltree(ltree,ltree); - -DROP FUNCTION ltree_risparent(ltree,ltree); - -DROP FUNCTION ltree_isparent(ltree,ltree); - -DROP FUNCTION lca(ltree,ltree,ltree,ltree,ltree,ltree,ltree,ltree); - -DROP FUNCTION lca(ltree,ltree,ltree,ltree,ltree,ltree,ltree); - -DROP FUNCTION lca(ltree,ltree,ltree,ltree,ltree,ltree); - -DROP FUNCTION lca(ltree,ltree,ltree,ltree,ltree); - -DROP FUNCTION lca(ltree,ltree,ltree,ltree); - -DROP FUNCTION lca(ltree,ltree,ltree); - -DROP FUNCTION lca(ltree,ltree); - -DROP FUNCTION lca(_ltree); - -DROP FUNCTION text2ltree(text); - -DROP FUNCTION ltree2text(ltree); - -DROP FUNCTION nlevel(ltree); - -DROP FUNCTION index(ltree,ltree,int4); - -DROP FUNCTION index(ltree,ltree); - -DROP FUNCTION subpath(ltree,int4); - -DROP FUNCTION subpath(ltree,int4,int4); - -DROP FUNCTION subltree(ltree,int4,int4); - -DROP OPERATOR <> (ltree, ltree); - -DROP OPERATOR = (ltree, ltree); - -DROP OPERATOR > (ltree, ltree); - -DROP OPERATOR >= (ltree, ltree); - -DROP OPERATOR <= (ltree, ltree); - -DROP OPERATOR < (ltree, ltree); - -DROP FUNCTION ltree_ne(ltree,ltree); - -DROP FUNCTION ltree_gt(ltree,ltree); - -DROP FUNCTION ltree_ge(ltree,ltree); - -DROP FUNCTION ltree_eq(ltree,ltree); - -DROP FUNCTION ltree_le(ltree,ltree); - -DROP FUNCTION ltree_lt(ltree,ltree); - -DROP FUNCTION ltree_cmp(ltree,ltree); - -DROP TYPE ltree CASCADE; diff --git a/contrib/pageinspect/.gitignore b/contrib/pageinspect/.gitignore deleted file mode 100644 index fad166aaee..0000000000 --- a/contrib/pageinspect/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/pageinspect.sql diff --git a/contrib/pageinspect/Makefile b/contrib/pageinspect/Makefile index a8ae51dfd1..13ba6d3911 100644 --- a/contrib/pageinspect/Makefile +++ b/contrib/pageinspect/Makefile @@ -1,15 +1,10 @@ -#------------------------------------------------------------------------- -# -# pageinspect Makefile -# # contrib/pageinspect/Makefile -# -#------------------------------------------------------------------------- MODULE_big = pageinspect OBJS = rawpage.o heapfuncs.o btreefuncs.o fsmfuncs.o -DATA_built = pageinspect.sql -DATA = uninstall_pageinspect.sql + +EXTENSION = pageinspect +DATA = pageinspect--1.0.sql pageinspect--unpackaged--1.0.sql ifdef USE_PGXS PG_CONFIG = pg_config diff --git a/contrib/pageinspect/pageinspect.sql.in b/contrib/pageinspect/pageinspect--1.0.sql index d6058d409f..b64eb61774 100644 --- a/contrib/pageinspect/pageinspect.sql.in +++ b/contrib/pageinspect/pageinspect--1.0.sql @@ -1,17 +1,14 @@ -/* contrib/pageinspect/pageinspect.sql.in */ - --- Adjust this setting to control where the objects get created. -SET search_path = public; +/* contrib/pageinspect/pageinspect--1.0.sql */ -- -- get_raw_page() -- -CREATE OR REPLACE FUNCTION get_raw_page(text, int4) +CREATE FUNCTION get_raw_page(text, int4) RETURNS bytea AS 'MODULE_PATHNAME', 'get_raw_page' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION get_raw_page(text, text, int4) +CREATE FUNCTION get_raw_page(text, text, int4) RETURNS bytea AS 'MODULE_PATHNAME', 'get_raw_page_fork' LANGUAGE C STRICT; @@ -19,7 +16,7 @@ LANGUAGE C STRICT; -- -- page_header() -- -CREATE OR REPLACE FUNCTION page_header(IN page bytea, +CREATE FUNCTION page_header(IN page bytea, OUT lsn text, OUT tli smallint, OUT flags smallint, @@ -35,7 +32,7 @@ LANGUAGE C STRICT; -- -- heap_page_items() -- -CREATE OR REPLACE FUNCTION heap_page_items(IN page bytea, +CREATE FUNCTION heap_page_items(IN page bytea, OUT lp smallint, OUT lp_off smallint, OUT lp_flags smallint, @@ -56,7 +53,7 @@ LANGUAGE C STRICT; -- -- bt_metap() -- -CREATE OR REPLACE FUNCTION bt_metap(IN relname text, +CREATE FUNCTION bt_metap(IN relname text, OUT magic int4, OUT version int4, OUT root int4, @@ -69,7 +66,7 @@ LANGUAGE C STRICT; -- -- bt_page_stats() -- -CREATE OR REPLACE FUNCTION bt_page_stats(IN relname text, IN blkno int4, +CREATE FUNCTION bt_page_stats(IN relname text, IN blkno int4, OUT blkno int4, OUT type "char", OUT live_items int4, @@ -87,7 +84,7 @@ LANGUAGE C STRICT; -- -- bt_page_items() -- -CREATE OR REPLACE FUNCTION bt_page_items(IN relname text, IN blkno int4, +CREATE FUNCTION bt_page_items(IN relname text, IN blkno int4, OUT itemoffset smallint, OUT ctid tid, OUT itemlen smallint, @@ -101,7 +98,7 @@ LANGUAGE C STRICT; -- -- fsm_page_contents() -- -CREATE OR REPLACE FUNCTION fsm_page_contents(IN page bytea) +CREATE FUNCTION fsm_page_contents(IN page bytea) RETURNS text AS 'MODULE_PATHNAME', 'fsm_page_contents' LANGUAGE C STRICT; diff --git a/contrib/pageinspect/pageinspect--unpackaged--1.0.sql b/contrib/pageinspect/pageinspect--unpackaged--1.0.sql new file mode 100644 index 0000000000..a9d1b52a42 --- /dev/null +++ b/contrib/pageinspect/pageinspect--unpackaged--1.0.sql @@ -0,0 +1,10 @@ +/* contrib/pageinspect/pageinspect--unpackaged--1.0.sql */ + +ALTER EXTENSION pageinspect ADD function get_raw_page(text,integer); +ALTER EXTENSION pageinspect ADD function get_raw_page(text,text,integer); +ALTER EXTENSION pageinspect ADD function page_header(bytea); +ALTER EXTENSION pageinspect ADD function heap_page_items(bytea); +ALTER EXTENSION pageinspect ADD function bt_metap(text); +ALTER EXTENSION pageinspect ADD function bt_page_stats(text,integer); +ALTER EXTENSION pageinspect ADD function bt_page_items(text,integer); +ALTER EXTENSION pageinspect ADD function fsm_page_contents(bytea); diff --git a/contrib/pageinspect/pageinspect.control b/contrib/pageinspect/pageinspect.control new file mode 100644 index 0000000000..f9da0e86ed --- /dev/null +++ b/contrib/pageinspect/pageinspect.control @@ -0,0 +1,5 @@ +# pageinspect extension +comment = 'inspect the contents of database pages at a low level' +default_version = '1.0' +module_pathname = '$libdir/pageinspect' +relocatable = true diff --git a/contrib/pageinspect/uninstall_pageinspect.sql b/contrib/pageinspect/uninstall_pageinspect.sql deleted file mode 100644 index a980fd7d01..0000000000 --- a/contrib/pageinspect/uninstall_pageinspect.sql +++ /dev/null @@ -1,13 +0,0 @@ -/* contrib/pageinspect/uninstall_pageinspect.sql */ - --- Adjust this setting to control where the objects get dropped. -SET search_path = public; - -DROP FUNCTION get_raw_page(text, int4); -DROP FUNCTION get_raw_page(text, text, int4); -DROP FUNCTION page_header(bytea); -DROP FUNCTION heap_page_items(bytea); -DROP FUNCTION bt_metap(text); -DROP FUNCTION bt_page_stats(text, int4); -DROP FUNCTION bt_page_items(text, int4); -DROP FUNCTION fsm_page_contents(bytea); diff --git a/contrib/pg_buffercache/.gitignore b/contrib/pg_buffercache/.gitignore deleted file mode 100644 index fea8b0b3d4..0000000000 --- a/contrib/pg_buffercache/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/pg_buffercache.sql diff --git a/contrib/pg_buffercache/Makefile b/contrib/pg_buffercache/Makefile index ffcf0c3b92..323c0ac8ed 100644 --- a/contrib/pg_buffercache/Makefile +++ b/contrib/pg_buffercache/Makefile @@ -3,8 +3,8 @@ MODULE_big = pg_buffercache OBJS = pg_buffercache_pages.o -DATA_built = pg_buffercache.sql -DATA = uninstall_pg_buffercache.sql +EXTENSION = pg_buffercache +DATA = pg_buffercache--1.0.sql pg_buffercache--unpackaged--1.0.sql ifdef USE_PGXS PG_CONFIG = pg_config diff --git a/contrib/pg_buffercache/pg_buffercache.sql.in b/contrib/pg_buffercache/pg_buffercache--1.0.sql index 88b5e643ac..9407d21410 100644 --- a/contrib/pg_buffercache/pg_buffercache.sql.in +++ b/contrib/pg_buffercache/pg_buffercache--1.0.sql @@ -1,10 +1,7 @@ -/* contrib/pg_buffercache/pg_buffercache.sql.in */ - --- Adjust this setting to control where the objects get created. -SET search_path = public; +/* contrib/pg_buffercache/pg_buffercache--1.0.sql */ -- Register the function. -CREATE OR REPLACE FUNCTION pg_buffercache_pages() +CREATE FUNCTION pg_buffercache_pages() RETURNS SETOF RECORD AS 'MODULE_PATHNAME', 'pg_buffercache_pages' LANGUAGE C; @@ -15,6 +12,6 @@ CREATE VIEW pg_buffercache AS (bufferid integer, relfilenode oid, reltablespace oid, reldatabase oid, relforknumber int2, relblocknumber int8, isdirty bool, usagecount int2); --- Don't want these to be available at public. +-- Don't want these to be available to public. REVOKE ALL ON FUNCTION pg_buffercache_pages() FROM PUBLIC; REVOKE ALL ON pg_buffercache FROM PUBLIC; diff --git a/contrib/pg_buffercache/pg_buffercache--unpackaged--1.0.sql b/contrib/pg_buffercache/pg_buffercache--unpackaged--1.0.sql new file mode 100644 index 0000000000..f00a954d86 --- /dev/null +++ b/contrib/pg_buffercache/pg_buffercache--unpackaged--1.0.sql @@ -0,0 +1,4 @@ +/* contrib/pg_buffercache/pg_buffercache--unpackaged--1.0.sql */ + +ALTER EXTENSION pg_buffercache ADD function pg_buffercache_pages(); +ALTER EXTENSION pg_buffercache ADD view pg_buffercache; diff --git a/contrib/pg_buffercache/pg_buffercache.control b/contrib/pg_buffercache/pg_buffercache.control new file mode 100644 index 0000000000..709513c334 --- /dev/null +++ b/contrib/pg_buffercache/pg_buffercache.control @@ -0,0 +1,5 @@ +# pg_buffercache extension +comment = 'examine the shared buffer cache' +default_version = '1.0' +module_pathname = '$libdir/pg_buffercache' +relocatable = true diff --git a/contrib/pg_buffercache/uninstall_pg_buffercache.sql b/contrib/pg_buffercache/uninstall_pg_buffercache.sql deleted file mode 100644 index 62617cd20d..0000000000 --- a/contrib/pg_buffercache/uninstall_pg_buffercache.sql +++ /dev/null @@ -1,8 +0,0 @@ -/* contrib/pg_buffercache/uninstall_pg_buffercache.sql */ - --- Adjust this setting to control where the objects get dropped. -SET search_path = public; - -DROP VIEW pg_buffercache; - -DROP FUNCTION pg_buffercache_pages(); diff --git a/contrib/pg_freespacemap/.gitignore b/contrib/pg_freespacemap/.gitignore deleted file mode 100644 index 645433a39f..0000000000 --- a/contrib/pg_freespacemap/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/pg_freespacemap.sql diff --git a/contrib/pg_freespacemap/Makefile b/contrib/pg_freespacemap/Makefile index 65539d5d71..b2e3ba3aa3 100644 --- a/contrib/pg_freespacemap/Makefile +++ b/contrib/pg_freespacemap/Makefile @@ -3,8 +3,8 @@ MODULE_big = pg_freespacemap OBJS = pg_freespacemap.o -DATA_built = pg_freespacemap.sql -DATA = uninstall_pg_freespacemap.sql +EXTENSION = pg_freespacemap +DATA = pg_freespacemap--1.0.sql pg_freespacemap--unpackaged--1.0.sql ifdef USE_PGXS PG_CONFIG = pg_config diff --git a/contrib/pg_freespacemap/pg_freespacemap.sql.in b/contrib/pg_freespacemap/pg_freespacemap--1.0.sql index 5ef8ba46ad..d63420e33a 100644 --- a/contrib/pg_freespacemap/pg_freespacemap.sql.in +++ b/contrib/pg_freespacemap/pg_freespacemap--1.0.sql @@ -1,17 +1,13 @@ -/* contrib/pg_freespacemap/pg_freespacemap.sql.in */ - --- Adjust this setting to control where the objects get created. -SET search_path = public; - +/* contrib/pg_freespacemap/pg_freespacemap--1.0.sql */ -- Register the C function. -CREATE OR REPLACE FUNCTION pg_freespace(regclass, bigint) +CREATE FUNCTION pg_freespace(regclass, bigint) RETURNS int2 AS 'MODULE_PATHNAME', 'pg_freespace' LANGUAGE C STRICT; -- pg_freespace shows the recorded space avail at each block in a relation -CREATE OR REPLACE FUNCTION +CREATE FUNCTION pg_freespace(rel regclass, blkno OUT bigint, avail OUT int2) RETURNS SETOF RECORD AS $$ diff --git a/contrib/pg_freespacemap/pg_freespacemap--unpackaged--1.0.sql b/contrib/pg_freespacemap/pg_freespacemap--unpackaged--1.0.sql new file mode 100644 index 0000000000..4c7487fa4e --- /dev/null +++ b/contrib/pg_freespacemap/pg_freespacemap--unpackaged--1.0.sql @@ -0,0 +1,4 @@ +/* contrib/pg_freespacemap/pg_freespacemap--unpackaged--1.0.sql */ + +ALTER EXTENSION pg_freespacemap ADD function pg_freespace(regclass,bigint); +ALTER EXTENSION pg_freespacemap ADD function pg_freespace(regclass); diff --git a/contrib/pg_freespacemap/pg_freespacemap.control b/contrib/pg_freespacemap/pg_freespacemap.control new file mode 100644 index 0000000000..34b695ff75 --- /dev/null +++ b/contrib/pg_freespacemap/pg_freespacemap.control @@ -0,0 +1,5 @@ +# pg_freespacemap extension +comment = 'examine the free space map (FSM)' +default_version = '1.0' +module_pathname = '$libdir/pg_freespacemap' +relocatable = true diff --git a/contrib/pg_freespacemap/uninstall_pg_freespacemap.sql b/contrib/pg_freespacemap/uninstall_pg_freespacemap.sql deleted file mode 100644 index 168506708a..0000000000 --- a/contrib/pg_freespacemap/uninstall_pg_freespacemap.sql +++ /dev/null @@ -1,7 +0,0 @@ -/* contrib/pg_freespacemap/uninstall_pg_freespacemap.sql */ - --- Adjust this setting to control where the objects get dropped. -SET search_path = public; - -DROP FUNCTION pg_freespace(regclass, bigint); -DROP FUNCTION pg_freespace(regclass); diff --git a/contrib/pg_stat_statements/.gitignore b/contrib/pg_stat_statements/.gitignore deleted file mode 100644 index 2ca3f068d0..0000000000 --- a/contrib/pg_stat_statements/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/pg_stat_statements.sql diff --git a/contrib/pg_stat_statements/Makefile b/contrib/pg_stat_statements/Makefile index efb26a90f6..e086fd8a82 100644 --- a/contrib/pg_stat_statements/Makefile +++ b/contrib/pg_stat_statements/Makefile @@ -1,10 +1,11 @@ # contrib/pg_stat_statements/Makefile MODULE_big = pg_stat_statements -DATA_built = pg_stat_statements.sql -DATA = uninstall_pg_stat_statements.sql OBJS = pg_stat_statements.o +EXTENSION = pg_stat_statements +DATA = pg_stat_statements--1.0.sql pg_stat_statements--unpackaged--1.0.sql + ifdef USE_PGXS PG_CONFIG = pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) diff --git a/contrib/pg_stat_statements/pg_stat_statements.sql.in b/contrib/pg_stat_statements/pg_stat_statements--1.0.sql index 56d5fd591a..e17b82c616 100644 --- a/contrib/pg_stat_statements/pg_stat_statements.sql.in +++ b/contrib/pg_stat_statements/pg_stat_statements--1.0.sql @@ -1,7 +1,4 @@ -/* contrib/pg_stat_statements/pg_stat_statements.sql.in */ - --- Adjust this setting to control where the objects get created. -SET search_path = public; +/* contrib/pg_stat_statements/pg_stat_statements--1.0.sql */ -- Register functions. CREATE FUNCTION pg_stat_statements_reset() diff --git a/contrib/pg_stat_statements/pg_stat_statements--unpackaged--1.0.sql b/contrib/pg_stat_statements/pg_stat_statements--unpackaged--1.0.sql new file mode 100644 index 0000000000..9dda85cbdf --- /dev/null +++ b/contrib/pg_stat_statements/pg_stat_statements--unpackaged--1.0.sql @@ -0,0 +1,5 @@ +/* contrib/pg_stat_statements/pg_stat_statements--unpackaged--1.0.sql */ + +ALTER EXTENSION pg_stat_statements ADD function pg_stat_statements_reset(); +ALTER EXTENSION pg_stat_statements ADD function pg_stat_statements(); +ALTER EXTENSION pg_stat_statements ADD view pg_stat_statements; diff --git a/contrib/pg_stat_statements/pg_stat_statements.control b/contrib/pg_stat_statements/pg_stat_statements.control new file mode 100644 index 0000000000..6f9a947122 --- /dev/null +++ b/contrib/pg_stat_statements/pg_stat_statements.control @@ -0,0 +1,5 @@ +# pg_stat_statements extension +comment = 'track execution statistics of all SQL statements executed' +default_version = '1.0' +module_pathname = '$libdir/pg_stat_statements' +relocatable = true diff --git a/contrib/pg_stat_statements/uninstall_pg_stat_statements.sql b/contrib/pg_stat_statements/uninstall_pg_stat_statements.sql deleted file mode 100644 index d2832a2986..0000000000 --- a/contrib/pg_stat_statements/uninstall_pg_stat_statements.sql +++ /dev/null @@ -1,8 +0,0 @@ -/* contrib/pg_stat_statements/uninstall_pg_stat_statements.sql */ - --- Adjust this setting to control where the objects get dropped. -SET search_path = public; - -DROP VIEW pg_stat_statements; -DROP FUNCTION pg_stat_statements(); -DROP FUNCTION pg_stat_statements_reset(); diff --git a/contrib/pg_test_fsync/Makefile b/contrib/pg_test_fsync/Makefile index a9365667b3..b456429098 100644 --- a/contrib/pg_test_fsync/Makefile +++ b/contrib/pg_test_fsync/Makefile @@ -1,6 +1,3 @@ -# -# Makefile for pg_test_fsync -# # contrib/pg_test_fsync/Makefile PGFILEDESC = "pg_test_fsync - test various disk sync methods" diff --git a/contrib/pg_trgm/.gitignore b/contrib/pg_trgm/.gitignore index 9cda826ca4..19b6c5ba42 100644 --- a/contrib/pg_trgm/.gitignore +++ b/contrib/pg_trgm/.gitignore @@ -1,3 +1,2 @@ -/pg_trgm.sql # Generated subdirectories /results/ diff --git a/contrib/pg_trgm/Makefile b/contrib/pg_trgm/Makefile index cf2dec795c..64fd69f2cb 100644 --- a/contrib/pg_trgm/Makefile +++ b/contrib/pg_trgm/Makefile @@ -3,8 +3,9 @@ MODULE_big = pg_trgm OBJS = trgm_op.o trgm_gist.o trgm_gin.o -DATA_built = pg_trgm.sql -DATA = uninstall_pg_trgm.sql +EXTENSION = pg_trgm +DATA = pg_trgm--1.0.sql pg_trgm--unpackaged--1.0.sql + REGRESS = pg_trgm ifdef USE_PGXS diff --git a/contrib/pg_trgm/expected/pg_trgm.out b/contrib/pg_trgm/expected/pg_trgm.out index 6e73af2a32..e7af7d4890 100644 --- a/contrib/pg_trgm/expected/pg_trgm.out +++ b/contrib/pg_trgm/expected/pg_trgm.out @@ -1,10 +1,4 @@ --- --- first, define the datatype. Turn off echoing so that expected file --- does not depend on contents of pg_tgrm.sql. --- -SET client_min_messages = warning; -\set ECHO none -RESET client_min_messages; +CREATE EXTENSION pg_trgm; select show_trgm(''); show_trgm ----------- diff --git a/contrib/pg_trgm/pg_trgm.sql.in b/contrib/pg_trgm/pg_trgm--1.0.sql index 12a8c21071..79ea509042 100644 --- a/contrib/pg_trgm/pg_trgm.sql.in +++ b/contrib/pg_trgm/pg_trgm--1.0.sql @@ -1,29 +1,26 @@ -/* contrib/pg_trgm/pg_trgm.sql.in */ +/* contrib/pg_trgm/pg_trgm--1.0.sql */ --- Adjust this setting to control where the objects get created. -SET search_path = public; - -CREATE OR REPLACE FUNCTION set_limit(float4) +CREATE FUNCTION set_limit(float4) RETURNS float4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT VOLATILE; -CREATE OR REPLACE FUNCTION show_limit() +CREATE FUNCTION show_limit() RETURNS float4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT STABLE; -CREATE OR REPLACE FUNCTION show_trgm(text) +CREATE FUNCTION show_trgm(text) RETURNS _text AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION similarity(text,text) +CREATE FUNCTION similarity(text,text) RETURNS float4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION similarity_op(text,text) +CREATE FUNCTION similarity_op(text,text) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT STABLE; -- stable because depends on trgm_limit @@ -37,7 +34,7 @@ CREATE OPERATOR % ( JOIN = contjoinsel ); -CREATE OR REPLACE FUNCTION similarity_dist(text,text) +CREATE FUNCTION similarity_dist(text,text) RETURNS float4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -50,12 +47,12 @@ CREATE OPERATOR <-> ( ); -- gist key -CREATE OR REPLACE FUNCTION gtrgm_in(cstring) +CREATE FUNCTION gtrgm_in(cstring) RETURNS gtrgm AS 'MODULE_PATHNAME' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION gtrgm_out(gtrgm) +CREATE FUNCTION gtrgm_out(gtrgm) RETURNS cstring AS 'MODULE_PATHNAME' LANGUAGE C STRICT; @@ -67,42 +64,42 @@ CREATE TYPE gtrgm ( ); -- support functions for gist -CREATE OR REPLACE FUNCTION gtrgm_consistent(internal,text,int,oid,internal) +CREATE FUNCTION gtrgm_consistent(internal,text,int,oid,internal) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gtrgm_distance(internal,text,int,oid) +CREATE FUNCTION gtrgm_distance(internal,text,int,oid) RETURNS float8 AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gtrgm_compress(internal) +CREATE FUNCTION gtrgm_compress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gtrgm_decompress(internal) +CREATE FUNCTION gtrgm_decompress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gtrgm_penalty(internal,internal,internal) +CREATE FUNCTION gtrgm_penalty(internal,internal,internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gtrgm_picksplit(internal, internal) +CREATE FUNCTION gtrgm_picksplit(internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gtrgm_union(bytea, internal) +CREATE FUNCTION gtrgm_union(bytea, internal) RETURNS _int4 AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gtrgm_same(gtrgm, gtrgm, internal) +CREATE FUNCTION gtrgm_same(gtrgm, gtrgm, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; @@ -126,17 +123,17 @@ AS STORAGE gtrgm; -- support functions for gin -CREATE OR REPLACE FUNCTION gin_extract_value_trgm(text, internal) +CREATE FUNCTION gin_extract_value_trgm(text, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gin_extract_query_trgm(text, internal, int2, internal, internal, internal, internal) +CREATE FUNCTION gin_extract_query_trgm(text, internal, int2, internal, internal, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gin_trgm_consistent(internal, int2, text, int4, internal, internal, internal, internal) +CREATE FUNCTION gin_trgm_consistent(internal, int2, text, int4, internal, internal, internal, internal) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; diff --git a/contrib/pg_trgm/pg_trgm--unpackaged--1.0.sql b/contrib/pg_trgm/pg_trgm--unpackaged--1.0.sql new file mode 100644 index 0000000000..ba070384d0 --- /dev/null +++ b/contrib/pg_trgm/pg_trgm--unpackaged--1.0.sql @@ -0,0 +1,40 @@ +/* contrib/pg_trgm/pg_trgm--unpackaged--1.0.sql */ + +ALTER EXTENSION pg_trgm ADD function set_limit(real); +ALTER EXTENSION pg_trgm ADD function show_limit(); +ALTER EXTENSION pg_trgm ADD function show_trgm(text); +ALTER EXTENSION pg_trgm ADD function similarity(text,text); +ALTER EXTENSION pg_trgm ADD function similarity_op(text,text); +ALTER EXTENSION pg_trgm ADD operator %(text,text); +ALTER EXTENSION pg_trgm ADD type gtrgm; +ALTER EXTENSION pg_trgm ADD function gtrgm_in(cstring); +ALTER EXTENSION pg_trgm ADD function gtrgm_out(gtrgm); +ALTER EXTENSION pg_trgm ADD function gtrgm_consistent(internal,text,integer,oid,internal); +ALTER EXTENSION pg_trgm ADD function gtrgm_distance(internal,text,integer,oid); +ALTER EXTENSION pg_trgm ADD function gtrgm_compress(internal); +ALTER EXTENSION pg_trgm ADD function gtrgm_decompress(internal); +ALTER EXTENSION pg_trgm ADD function gtrgm_penalty(internal,internal,internal); +ALTER EXTENSION pg_trgm ADD function gtrgm_picksplit(internal,internal); +ALTER EXTENSION pg_trgm ADD function gtrgm_union(bytea,internal); +ALTER EXTENSION pg_trgm ADD function gtrgm_same(gtrgm,gtrgm,internal); +ALTER EXTENSION pg_trgm ADD operator family gist_trgm_ops using gist; +ALTER EXTENSION pg_trgm ADD operator class gist_trgm_ops using gist; +ALTER EXTENSION pg_trgm ADD function gin_extract_value_trgm(text,internal); +ALTER EXTENSION pg_trgm ADD function gin_extract_query_trgm(text,internal,smallint,internal,internal,internal,internal); +ALTER EXTENSION pg_trgm ADD function gin_trgm_consistent(internal,smallint,text,integer,internal,internal,internal,internal); +ALTER EXTENSION pg_trgm ADD operator family gin_trgm_ops using gin; +ALTER EXTENSION pg_trgm ADD operator class gin_trgm_ops using gin; + +-- these were not in 9.0: + +CREATE FUNCTION similarity_dist(text,text) +RETURNS float4 +AS 'MODULE_PATHNAME' +LANGUAGE C STRICT IMMUTABLE; + +CREATE OPERATOR <-> ( + LEFTARG = text, + RIGHTARG = text, + PROCEDURE = similarity_dist, + COMMUTATOR = '<->' +); diff --git a/contrib/pg_trgm/pg_trgm.control b/contrib/pg_trgm/pg_trgm.control new file mode 100644 index 0000000000..70404d881d --- /dev/null +++ b/contrib/pg_trgm/pg_trgm.control @@ -0,0 +1,5 @@ +# pg_trgm extension +comment = 'text similarity measurement and index searching based on trigrams' +default_version = '1.0' +module_pathname = '$libdir/pg_trgm' +relocatable = true diff --git a/contrib/pg_trgm/sql/pg_trgm.sql b/contrib/pg_trgm/sql/pg_trgm.sql index b8209344c3..ea902f602f 100644 --- a/contrib/pg_trgm/sql/pg_trgm.sql +++ b/contrib/pg_trgm/sql/pg_trgm.sql @@ -1,12 +1,4 @@ --- --- first, define the datatype. Turn off echoing so that expected file --- does not depend on contents of pg_tgrm.sql. --- -SET client_min_messages = warning; -\set ECHO none -\i pg_trgm.sql -\set ECHO all -RESET client_min_messages; +CREATE EXTENSION pg_trgm; select show_trgm(''); select show_trgm('(*&^$@%@'); diff --git a/contrib/pg_trgm/uninstall_pg_trgm.sql b/contrib/pg_trgm/uninstall_pg_trgm.sql deleted file mode 100644 index 961e40ca48..0000000000 --- a/contrib/pg_trgm/uninstall_pg_trgm.sql +++ /dev/null @@ -1,48 +0,0 @@ -/* contrib/pg_trgm/uninstall_pg_trgm.sql */ - --- Adjust this setting to control where the objects get dropped. -SET search_path = public; - -DROP OPERATOR CLASS gist_trgm_ops USING gist; - -DROP FUNCTION gtrgm_same(gtrgm, gtrgm, internal); - -DROP FUNCTION gtrgm_union(bytea, internal); - -DROP FUNCTION gtrgm_picksplit(internal, internal); - -DROP FUNCTION gtrgm_penalty(internal,internal,internal); - -DROP FUNCTION gtrgm_decompress(internal); - -DROP FUNCTION gtrgm_compress(internal); - -DROP FUNCTION gtrgm_consistent(internal,text,int,oid,internal); - -DROP FUNCTION gtrgm_distance(internal,text,int,oid); - -DROP TYPE gtrgm CASCADE; - -DROP OPERATOR CLASS gin_trgm_ops USING gin; - -DROP FUNCTION gin_extract_value_trgm(text, internal); - -DROP FUNCTION gin_extract_query_trgm(text, internal, int2, internal, internal, internal, internal); - -DROP FUNCTION gin_trgm_consistent(internal, int2, text, int4, internal, internal, internal, internal); - -DROP OPERATOR % (text, text); - -DROP FUNCTION similarity_op(text,text); - -DROP OPERATOR <-> (text, text); - -DROP FUNCTION similarity_dist(text,text); - -DROP FUNCTION similarity(text,text); - -DROP FUNCTION show_trgm(text); - -DROP FUNCTION show_limit(); - -DROP FUNCTION set_limit(float4); diff --git a/contrib/pg_upgrade/Makefile b/contrib/pg_upgrade/Makefile index 79ac234b5e..8f3fd7c9bb 100644 --- a/contrib/pg_upgrade/Makefile +++ b/contrib/pg_upgrade/Makefile @@ -1,6 +1,3 @@ -# -# Makefile for pg_upgrade -# # contrib/pg_upgrade/Makefile PGFILEDESC = "pg_upgrade - an in-place binary upgrade utility" diff --git a/contrib/pg_upgrade/check.c b/contrib/pg_upgrade/check.c index 1103e362a4..2e6400e4cc 100644 --- a/contrib/pg_upgrade/check.c +++ b/contrib/pg_upgrade/check.c @@ -362,8 +362,6 @@ check_new_db_is_empty(void) } } - free_db_and_rel_infos(&new_cluster.dbarr); - if (found) pg_log(PG_FATAL, "New cluster is not empty; exiting\n"); } diff --git a/contrib/pg_upgrade/function.c b/contrib/pg_upgrade/function.c index d7c790c67d..c01ff046bb 100644 --- a/contrib/pg_upgrade/function.c +++ b/contrib/pg_upgrade/function.c @@ -36,52 +36,58 @@ install_support_functions_in_new_db(const char *db_name) PQclear(executeQueryOrDie(conn, "CREATE OR REPLACE FUNCTION " - " binary_upgrade.set_next_pg_type_oid(OID) " + "binary_upgrade.set_next_pg_type_oid(OID) " "RETURNS VOID " "AS '$libdir/pg_upgrade_support' " "LANGUAGE C STRICT;")); PQclear(executeQueryOrDie(conn, "CREATE OR REPLACE FUNCTION " - " binary_upgrade.set_next_array_pg_type_oid(OID) " + "binary_upgrade.set_next_array_pg_type_oid(OID) " "RETURNS VOID " "AS '$libdir/pg_upgrade_support' " "LANGUAGE C STRICT;")); PQclear(executeQueryOrDie(conn, "CREATE OR REPLACE FUNCTION " - " binary_upgrade.set_next_toast_pg_type_oid(OID) " + "binary_upgrade.set_next_toast_pg_type_oid(OID) " "RETURNS VOID " "AS '$libdir/pg_upgrade_support' " "LANGUAGE C STRICT;")); PQclear(executeQueryOrDie(conn, "CREATE OR REPLACE FUNCTION " - " binary_upgrade.set_next_heap_pg_class_oid(OID) " + "binary_upgrade.set_next_heap_pg_class_oid(OID) " "RETURNS VOID " "AS '$libdir/pg_upgrade_support' " "LANGUAGE C STRICT;")); PQclear(executeQueryOrDie(conn, "CREATE OR REPLACE FUNCTION " - " binary_upgrade.set_next_index_pg_class_oid(OID) " + "binary_upgrade.set_next_index_pg_class_oid(OID) " "RETURNS VOID " "AS '$libdir/pg_upgrade_support' " "LANGUAGE C STRICT;")); PQclear(executeQueryOrDie(conn, "CREATE OR REPLACE FUNCTION " - " binary_upgrade.set_next_toast_pg_class_oid(OID) " + "binary_upgrade.set_next_toast_pg_class_oid(OID) " "RETURNS VOID " "AS '$libdir/pg_upgrade_support' " "LANGUAGE C STRICT;")); PQclear(executeQueryOrDie(conn, "CREATE OR REPLACE FUNCTION " - " binary_upgrade.set_next_pg_enum_oid(OID) " + "binary_upgrade.set_next_pg_enum_oid(OID) " "RETURNS VOID " "AS '$libdir/pg_upgrade_support' " "LANGUAGE C STRICT;")); PQclear(executeQueryOrDie(conn, "CREATE OR REPLACE FUNCTION " - " binary_upgrade.set_next_pg_authid_oid(OID) " + "binary_upgrade.set_next_pg_authid_oid(OID) " "RETURNS VOID " "AS '$libdir/pg_upgrade_support' " "LANGUAGE C STRICT;")); + PQclear(executeQueryOrDie(conn, + "CREATE OR REPLACE FUNCTION " + "binary_upgrade.create_empty_extension(text, text, bool, text, oid[], text[], text[]) " + "RETURNS VOID " + "AS '$libdir/pg_upgrade_support' " + "LANGUAGE C;")); PQfinish(conn); } @@ -139,8 +145,8 @@ get_loadable_libraries(void) "SELECT DISTINCT probin " "FROM pg_catalog.pg_proc " "WHERE prolang = 13 /* C */ AND " - " probin IS NOT NULL AND " - " oid >= %u;", + "probin IS NOT NULL AND " + "oid >= %u;", FirstNormalObjectId); totaltups += PQntuples(ress[dbnum]); diff --git a/contrib/pg_upgrade/info.c b/contrib/pg_upgrade/info.c index c0412311d9..0c518a2d1b 100644 --- a/contrib/pg_upgrade/info.c +++ b/contrib/pg_upgrade/info.c @@ -48,7 +48,7 @@ gen_db_file_maps(DbInfo *old_db, DbInfo *new_db, for (relnum = 0; relnum < old_db->rel_arr.nrels; relnum++) { RelInfo *old_rel = &old_db->rel_arr.rels[relnum]; - RelInfo *new_rel = &old_db->rel_arr.rels[relnum]; + RelInfo *new_rel = &new_db->rel_arr.rels[relnum]; if (old_rel->reloid != new_rel->reloid) pg_log(PG_FATAL, "mismatch of relation id: database \"%s\", old relid %d, new relid %d\n", @@ -104,6 +104,12 @@ create_rel_filename_map(const char *old_data, const char *new_data, /* new_relfilenode will match old and new pg_class.oid */ map->new_relfilenode = new_rel->relfilenode; + if (strcmp(old_rel->nspname, new_rel->nspname) != 0 || + strcmp(old_rel->relname, new_rel->relname) != 0) + pg_log(PG_FATAL, "mismatch of relation id: database \"%s\", old rel %s.%s, new rel %s.%s\n", + old_db, old_rel->nspname, old_rel->relname, + new_rel->nspname, new_rel->relname); + /* used only for logging and error reporing, old/new are identical */ snprintf(map->nspname, sizeof(map->nspname), "%s", old_rel->nspname); snprintf(map->relname, sizeof(map->relname), "%s", old_rel->relname); @@ -141,6 +147,9 @@ get_db_and_rel_infos(ClusterInfo *cluster) { int dbnum; + if (cluster->dbarr.dbs != NULL) + free_db_and_rel_infos(&cluster->dbarr); + get_db_infos(cluster); for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++) @@ -148,7 +157,7 @@ get_db_and_rel_infos(ClusterInfo *cluster) if (log_opts.debug) { - pg_log(PG_DEBUG, "%s databases\n", CLUSTER_NAME(cluster)); + pg_log(PG_DEBUG, "\n%s databases:\n", CLUSTER_NAME(cluster)); print_db_infos(&cluster->dbarr); } } @@ -311,6 +320,7 @@ free_db_and_rel_infos(DbInfoArr *db_arr) for (dbnum = 0; dbnum < db_arr->ndbs; dbnum++) free_rel_infos(&db_arr->dbs[dbnum].rel_arr); pg_free(db_arr->dbs); + db_arr->dbs = NULL; db_arr->ndbs = 0; } diff --git a/contrib/pg_upgrade/pg_upgrade.c b/contrib/pg_upgrade/pg_upgrade.c index 368c0e781a..061544cac8 100644 --- a/contrib/pg_upgrade/pg_upgrade.c +++ b/contrib/pg_upgrade/pg_upgrade.c @@ -286,7 +286,6 @@ create_new_objects(void) check_ok(); /* regenerate now that we have objects in the databases */ - free_db_and_rel_infos(&new_cluster.dbarr); get_db_and_rel_infos(&new_cluster); uninstall_support_functions_from_new_cluster(); @@ -426,31 +425,10 @@ set_frozenxids(void) static void cleanup(void) { - int tblnum; char filename[MAXPGPATH]; - for (tblnum = 0; tblnum < os_info.num_tablespaces; tblnum++) - pg_free(os_info.tablespaces[tblnum]); - pg_free(os_info.tablespaces); - - free_db_and_rel_infos(&old_cluster.dbarr); - free_db_and_rel_infos(&new_cluster.dbarr); - pg_free(log_opts.filename); - pg_free(os_info.user); - pg_free(old_cluster.controldata.lc_collate); - pg_free(new_cluster.controldata.lc_collate); - pg_free(old_cluster.controldata.lc_ctype); - pg_free(new_cluster.controldata.lc_ctype); - pg_free(old_cluster.controldata.encoding); - pg_free(new_cluster.controldata.encoding); - pg_free(old_cluster.tablespace_suffix); - pg_free(new_cluster.tablespace_suffix); - - if (log_opts.fd != NULL) - { + if (log_opts.fd) fclose(log_opts.fd); - log_opts.fd = NULL; - } if (log_opts.debug_fd) fclose(log_opts.debug_fd); diff --git a/contrib/pg_upgrade/relfilenode.c b/contrib/pg_upgrade/relfilenode.c index ca9640794e..d111b13de9 100644 --- a/contrib/pg_upgrade/relfilenode.c +++ b/contrib/pg_upgrade/relfilenode.c @@ -49,7 +49,7 @@ transfer_all_new_dbs(DbInfoArr *old_db_arr, pageCnvCtx *pageConverter = NULL; if (strcmp(old_db->db_name, new_db->db_name) != 0) - pg_log(PG_FATAL, "old and new databases have a different names: old \"%s\", new \"%s\"\n", + pg_log(PG_FATAL, "old and new databases have different names: old \"%s\", new \"%s\"\n", old_db->db_name, new_db->db_name); n_maps = 0; diff --git a/contrib/pg_upgrade_support/Makefile b/contrib/pg_upgrade_support/Makefile index e23c9bebcb..f7def160c3 100644 --- a/contrib/pg_upgrade_support/Makefile +++ b/contrib/pg_upgrade_support/Makefile @@ -1,6 +1,3 @@ -# -# Makefile for pg_upgrade_support -# # contrib/pg_upgrade_support/Makefile PGFILEDESC = "pg_upgrade_support - server-side functions for pg_upgrade" diff --git a/contrib/pg_upgrade_support/pg_upgrade_support.c b/contrib/pg_upgrade_support/pg_upgrade_support.c index b499ff80e6..02d1512719 100644 --- a/contrib/pg_upgrade_support/pg_upgrade_support.c +++ b/contrib/pg_upgrade_support/pg_upgrade_support.c @@ -1,8 +1,9 @@ /* - * pg_upgrade_sysoids.c + * pg_upgrade_support.c * * server-side functions to set backend global variables - * to control oid and relfilenode assignment + * to control oid and relfilenode assignment, and do other special + * hacks needed for pg_upgrade. * * Copyright (c) 2010-2011, PostgreSQL Global Development Group * contrib/pg_upgrade_support/pg_upgrade_support.c @@ -12,7 +13,13 @@ #include "fmgr.h" #include "catalog/dependency.h" +#include "catalog/namespace.h" #include "catalog/pg_class.h" +#include "catalog/pg_type.h" +#include "commands/extension.h" +#include "miscadmin.h" +#include "utils/array.h" +#include "utils/builtins.h" /* THIS IS USED ONLY FOR PG >= 9.0 */ @@ -42,6 +49,8 @@ Datum set_next_toast_pg_class_oid(PG_FUNCTION_ARGS); Datum set_next_pg_enum_oid(PG_FUNCTION_ARGS); Datum set_next_pg_authid_oid(PG_FUNCTION_ARGS); +Datum create_empty_extension(PG_FUNCTION_ARGS); + PG_FUNCTION_INFO_V1(set_next_pg_type_oid); PG_FUNCTION_INFO_V1(set_next_array_pg_type_oid); PG_FUNCTION_INFO_V1(set_next_toast_pg_type_oid); @@ -53,6 +62,8 @@ PG_FUNCTION_INFO_V1(set_next_toast_pg_class_oid); PG_FUNCTION_INFO_V1(set_next_pg_enum_oid); PG_FUNCTION_INFO_V1(set_next_pg_authid_oid); +PG_FUNCTION_INFO_V1(create_empty_extension); + Datum set_next_pg_type_oid(PG_FUNCTION_ARGS) @@ -133,3 +144,56 @@ set_next_pg_authid_oid(PG_FUNCTION_ARGS) PG_RETURN_VOID(); } +Datum +create_empty_extension(PG_FUNCTION_ARGS) +{ + text *extName = PG_GETARG_TEXT_PP(0); + text *schemaName = PG_GETARG_TEXT_PP(1); + bool relocatable = PG_GETARG_BOOL(2); + text *extVersion = PG_GETARG_TEXT_PP(3); + Datum extConfig; + Datum extCondition; + List *requiredExtensions; + + if (PG_ARGISNULL(4)) + extConfig = PointerGetDatum(NULL); + else + extConfig = PG_GETARG_DATUM(4); + + if (PG_ARGISNULL(5)) + extCondition = PointerGetDatum(NULL); + else + extCondition = PG_GETARG_DATUM(5); + + requiredExtensions = NIL; + if (!PG_ARGISNULL(6)) + { + ArrayType *textArray = PG_GETARG_ARRAYTYPE_P(6); + Datum *textDatums; + int ndatums; + int i; + + deconstruct_array(textArray, + TEXTOID, -1, false, 'i', + &textDatums, NULL, &ndatums); + for (i = 0; i < ndatums; i++) + { + text *txtname = DatumGetTextPP(textDatums[i]); + char *extName = text_to_cstring(txtname); + Oid extOid = get_extension_oid(extName, false); + + requiredExtensions = lappend_oid(requiredExtensions, extOid); + } + } + + InsertExtensionTuple(text_to_cstring(extName), + GetUserId(), + get_namespace_oid(text_to_cstring(schemaName), false), + relocatable, + text_to_cstring(extVersion), + extConfig, + extCondition, + requiredExtensions); + + PG_RETURN_VOID(); +} diff --git a/contrib/pgcrypto/.gitignore b/contrib/pgcrypto/.gitignore index 07b24d98f0..19b6c5ba42 100644 --- a/contrib/pgcrypto/.gitignore +++ b/contrib/pgcrypto/.gitignore @@ -1,3 +1,2 @@ -/pgcrypto.sql # Generated subdirectories /results/ diff --git a/contrib/pgcrypto/Makefile b/contrib/pgcrypto/Makefile index f429fab4ed..dadec953c2 100644 --- a/contrib/pgcrypto/Makefile +++ b/contrib/pgcrypto/Makefile @@ -1,6 +1,4 @@ -# # contrib/pgcrypto/Makefile -# INT_SRCS = md5.c sha1.c sha2.c internal.c internal-sha2.c blf.c rijndael.c \ fortuna.c random.c pgp-mpi-internal.c imath.c @@ -26,9 +24,9 @@ SRCS = pgcrypto.c px.c px-hmac.c px-crypt.c \ MODULE_big = pgcrypto OBJS = $(SRCS:.c=.o) -DATA_built = pgcrypto.sql -DATA = uninstall_pgcrypto.sql -EXTRA_CLEAN = gen-rtab + +EXTENSION = pgcrypto +DATA = pgcrypto--1.0.sql pgcrypto--unpackaged--1.0.sql REGRESS = init md5 sha1 hmac-md5 hmac-sha1 blowfish rijndael \ $(CF_TESTS) \ @@ -36,6 +34,7 @@ REGRESS = init md5 sha1 hmac-md5 hmac-sha1 blowfish rijndael \ pgp-armor pgp-decrypt pgp-encrypt $(CF_PGP_TESTS) \ pgp-pubkey-decrypt pgp-pubkey-encrypt pgp-info +EXTRA_CLEAN = gen-rtab ifdef USE_PGXS PG_CONFIG = pg_config diff --git a/contrib/pgcrypto/expected/init.out b/contrib/pgcrypto/expected/init.out index 4cb1081997..bd8f8e1380 100644 --- a/contrib/pgcrypto/expected/init.out +++ b/contrib/pgcrypto/expected/init.out @@ -1,13 +1,7 @@ -- -- init pgcrypto -- --- --- first, define the functions. Turn off echoing so that expected file --- does not depend on contents of pgcrypto.sql. --- -SET client_min_messages = warning; -\set ECHO none -RESET client_min_messages; +CREATE EXTENSION pgcrypto; -- ensure consistent test output regardless of the default bytea format SET bytea_output TO escape; -- check for encoding fn's diff --git a/contrib/pgcrypto/pgcrypto.sql.in b/contrib/pgcrypto/pgcrypto--1.0.sql index 37ae100412..52be0950ce 100644 --- a/contrib/pgcrypto/pgcrypto.sql.in +++ b/contrib/pgcrypto/pgcrypto--1.0.sql @@ -1,64 +1,61 @@ -/* contrib/pgcrypto/pgcrypto.sql.in */ +/* contrib/pgcrypto/pgcrypto--1.0.sql */ --- Adjust this setting to control where the objects get created. -SET search_path = public; - -CREATE OR REPLACE FUNCTION digest(text, text) +CREATE FUNCTION digest(text, text) RETURNS bytea AS 'MODULE_PATHNAME', 'pg_digest' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION digest(bytea, text) +CREATE FUNCTION digest(bytea, text) RETURNS bytea AS 'MODULE_PATHNAME', 'pg_digest' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION hmac(text, text, text) +CREATE FUNCTION hmac(text, text, text) RETURNS bytea AS 'MODULE_PATHNAME', 'pg_hmac' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION hmac(bytea, bytea, text) +CREATE FUNCTION hmac(bytea, bytea, text) RETURNS bytea AS 'MODULE_PATHNAME', 'pg_hmac' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION crypt(text, text) +CREATE FUNCTION crypt(text, text) RETURNS text AS 'MODULE_PATHNAME', 'pg_crypt' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gen_salt(text) +CREATE FUNCTION gen_salt(text) RETURNS text AS 'MODULE_PATHNAME', 'pg_gen_salt' LANGUAGE C VOLATILE STRICT; -CREATE OR REPLACE FUNCTION gen_salt(text, int4) +CREATE FUNCTION gen_salt(text, int4) RETURNS text AS 'MODULE_PATHNAME', 'pg_gen_salt_rounds' LANGUAGE C VOLATILE STRICT; -CREATE OR REPLACE FUNCTION encrypt(bytea, bytea, text) +CREATE FUNCTION encrypt(bytea, bytea, text) RETURNS bytea AS 'MODULE_PATHNAME', 'pg_encrypt' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION decrypt(bytea, bytea, text) +CREATE FUNCTION decrypt(bytea, bytea, text) RETURNS bytea AS 'MODULE_PATHNAME', 'pg_decrypt' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION encrypt_iv(bytea, bytea, bytea, text) +CREATE FUNCTION encrypt_iv(bytea, bytea, bytea, text) RETURNS bytea AS 'MODULE_PATHNAME', 'pg_encrypt_iv' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION decrypt_iv(bytea, bytea, bytea, text) +CREATE FUNCTION decrypt_iv(bytea, bytea, bytea, text) RETURNS bytea AS 'MODULE_PATHNAME', 'pg_decrypt_iv' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gen_random_bytes(int4) +CREATE FUNCTION gen_random_bytes(int4) RETURNS bytea AS 'MODULE_PATHNAME', 'pg_random_bytes' LANGUAGE 'C' VOLATILE STRICT; @@ -66,12 +63,12 @@ LANGUAGE 'C' VOLATILE STRICT; -- -- pgp_sym_encrypt(data, key) -- -CREATE OR REPLACE FUNCTION pgp_sym_encrypt(text, text) +CREATE FUNCTION pgp_sym_encrypt(text, text) RETURNS bytea AS 'MODULE_PATHNAME', 'pgp_sym_encrypt_text' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION pgp_sym_encrypt_bytea(bytea, text) +CREATE FUNCTION pgp_sym_encrypt_bytea(bytea, text) RETURNS bytea AS 'MODULE_PATHNAME', 'pgp_sym_encrypt_bytea' LANGUAGE C STRICT; @@ -79,12 +76,12 @@ LANGUAGE C STRICT; -- -- pgp_sym_encrypt(data, key, args) -- -CREATE OR REPLACE FUNCTION pgp_sym_encrypt(text, text, text) +CREATE FUNCTION pgp_sym_encrypt(text, text, text) RETURNS bytea AS 'MODULE_PATHNAME', 'pgp_sym_encrypt_text' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION pgp_sym_encrypt_bytea(bytea, text, text) +CREATE FUNCTION pgp_sym_encrypt_bytea(bytea, text, text) RETURNS bytea AS 'MODULE_PATHNAME', 'pgp_sym_encrypt_bytea' LANGUAGE C STRICT; @@ -92,12 +89,12 @@ LANGUAGE C STRICT; -- -- pgp_sym_decrypt(data, key) -- -CREATE OR REPLACE FUNCTION pgp_sym_decrypt(bytea, text) +CREATE FUNCTION pgp_sym_decrypt(bytea, text) RETURNS text AS 'MODULE_PATHNAME', 'pgp_sym_decrypt_text' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION pgp_sym_decrypt_bytea(bytea, text) +CREATE FUNCTION pgp_sym_decrypt_bytea(bytea, text) RETURNS bytea AS 'MODULE_PATHNAME', 'pgp_sym_decrypt_bytea' LANGUAGE C IMMUTABLE STRICT; @@ -105,12 +102,12 @@ LANGUAGE C IMMUTABLE STRICT; -- -- pgp_sym_decrypt(data, key, args) -- -CREATE OR REPLACE FUNCTION pgp_sym_decrypt(bytea, text, text) +CREATE FUNCTION pgp_sym_decrypt(bytea, text, text) RETURNS text AS 'MODULE_PATHNAME', 'pgp_sym_decrypt_text' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION pgp_sym_decrypt_bytea(bytea, text, text) +CREATE FUNCTION pgp_sym_decrypt_bytea(bytea, text, text) RETURNS bytea AS 'MODULE_PATHNAME', 'pgp_sym_decrypt_bytea' LANGUAGE C IMMUTABLE STRICT; @@ -118,12 +115,12 @@ LANGUAGE C IMMUTABLE STRICT; -- -- pgp_pub_encrypt(data, key) -- -CREATE OR REPLACE FUNCTION pgp_pub_encrypt(text, bytea) +CREATE FUNCTION pgp_pub_encrypt(text, bytea) RETURNS bytea AS 'MODULE_PATHNAME', 'pgp_pub_encrypt_text' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION pgp_pub_encrypt_bytea(bytea, bytea) +CREATE FUNCTION pgp_pub_encrypt_bytea(bytea, bytea) RETURNS bytea AS 'MODULE_PATHNAME', 'pgp_pub_encrypt_bytea' LANGUAGE C STRICT; @@ -131,12 +128,12 @@ LANGUAGE C STRICT; -- -- pgp_pub_encrypt(data, key, args) -- -CREATE OR REPLACE FUNCTION pgp_pub_encrypt(text, bytea, text) +CREATE FUNCTION pgp_pub_encrypt(text, bytea, text) RETURNS bytea AS 'MODULE_PATHNAME', 'pgp_pub_encrypt_text' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION pgp_pub_encrypt_bytea(bytea, bytea, text) +CREATE FUNCTION pgp_pub_encrypt_bytea(bytea, bytea, text) RETURNS bytea AS 'MODULE_PATHNAME', 'pgp_pub_encrypt_bytea' LANGUAGE C STRICT; @@ -144,12 +141,12 @@ LANGUAGE C STRICT; -- -- pgp_pub_decrypt(data, key) -- -CREATE OR REPLACE FUNCTION pgp_pub_decrypt(bytea, bytea) +CREATE FUNCTION pgp_pub_decrypt(bytea, bytea) RETURNS text AS 'MODULE_PATHNAME', 'pgp_pub_decrypt_text' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION pgp_pub_decrypt_bytea(bytea, bytea) +CREATE FUNCTION pgp_pub_decrypt_bytea(bytea, bytea) RETURNS bytea AS 'MODULE_PATHNAME', 'pgp_pub_decrypt_bytea' LANGUAGE C IMMUTABLE STRICT; @@ -157,12 +154,12 @@ LANGUAGE C IMMUTABLE STRICT; -- -- pgp_pub_decrypt(data, key, psw) -- -CREATE OR REPLACE FUNCTION pgp_pub_decrypt(bytea, bytea, text) +CREATE FUNCTION pgp_pub_decrypt(bytea, bytea, text) RETURNS text AS 'MODULE_PATHNAME', 'pgp_pub_decrypt_text' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION pgp_pub_decrypt_bytea(bytea, bytea, text) +CREATE FUNCTION pgp_pub_decrypt_bytea(bytea, bytea, text) RETURNS bytea AS 'MODULE_PATHNAME', 'pgp_pub_decrypt_bytea' LANGUAGE C IMMUTABLE STRICT; @@ -170,12 +167,12 @@ LANGUAGE C IMMUTABLE STRICT; -- -- pgp_pub_decrypt(data, key, psw, arg) -- -CREATE OR REPLACE FUNCTION pgp_pub_decrypt(bytea, bytea, text, text) +CREATE FUNCTION pgp_pub_decrypt(bytea, bytea, text, text) RETURNS text AS 'MODULE_PATHNAME', 'pgp_pub_decrypt_text' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION pgp_pub_decrypt_bytea(bytea, bytea, text, text) +CREATE FUNCTION pgp_pub_decrypt_bytea(bytea, bytea, text, text) RETURNS bytea AS 'MODULE_PATHNAME', 'pgp_pub_decrypt_bytea' LANGUAGE C IMMUTABLE STRICT; @@ -183,7 +180,7 @@ LANGUAGE C IMMUTABLE STRICT; -- -- PGP key ID -- -CREATE OR REPLACE FUNCTION pgp_key_id(bytea) +CREATE FUNCTION pgp_key_id(bytea) RETURNS text AS 'MODULE_PATHNAME', 'pgp_key_id_w' LANGUAGE C IMMUTABLE STRICT; @@ -191,12 +188,12 @@ LANGUAGE C IMMUTABLE STRICT; -- -- pgp armor -- -CREATE OR REPLACE FUNCTION armor(bytea) +CREATE FUNCTION armor(bytea) RETURNS text AS 'MODULE_PATHNAME', 'pg_armor' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION dearmor(text) +CREATE FUNCTION dearmor(text) RETURNS bytea AS 'MODULE_PATHNAME', 'pg_dearmor' LANGUAGE C IMMUTABLE STRICT; diff --git a/contrib/pgcrypto/pgcrypto--unpackaged--1.0.sql b/contrib/pgcrypto/pgcrypto--unpackaged--1.0.sql new file mode 100644 index 0000000000..64f0cdf23a --- /dev/null +++ b/contrib/pgcrypto/pgcrypto--unpackaged--1.0.sql @@ -0,0 +1,35 @@ +/* contrib/pgcrypto/pgcrypto--unpackaged--1.0.sql */ + +ALTER EXTENSION pgcrypto ADD function digest(text,text); +ALTER EXTENSION pgcrypto ADD function digest(bytea,text); +ALTER EXTENSION pgcrypto ADD function hmac(text,text,text); +ALTER EXTENSION pgcrypto ADD function hmac(bytea,bytea,text); +ALTER EXTENSION pgcrypto ADD function crypt(text,text); +ALTER EXTENSION pgcrypto ADD function gen_salt(text); +ALTER EXTENSION pgcrypto ADD function gen_salt(text,integer); +ALTER EXTENSION pgcrypto ADD function encrypt(bytea,bytea,text); +ALTER EXTENSION pgcrypto ADD function decrypt(bytea,bytea,text); +ALTER EXTENSION pgcrypto ADD function encrypt_iv(bytea,bytea,bytea,text); +ALTER EXTENSION pgcrypto ADD function decrypt_iv(bytea,bytea,bytea,text); +ALTER EXTENSION pgcrypto ADD function gen_random_bytes(integer); +ALTER EXTENSION pgcrypto ADD function pgp_sym_encrypt(text,text); +ALTER EXTENSION pgcrypto ADD function pgp_sym_encrypt_bytea(bytea,text); +ALTER EXTENSION pgcrypto ADD function pgp_sym_encrypt(text,text,text); +ALTER EXTENSION pgcrypto ADD function pgp_sym_encrypt_bytea(bytea,text,text); +ALTER EXTENSION pgcrypto ADD function pgp_sym_decrypt(bytea,text); +ALTER EXTENSION pgcrypto ADD function pgp_sym_decrypt_bytea(bytea,text); +ALTER EXTENSION pgcrypto ADD function pgp_sym_decrypt(bytea,text,text); +ALTER EXTENSION pgcrypto ADD function pgp_sym_decrypt_bytea(bytea,text,text); +ALTER EXTENSION pgcrypto ADD function pgp_pub_encrypt(text,bytea); +ALTER EXTENSION pgcrypto ADD function pgp_pub_encrypt_bytea(bytea,bytea); +ALTER EXTENSION pgcrypto ADD function pgp_pub_encrypt(text,bytea,text); +ALTER EXTENSION pgcrypto ADD function pgp_pub_encrypt_bytea(bytea,bytea,text); +ALTER EXTENSION pgcrypto ADD function pgp_pub_decrypt(bytea,bytea); +ALTER EXTENSION pgcrypto ADD function pgp_pub_decrypt_bytea(bytea,bytea); +ALTER EXTENSION pgcrypto ADD function pgp_pub_decrypt(bytea,bytea,text); +ALTER EXTENSION pgcrypto ADD function pgp_pub_decrypt_bytea(bytea,bytea,text); +ALTER EXTENSION pgcrypto ADD function pgp_pub_decrypt(bytea,bytea,text,text); +ALTER EXTENSION pgcrypto ADD function pgp_pub_decrypt_bytea(bytea,bytea,text,text); +ALTER EXTENSION pgcrypto ADD function pgp_key_id(bytea); +ALTER EXTENSION pgcrypto ADD function armor(bytea); +ALTER EXTENSION pgcrypto ADD function dearmor(text); diff --git a/contrib/pgcrypto/pgcrypto.control b/contrib/pgcrypto/pgcrypto.control new file mode 100644 index 0000000000..8375cf9e7b --- /dev/null +++ b/contrib/pgcrypto/pgcrypto.control @@ -0,0 +1,5 @@ +# pgcrypto extension +comment = 'cryptographic functions' +default_version = '1.0' +module_pathname = '$libdir/pgcrypto' +relocatable = true diff --git a/contrib/pgcrypto/sql/init.sql b/contrib/pgcrypto/sql/init.sql index 8c1d2192a6..5c3d100576 100644 --- a/contrib/pgcrypto/sql/init.sql +++ b/contrib/pgcrypto/sql/init.sql @@ -2,15 +2,7 @@ -- init pgcrypto -- --- --- first, define the functions. Turn off echoing so that expected file --- does not depend on contents of pgcrypto.sql. --- -SET client_min_messages = warning; -\set ECHO none -\i pgcrypto.sql -\set ECHO all -RESET client_min_messages; +CREATE EXTENSION pgcrypto; -- ensure consistent test output regardless of the default bytea format SET bytea_output TO escape; diff --git a/contrib/pgcrypto/uninstall_pgcrypto.sql b/contrib/pgcrypto/uninstall_pgcrypto.sql deleted file mode 100644 index 3005c50333..0000000000 --- a/contrib/pgcrypto/uninstall_pgcrypto.sql +++ /dev/null @@ -1,45 +0,0 @@ -/* contrib/pgcrypto/uninstall_pgcrypto.sql */ - --- Adjust this setting to control where the objects get dropped. -SET search_path = public; - -DROP FUNCTION digest(text, text); -DROP FUNCTION digest(bytea, text); - -DROP FUNCTION hmac(text, text, text); -DROP FUNCTION hmac(bytea, bytea, text); - -DROP FUNCTION crypt(text, text); -DROP FUNCTION gen_salt(text); -DROP FUNCTION gen_salt(text, int4); - -DROP FUNCTION encrypt(bytea, bytea, text); -DROP FUNCTION decrypt(bytea, bytea, text); -DROP FUNCTION encrypt_iv(bytea, bytea, bytea, text); -DROP FUNCTION decrypt_iv(bytea, bytea, bytea, text); - -DROP FUNCTION gen_random_bytes(int4); - -DROP FUNCTION pgp_sym_encrypt(text, text); -DROP FUNCTION pgp_sym_encrypt_bytea(bytea, text); -DROP FUNCTION pgp_sym_encrypt(text, text, text); -DROP FUNCTION pgp_sym_encrypt_bytea(bytea, text, text); -DROP FUNCTION pgp_sym_decrypt(bytea, text); -DROP FUNCTION pgp_sym_decrypt_bytea(bytea, text); -DROP FUNCTION pgp_sym_decrypt(bytea, text, text); -DROP FUNCTION pgp_sym_decrypt_bytea(bytea, text, text); - -DROP FUNCTION pgp_pub_encrypt(text, bytea); -DROP FUNCTION pgp_pub_encrypt_bytea(bytea, bytea); -DROP FUNCTION pgp_pub_encrypt(text, bytea, text); -DROP FUNCTION pgp_pub_encrypt_bytea(bytea, bytea, text); -DROP FUNCTION pgp_pub_decrypt(bytea, bytea); -DROP FUNCTION pgp_pub_decrypt_bytea(bytea, bytea); -DROP FUNCTION pgp_pub_decrypt(bytea, bytea, text); -DROP FUNCTION pgp_pub_decrypt_bytea(bytea, bytea, text); -DROP FUNCTION pgp_pub_decrypt(bytea, bytea, text, text); -DROP FUNCTION pgp_pub_decrypt_bytea(bytea, bytea, text, text); - -DROP FUNCTION pgp_key_id(bytea); -DROP FUNCTION armor(bytea); -DROP FUNCTION dearmor(text); diff --git a/contrib/pgrowlocks/.gitignore b/contrib/pgrowlocks/.gitignore deleted file mode 100644 index b2729282bf..0000000000 --- a/contrib/pgrowlocks/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/pgrowlocks.sql diff --git a/contrib/pgrowlocks/Makefile b/contrib/pgrowlocks/Makefile index fd338d75d7..f56389b0e2 100644 --- a/contrib/pgrowlocks/Makefile +++ b/contrib/pgrowlocks/Makefile @@ -1,15 +1,10 @@ -#------------------------------------------------------------------------- -# -# pgrowlocks Makefile -# # contrib/pgrowlocks/Makefile -# -#------------------------------------------------------------------------- MODULE_big = pgrowlocks OBJS = pgrowlocks.o -DATA_built = pgrowlocks.sql -DATA = uninstall_pgrowlocks.sql + +EXTENSION = pgrowlocks +DATA = pgrowlocks--1.0.sql pgrowlocks--unpackaged--1.0.sql ifdef USE_PGXS PG_CONFIG = pg_config diff --git a/contrib/pgrowlocks/pgrowlocks.sql.in b/contrib/pgrowlocks/pgrowlocks--1.0.sql index 3bcb3ee7ea..0b60fdcd07 100644 --- a/contrib/pgrowlocks/pgrowlocks.sql.in +++ b/contrib/pgrowlocks/pgrowlocks--1.0.sql @@ -1,9 +1,6 @@ -/* contrib/pgrowlocks/pgrowlocks.sql.in */ +/* contrib/pgrowlocks/pgrowlocks--1.0.sql */ --- Adjust this setting to control where the objects get created. -SET search_path = public; - -CREATE OR REPLACE FUNCTION pgrowlocks(IN relname text, +CREATE FUNCTION pgrowlocks(IN relname text, OUT locked_row TID, -- row TID OUT lock_type TEXT, -- lock type OUT locker XID, -- locking XID diff --git a/contrib/pgrowlocks/pgrowlocks--unpackaged--1.0.sql b/contrib/pgrowlocks/pgrowlocks--unpackaged--1.0.sql new file mode 100644 index 0000000000..2d9d1eed41 --- /dev/null +++ b/contrib/pgrowlocks/pgrowlocks--unpackaged--1.0.sql @@ -0,0 +1,3 @@ +/* contrib/pgrowlocks/pgrowlocks--unpackaged--1.0.sql */ + +ALTER EXTENSION pgrowlocks ADD function pgrowlocks(text); diff --git a/contrib/pgrowlocks/pgrowlocks.control b/contrib/pgrowlocks/pgrowlocks.control new file mode 100644 index 0000000000..a6ba164515 --- /dev/null +++ b/contrib/pgrowlocks/pgrowlocks.control @@ -0,0 +1,5 @@ +# pgrowlocks extension +comment = 'show row-level locking information' +default_version = '1.0' +module_pathname = '$libdir/pgrowlocks' +relocatable = true diff --git a/contrib/pgrowlocks/uninstall_pgrowlocks.sql b/contrib/pgrowlocks/uninstall_pgrowlocks.sql deleted file mode 100644 index 004e97c0e9..0000000000 --- a/contrib/pgrowlocks/uninstall_pgrowlocks.sql +++ /dev/null @@ -1,6 +0,0 @@ -/* contrib/pgrowlocks/uninstall_pgrowlocks.sql */ - --- Adjust this setting to control where the objects get dropped. -SET search_path = public; - -DROP FUNCTION pgrowlocks(text); diff --git a/contrib/pgstattuple/.gitignore b/contrib/pgstattuple/.gitignore deleted file mode 100644 index 69b22b64cd..0000000000 --- a/contrib/pgstattuple/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/pgstattuple.sql diff --git a/contrib/pgstattuple/Makefile b/contrib/pgstattuple/Makefile index 33386cdf17..13b87090ee 100644 --- a/contrib/pgstattuple/Makefile +++ b/contrib/pgstattuple/Makefile @@ -1,15 +1,10 @@ -#------------------------------------------------------------------------- -# -# pgstattuple Makefile -# # contrib/pgstattuple/Makefile -# -#------------------------------------------------------------------------- MODULE_big = pgstattuple OBJS = pgstattuple.o pgstatindex.o -DATA_built = pgstattuple.sql -DATA = uninstall_pgstattuple.sql + +EXTENSION = pgstattuple +DATA = pgstattuple--1.0.sql pgstattuple--unpackaged--1.0.sql ifdef USE_PGXS PG_CONFIG = pg_config diff --git a/contrib/pgstattuple/pgstattuple.sql.in b/contrib/pgstattuple/pgstattuple--1.0.sql index 6a09136596..83445ec4ae 100644 --- a/contrib/pgstattuple/pgstattuple.sql.in +++ b/contrib/pgstattuple/pgstattuple--1.0.sql @@ -1,9 +1,6 @@ -/* contrib/pgstattuple/pgstattuple.sql.in */ +/* contrib/pgstattuple/pgstattuple--1.0.sql */ --- Adjust this setting to control where the objects get created. -SET search_path = public; - -CREATE OR REPLACE FUNCTION pgstattuple(IN relname text, +CREATE FUNCTION pgstattuple(IN relname text, OUT table_len BIGINT, -- physical table length in bytes OUT tuple_count BIGINT, -- number of live tuples OUT tuple_len BIGINT, -- total tuples length in bytes @@ -16,7 +13,7 @@ CREATE OR REPLACE FUNCTION pgstattuple(IN relname text, AS 'MODULE_PATHNAME', 'pgstattuple' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION pgstattuple(IN reloid oid, +CREATE FUNCTION pgstattuple(IN reloid oid, OUT table_len BIGINT, -- physical table length in bytes OUT tuple_count BIGINT, -- number of live tuples OUT tuple_len BIGINT, -- total tuples length in bytes @@ -29,7 +26,7 @@ CREATE OR REPLACE FUNCTION pgstattuple(IN reloid oid, AS 'MODULE_PATHNAME', 'pgstattuplebyid' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION pgstatindex(IN relname text, +CREATE FUNCTION pgstatindex(IN relname text, OUT version INT, OUT tree_level INT, OUT index_size BIGINT, @@ -43,7 +40,7 @@ CREATE OR REPLACE FUNCTION pgstatindex(IN relname text, AS 'MODULE_PATHNAME', 'pgstatindex' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION pg_relpages(IN relname text) +CREATE FUNCTION pg_relpages(IN relname text) RETURNS BIGINT AS 'MODULE_PATHNAME', 'pg_relpages' LANGUAGE C STRICT; diff --git a/contrib/pgstattuple/pgstattuple--unpackaged--1.0.sql b/contrib/pgstattuple/pgstattuple--unpackaged--1.0.sql new file mode 100644 index 0000000000..3cfb8db534 --- /dev/null +++ b/contrib/pgstattuple/pgstattuple--unpackaged--1.0.sql @@ -0,0 +1,6 @@ +/* contrib/pgstattuple/pgstattuple--unpackaged--1.0.sql */ + +ALTER EXTENSION pgstattuple ADD function pgstattuple(text); +ALTER EXTENSION pgstattuple ADD function pgstattuple(oid); +ALTER EXTENSION pgstattuple ADD function pgstatindex(text); +ALTER EXTENSION pgstattuple ADD function pg_relpages(text); diff --git a/contrib/pgstattuple/pgstattuple.control b/contrib/pgstattuple/pgstattuple.control new file mode 100644 index 0000000000..7b5129b2f2 --- /dev/null +++ b/contrib/pgstattuple/pgstattuple.control @@ -0,0 +1,5 @@ +# pgstattuple extension +comment = 'show tuple-level statistics' +default_version = '1.0' +module_pathname = '$libdir/pgstattuple' +relocatable = true diff --git a/contrib/pgstattuple/uninstall_pgstattuple.sql b/contrib/pgstattuple/uninstall_pgstattuple.sql deleted file mode 100644 index 29eac40f29..0000000000 --- a/contrib/pgstattuple/uninstall_pgstattuple.sql +++ /dev/null @@ -1,9 +0,0 @@ -/* contrib/pgstattuple/uninstall_pgstattuple.sql */ - --- Adjust this setting to control where the objects get dropped. -SET search_path = public; - -DROP FUNCTION pgstattuple(text); -DROP FUNCTION pgstattuple(oid); -DROP FUNCTION pgstatindex(text); -DROP FUNCTION pg_relpages(text); diff --git a/contrib/seg/.gitignore b/contrib/seg/.gitignore index a8973ff696..102f8b3246 100644 --- a/contrib/seg/.gitignore +++ b/contrib/seg/.gitignore @@ -1,5 +1,4 @@ /segparse.c /segscan.c -/seg.sql # Generated subdirectories /results/ diff --git a/contrib/seg/Makefile b/contrib/seg/Makefile index e8c7a44845..d84934c67f 100644 --- a/contrib/seg/Makefile +++ b/contrib/seg/Makefile @@ -2,8 +2,10 @@ MODULE_big = seg OBJS = seg.o segparse.o -DATA_built = seg.sql -DATA = uninstall_seg.sql + +EXTENSION = seg +DATA = seg--1.0.sql seg--unpackaged--1.0.sql + REGRESS = seg EXTRA_CLEAN = y.tab.c y.tab.h diff --git a/contrib/seg/expected/seg.out b/contrib/seg/expected/seg.out index 17c803e50e..1f82a4abb8 100644 --- a/contrib/seg/expected/seg.out +++ b/contrib/seg/expected/seg.out @@ -1,13 +1,7 @@ -- -- Test seg datatype -- --- --- first, define the datatype. Turn off echoing so that expected file --- does not depend on contents of seg.sql. --- -SET client_min_messages = warning; -\set ECHO none -RESET client_min_messages; +CREATE EXTENSION seg; -- -- testing the input and output functions -- diff --git a/contrib/seg/expected/seg_1.out b/contrib/seg/expected/seg_1.out index a4cca8b391..563c744b2d 100644 --- a/contrib/seg/expected/seg_1.out +++ b/contrib/seg/expected/seg_1.out @@ -1,13 +1,7 @@ -- -- Test seg datatype -- --- --- first, define the datatype. Turn off echoing so that expected file --- does not depend on contents of seg.sql. --- -SET client_min_messages = warning; -\set ECHO none -RESET client_min_messages; +CREATE EXTENSION seg; -- -- testing the input and output functions -- diff --git a/contrib/seg/seg.sql.in b/contrib/seg/seg--1.0.sql index 9bd747656c..563411da6a 100644 --- a/contrib/seg/seg.sql.in +++ b/contrib/seg/seg--1.0.sql @@ -1,17 +1,13 @@ -/* contrib/seg/seg.sql.in */ - --- Adjust this setting to control where the objects get created. -SET search_path = public; +/* contrib/seg/seg--1.0.sql */ -- Create the user-defined type for 1-D floating point intervals (seg) --- -CREATE OR REPLACE FUNCTION seg_in(cstring) +CREATE FUNCTION seg_in(cstring) RETURNS seg AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION seg_out(seg) +CREATE FUNCTION seg_out(seg) RETURNS cstring AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -31,7 +27,7 @@ COMMENT ON TYPE seg IS -- Left/Right methods -CREATE OR REPLACE FUNCTION seg_over_left(seg, seg) +CREATE FUNCTION seg_over_left(seg, seg) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -39,7 +35,7 @@ LANGUAGE C STRICT IMMUTABLE; COMMENT ON FUNCTION seg_over_left(seg, seg) IS 'overlaps or is left of'; -CREATE OR REPLACE FUNCTION seg_over_right(seg, seg) +CREATE FUNCTION seg_over_right(seg, seg) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -47,7 +43,7 @@ LANGUAGE C STRICT IMMUTABLE; COMMENT ON FUNCTION seg_over_right(seg, seg) IS 'overlaps or is right of'; -CREATE OR REPLACE FUNCTION seg_left(seg, seg) +CREATE FUNCTION seg_left(seg, seg) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -55,7 +51,7 @@ LANGUAGE C STRICT IMMUTABLE; COMMENT ON FUNCTION seg_left(seg, seg) IS 'is left of'; -CREATE OR REPLACE FUNCTION seg_right(seg, seg) +CREATE FUNCTION seg_right(seg, seg) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -66,7 +62,7 @@ COMMENT ON FUNCTION seg_right(seg, seg) IS -- Scalar comparison methods -CREATE OR REPLACE FUNCTION seg_lt(seg, seg) +CREATE FUNCTION seg_lt(seg, seg) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -74,7 +70,7 @@ LANGUAGE C STRICT IMMUTABLE; COMMENT ON FUNCTION seg_lt(seg, seg) IS 'less than'; -CREATE OR REPLACE FUNCTION seg_le(seg, seg) +CREATE FUNCTION seg_le(seg, seg) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -82,7 +78,7 @@ LANGUAGE C STRICT IMMUTABLE; COMMENT ON FUNCTION seg_le(seg, seg) IS 'less than or equal'; -CREATE OR REPLACE FUNCTION seg_gt(seg, seg) +CREATE FUNCTION seg_gt(seg, seg) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -90,7 +86,7 @@ LANGUAGE C STRICT IMMUTABLE; COMMENT ON FUNCTION seg_gt(seg, seg) IS 'greater than'; -CREATE OR REPLACE FUNCTION seg_ge(seg, seg) +CREATE FUNCTION seg_ge(seg, seg) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -98,7 +94,7 @@ LANGUAGE C STRICT IMMUTABLE; COMMENT ON FUNCTION seg_ge(seg, seg) IS 'greater than or equal'; -CREATE OR REPLACE FUNCTION seg_contains(seg, seg) +CREATE FUNCTION seg_contains(seg, seg) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -106,7 +102,7 @@ LANGUAGE C STRICT IMMUTABLE; COMMENT ON FUNCTION seg_contains(seg, seg) IS 'contains'; -CREATE OR REPLACE FUNCTION seg_contained(seg, seg) +CREATE FUNCTION seg_contained(seg, seg) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -114,7 +110,7 @@ LANGUAGE C STRICT IMMUTABLE; COMMENT ON FUNCTION seg_contained(seg, seg) IS 'contained in'; -CREATE OR REPLACE FUNCTION seg_overlap(seg, seg) +CREATE FUNCTION seg_overlap(seg, seg) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -122,7 +118,7 @@ LANGUAGE C STRICT IMMUTABLE; COMMENT ON FUNCTION seg_overlap(seg, seg) IS 'overlaps'; -CREATE OR REPLACE FUNCTION seg_same(seg, seg) +CREATE FUNCTION seg_same(seg, seg) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -130,7 +126,7 @@ LANGUAGE C STRICT IMMUTABLE; COMMENT ON FUNCTION seg_same(seg, seg) IS 'same as'; -CREATE OR REPLACE FUNCTION seg_different(seg, seg) +CREATE FUNCTION seg_different(seg, seg) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -140,41 +136,41 @@ COMMENT ON FUNCTION seg_different(seg, seg) IS -- support routines for indexing -CREATE OR REPLACE FUNCTION seg_cmp(seg, seg) +CREATE FUNCTION seg_cmp(seg, seg) RETURNS int4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; COMMENT ON FUNCTION seg_cmp(seg, seg) IS 'btree comparison function'; -CREATE OR REPLACE FUNCTION seg_union(seg, seg) +CREATE FUNCTION seg_union(seg, seg) RETURNS seg AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION seg_inter(seg, seg) +CREATE FUNCTION seg_inter(seg, seg) RETURNS seg AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION seg_size(seg) +CREATE FUNCTION seg_size(seg) RETURNS float4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -- miscellaneous -CREATE OR REPLACE FUNCTION seg_center(seg) +CREATE FUNCTION seg_center(seg) RETURNS float4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION seg_upper(seg) +CREATE FUNCTION seg_upper(seg) RETURNS float4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION seg_lower(seg) +CREATE FUNCTION seg_lower(seg) RETURNS float4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; @@ -327,37 +323,37 @@ CREATE OPERATOR ~ ( -- define the GiST support methods -CREATE OR REPLACE FUNCTION gseg_consistent(internal,seg,int,oid,internal) +CREATE FUNCTION gseg_consistent(internal,seg,int,oid,internal) RETURNS bool AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gseg_compress(internal) +CREATE FUNCTION gseg_compress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gseg_decompress(internal) +CREATE FUNCTION gseg_decompress(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gseg_penalty(internal,internal,internal) +CREATE FUNCTION gseg_penalty(internal,internal,internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gseg_picksplit(internal, internal) +CREATE FUNCTION gseg_picksplit(internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gseg_union(internal, internal) +CREATE FUNCTION gseg_union(internal, internal) RETURNS seg AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; -CREATE OR REPLACE FUNCTION gseg_same(seg, seg, internal) +CREATE FUNCTION gseg_same(seg, seg, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; diff --git a/contrib/seg/seg--unpackaged--1.0.sql b/contrib/seg/seg--unpackaged--1.0.sql new file mode 100644 index 0000000000..9fefbfc9aa --- /dev/null +++ b/contrib/seg/seg--unpackaged--1.0.sql @@ -0,0 +1,51 @@ +/* contrib/seg/seg--unpackaged--1.0.sql */ + +ALTER EXTENSION seg ADD type seg; +ALTER EXTENSION seg ADD function seg_in(cstring); +ALTER EXTENSION seg ADD function seg_out(seg); +ALTER EXTENSION seg ADD function seg_over_left(seg,seg); +ALTER EXTENSION seg ADD function seg_over_right(seg,seg); +ALTER EXTENSION seg ADD function seg_left(seg,seg); +ALTER EXTENSION seg ADD function seg_right(seg,seg); +ALTER EXTENSION seg ADD function seg_lt(seg,seg); +ALTER EXTENSION seg ADD function seg_le(seg,seg); +ALTER EXTENSION seg ADD function seg_gt(seg,seg); +ALTER EXTENSION seg ADD function seg_ge(seg,seg); +ALTER EXTENSION seg ADD function seg_contains(seg,seg); +ALTER EXTENSION seg ADD function seg_contained(seg,seg); +ALTER EXTENSION seg ADD function seg_overlap(seg,seg); +ALTER EXTENSION seg ADD function seg_same(seg,seg); +ALTER EXTENSION seg ADD function seg_different(seg,seg); +ALTER EXTENSION seg ADD function seg_cmp(seg,seg); +ALTER EXTENSION seg ADD function seg_union(seg,seg); +ALTER EXTENSION seg ADD function seg_inter(seg,seg); +ALTER EXTENSION seg ADD function seg_size(seg); +ALTER EXTENSION seg ADD function seg_center(seg); +ALTER EXTENSION seg ADD function seg_upper(seg); +ALTER EXTENSION seg ADD function seg_lower(seg); +ALTER EXTENSION seg ADD operator >(seg,seg); +ALTER EXTENSION seg ADD operator >=(seg,seg); +ALTER EXTENSION seg ADD operator <(seg,seg); +ALTER EXTENSION seg ADD operator <=(seg,seg); +ALTER EXTENSION seg ADD operator >>(seg,seg); +ALTER EXTENSION seg ADD operator <<(seg,seg); +ALTER EXTENSION seg ADD operator &<(seg,seg); +ALTER EXTENSION seg ADD operator &&(seg,seg); +ALTER EXTENSION seg ADD operator &>(seg,seg); +ALTER EXTENSION seg ADD operator <>(seg,seg); +ALTER EXTENSION seg ADD operator =(seg,seg); +ALTER EXTENSION seg ADD operator <@(seg,seg); +ALTER EXTENSION seg ADD operator @>(seg,seg); +ALTER EXTENSION seg ADD operator ~(seg,seg); +ALTER EXTENSION seg ADD operator @(seg,seg); +ALTER EXTENSION seg ADD function gseg_consistent(internal,seg,integer,oid,internal); +ALTER EXTENSION seg ADD function gseg_compress(internal); +ALTER EXTENSION seg ADD function gseg_decompress(internal); +ALTER EXTENSION seg ADD function gseg_penalty(internal,internal,internal); +ALTER EXTENSION seg ADD function gseg_picksplit(internal,internal); +ALTER EXTENSION seg ADD function gseg_union(internal,internal); +ALTER EXTENSION seg ADD function gseg_same(seg,seg,internal); +ALTER EXTENSION seg ADD operator family seg_ops using btree; +ALTER EXTENSION seg ADD operator class seg_ops using btree; +ALTER EXTENSION seg ADD operator family gist_seg_ops using gist; +ALTER EXTENSION seg ADD operator class gist_seg_ops using gist; diff --git a/contrib/seg/seg.control b/contrib/seg/seg.control new file mode 100644 index 0000000000..a1286962ee --- /dev/null +++ b/contrib/seg/seg.control @@ -0,0 +1,5 @@ +# seg extension +comment = 'data type for representing line segments or floating-point intervals' +default_version = '1.0' +module_pathname = '$libdir/seg' +relocatable = true diff --git a/contrib/seg/sql/seg.sql b/contrib/seg/sql/seg.sql index b8a29d659a..7b7f138dbf 100644 --- a/contrib/seg/sql/seg.sql +++ b/contrib/seg/sql/seg.sql @@ -2,15 +2,7 @@ -- Test seg datatype -- --- --- first, define the datatype. Turn off echoing so that expected file --- does not depend on contents of seg.sql. --- -SET client_min_messages = warning; -\set ECHO none -\i seg.sql -\set ECHO all -RESET client_min_messages; +CREATE EXTENSION seg; -- -- testing the input and output functions diff --git a/contrib/seg/uninstall_seg.sql b/contrib/seg/uninstall_seg.sql deleted file mode 100644 index 27e8ba901a..0000000000 --- a/contrib/seg/uninstall_seg.sql +++ /dev/null @@ -1,94 +0,0 @@ -/* contrib/seg/uninstall_seg.sql */ - --- Adjust this setting to control where the objects get dropped. -SET search_path = public; - -DROP OPERATOR CLASS gist_seg_ops USING gist; - -DROP OPERATOR CLASS seg_ops USING btree; - -DROP FUNCTION gseg_same(seg, seg, internal); - -DROP FUNCTION gseg_union(internal, internal); - -DROP FUNCTION gseg_picksplit(internal, internal); - -DROP FUNCTION gseg_penalty(internal,internal,internal); - -DROP FUNCTION gseg_decompress(internal); - -DROP FUNCTION gseg_compress(internal); - -DROP FUNCTION gseg_consistent(internal,seg,int,oid,internal); - -DROP OPERATOR <@ (seg, seg); - -DROP OPERATOR @> (seg, seg); - -DROP OPERATOR ~ (seg, seg); - -DROP OPERATOR @ (seg, seg); - -DROP OPERATOR <> (seg, seg); - -DROP OPERATOR = (seg, seg); - -DROP OPERATOR >> (seg, seg); - -DROP OPERATOR &> (seg, seg); - -DROP OPERATOR && (seg, seg); - -DROP OPERATOR &< (seg, seg); - -DROP OPERATOR << (seg, seg); - -DROP OPERATOR >= (seg, seg); - -DROP OPERATOR > (seg, seg); - -DROP OPERATOR <= (seg, seg); - -DROP OPERATOR < (seg, seg); - -DROP FUNCTION seg_center(seg); - -DROP FUNCTION seg_lower(seg); - -DROP FUNCTION seg_upper(seg); - -DROP FUNCTION seg_size(seg); - -DROP FUNCTION seg_inter(seg, seg); - -DROP FUNCTION seg_union(seg, seg); - -DROP FUNCTION seg_cmp(seg, seg); - -DROP FUNCTION seg_different(seg, seg); - -DROP FUNCTION seg_same(seg, seg); - -DROP FUNCTION seg_overlap(seg, seg); - -DROP FUNCTION seg_contained(seg, seg); - -DROP FUNCTION seg_contains(seg, seg); - -DROP FUNCTION seg_ge(seg, seg); - -DROP FUNCTION seg_gt(seg, seg); - -DROP FUNCTION seg_le(seg, seg); - -DROP FUNCTION seg_lt(seg, seg); - -DROP FUNCTION seg_right(seg, seg); - -DROP FUNCTION seg_left(seg, seg); - -DROP FUNCTION seg_over_right(seg, seg); - -DROP FUNCTION seg_over_left(seg, seg); - -DROP TYPE seg CASCADE; diff --git a/contrib/spi/.gitignore b/contrib/spi/.gitignore deleted file mode 100644 index 6c07a33b11..0000000000 --- a/contrib/spi/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -/autoinc.sql -/insert_username.sql -/moddatetime.sql -/refint.sql -/timetravel.sql diff --git a/contrib/spi/Makefile b/contrib/spi/Makefile index 531d406605..0c11bfcbbd 100644 --- a/contrib/spi/Makefile +++ b/contrib/spi/Makefile @@ -1,7 +1,15 @@ # contrib/spi/Makefile MODULES = autoinc insert_username moddatetime refint timetravel -DATA_built = $(addsuffix .sql, $(MODULES)) + +EXTENSION = autoinc insert_username moddatetime refint timetravel + +DATA = autoinc--1.0.sql autoinc--unpackaged--1.0.sql \ + insert_username--1.0.sql insert_username--unpackaged--1.0.sql \ + moddatetime--1.0.sql moddatetime--unpackaged--1.0.sql \ + refint--1.0.sql refint--unpackaged--1.0.sql \ + timetravel--1.0.sql timetravel--unpackaged--1.0.sql + DOCS = $(addsuffix .example, $(MODULES)) # this is needed for the regression tests; diff --git a/contrib/spi/autoinc--1.0.sql b/contrib/spi/autoinc--1.0.sql new file mode 100644 index 0000000000..9c9df9ce0b --- /dev/null +++ b/contrib/spi/autoinc--1.0.sql @@ -0,0 +1,6 @@ +/* contrib/spi/autoinc--1.0.sql */ + +CREATE FUNCTION autoinc() +RETURNS trigger +AS 'MODULE_PATHNAME' +LANGUAGE C; diff --git a/contrib/spi/autoinc--unpackaged--1.0.sql b/contrib/spi/autoinc--unpackaged--1.0.sql new file mode 100644 index 0000000000..232e9170fc --- /dev/null +++ b/contrib/spi/autoinc--unpackaged--1.0.sql @@ -0,0 +1,3 @@ +/* contrib/spi/autoinc--unpackaged--1.0.sql */ + +ALTER EXTENSION autoinc ADD function autoinc(); diff --git a/contrib/spi/autoinc.control b/contrib/spi/autoinc.control new file mode 100644 index 0000000000..1d7a8e53d4 --- /dev/null +++ b/contrib/spi/autoinc.control @@ -0,0 +1,5 @@ +# autoinc extension +comment = 'functions for autoincrementing fields' +default_version = '1.0' +module_pathname = '$libdir/autoinc' +relocatable = true diff --git a/contrib/spi/autoinc.sql.in b/contrib/spi/autoinc.sql.in deleted file mode 100644 index 1fa322f9c7..0000000000 --- a/contrib/spi/autoinc.sql.in +++ /dev/null @@ -1,9 +0,0 @@ -/* contrib/spi/autoinc.sql.in */ - --- Adjust this setting to control where the objects get created. -SET search_path = public; - -CREATE OR REPLACE FUNCTION autoinc() -RETURNS trigger -AS 'MODULE_PATHNAME' -LANGUAGE C; diff --git a/contrib/spi/insert_username--1.0.sql b/contrib/spi/insert_username--1.0.sql new file mode 100644 index 0000000000..bd854587e0 --- /dev/null +++ b/contrib/spi/insert_username--1.0.sql @@ -0,0 +1,6 @@ +/* contrib/spi/insert_username--1.0.sql */ + +CREATE FUNCTION insert_username() +RETURNS trigger +AS 'MODULE_PATHNAME' +LANGUAGE C; diff --git a/contrib/spi/insert_username--unpackaged--1.0.sql b/contrib/spi/insert_username--unpackaged--1.0.sql new file mode 100644 index 0000000000..f53cb690f1 --- /dev/null +++ b/contrib/spi/insert_username--unpackaged--1.0.sql @@ -0,0 +1,3 @@ +/* contrib/spi/insert_username--unpackaged--1.0.sql */ + +ALTER EXTENSION insert_username ADD function insert_username(); diff --git a/contrib/spi/insert_username.control b/contrib/spi/insert_username.control new file mode 100644 index 0000000000..9d110643ee --- /dev/null +++ b/contrib/spi/insert_username.control @@ -0,0 +1,5 @@ +# insert_username extension +comment = 'functions for tracking who changed a table' +default_version = '1.0' +module_pathname = '$libdir/insert_username' +relocatable = true diff --git a/contrib/spi/insert_username.sql.in b/contrib/spi/insert_username.sql.in deleted file mode 100644 index bdc2deb340..0000000000 --- a/contrib/spi/insert_username.sql.in +++ /dev/null @@ -1,9 +0,0 @@ -/* contrib/spi/insert_username.sql.in */ - --- Adjust this setting to control where the objects get created. -SET search_path = public; - -CREATE OR REPLACE FUNCTION insert_username() -RETURNS trigger -AS 'MODULE_PATHNAME' -LANGUAGE C; diff --git a/contrib/spi/moddatetime--1.0.sql b/contrib/spi/moddatetime--1.0.sql new file mode 100644 index 0000000000..97cc63289f --- /dev/null +++ b/contrib/spi/moddatetime--1.0.sql @@ -0,0 +1,6 @@ +/* contrib/spi/moddatetime--1.0.sql */ + +CREATE FUNCTION moddatetime() +RETURNS trigger +AS 'MODULE_PATHNAME' +LANGUAGE C; diff --git a/contrib/spi/moddatetime--unpackaged--1.0.sql b/contrib/spi/moddatetime--unpackaged--1.0.sql new file mode 100644 index 0000000000..f3a0a96837 --- /dev/null +++ b/contrib/spi/moddatetime--unpackaged--1.0.sql @@ -0,0 +1,3 @@ +/* contrib/spi/moddatetime--unpackaged--1.0.sql */ + +ALTER EXTENSION moddatetime ADD function moddatetime(); diff --git a/contrib/spi/moddatetime.control b/contrib/spi/moddatetime.control new file mode 100644 index 0000000000..93dfac589a --- /dev/null +++ b/contrib/spi/moddatetime.control @@ -0,0 +1,5 @@ +# moddatetime extension +comment = 'functions for tracking last modification time' +default_version = '1.0' +module_pathname = '$libdir/moddatetime' +relocatable = true diff --git a/contrib/spi/moddatetime.sql.in b/contrib/spi/moddatetime.sql.in deleted file mode 100644 index e4ca6a6653..0000000000 --- a/contrib/spi/moddatetime.sql.in +++ /dev/null @@ -1,9 +0,0 @@ -/* contrib/spi/moddatetime.sql.in */ - --- Adjust this setting to control where the objects get created. -SET search_path = public; - -CREATE OR REPLACE FUNCTION moddatetime() -RETURNS trigger -AS 'MODULE_PATHNAME' -LANGUAGE C; diff --git a/contrib/spi/refint--1.0.sql b/contrib/spi/refint--1.0.sql new file mode 100644 index 0000000000..9e5d931df5 --- /dev/null +++ b/contrib/spi/refint--1.0.sql @@ -0,0 +1,11 @@ +/* contrib/spi/refint--1.0.sql */ + +CREATE FUNCTION check_primary_key() +RETURNS trigger +AS 'MODULE_PATHNAME' +LANGUAGE C; + +CREATE FUNCTION check_foreign_key() +RETURNS trigger +AS 'MODULE_PATHNAME' +LANGUAGE C; diff --git a/contrib/spi/refint--unpackaged--1.0.sql b/contrib/spi/refint--unpackaged--1.0.sql new file mode 100644 index 0000000000..54fece055a --- /dev/null +++ b/contrib/spi/refint--unpackaged--1.0.sql @@ -0,0 +1,4 @@ +/* contrib/spi/refint--unpackaged--1.0.sql */ + +ALTER EXTENSION refint ADD function check_primary_key(); +ALTER EXTENSION refint ADD function check_foreign_key(); diff --git a/contrib/spi/refint.control b/contrib/spi/refint.control new file mode 100644 index 0000000000..cbede45784 --- /dev/null +++ b/contrib/spi/refint.control @@ -0,0 +1,5 @@ +# refint extension +comment = 'functions for implementing referential integrity (obsolete)' +default_version = '1.0' +module_pathname = '$libdir/refint' +relocatable = true diff --git a/contrib/spi/refint.sql.in b/contrib/spi/refint.sql.in deleted file mode 100644 index 2525b70006..0000000000 --- a/contrib/spi/refint.sql.in +++ /dev/null @@ -1,14 +0,0 @@ -/* contrib/spi/refint.sql.in */ - --- Adjust this setting to control where the objects get created. -SET search_path = public; - -CREATE OR REPLACE FUNCTION check_primary_key() -RETURNS trigger -AS 'MODULE_PATHNAME' -LANGUAGE C; - -CREATE OR REPLACE FUNCTION check_foreign_key() -RETURNS trigger -AS 'MODULE_PATHNAME' -LANGUAGE C; diff --git a/contrib/spi/timetravel--1.0.sql b/contrib/spi/timetravel--1.0.sql new file mode 100644 index 0000000000..b68cf2f247 --- /dev/null +++ b/contrib/spi/timetravel--1.0.sql @@ -0,0 +1,16 @@ +/* contrib/spi/timetravel--1.0.sql */ + +CREATE FUNCTION timetravel() +RETURNS trigger +AS 'MODULE_PATHNAME' +LANGUAGE C; + +CREATE FUNCTION set_timetravel(name, int4) +RETURNS int4 +AS 'MODULE_PATHNAME' +LANGUAGE C RETURNS NULL ON NULL INPUT; + +CREATE FUNCTION get_timetravel(name) +RETURNS int4 +AS 'MODULE_PATHNAME' +LANGUAGE C RETURNS NULL ON NULL INPUT; diff --git a/contrib/spi/timetravel--unpackaged--1.0.sql b/contrib/spi/timetravel--unpackaged--1.0.sql new file mode 100644 index 0000000000..e3716afe95 --- /dev/null +++ b/contrib/spi/timetravel--unpackaged--1.0.sql @@ -0,0 +1,5 @@ +/* contrib/spi/timetravel--unpackaged--1.0.sql */ + +ALTER EXTENSION timetravel ADD function timetravel(); +ALTER EXTENSION timetravel ADD function set_timetravel(name,integer); +ALTER EXTENSION timetravel ADD function get_timetravel(name); diff --git a/contrib/spi/timetravel.control b/contrib/spi/timetravel.control new file mode 100644 index 0000000000..9b4bb6ba04 --- /dev/null +++ b/contrib/spi/timetravel.control @@ -0,0 +1,5 @@ +# timetravel extension +comment = 'functions for implementing time travel' +default_version = '1.0' +module_pathname = '$libdir/timetravel' +relocatable = true diff --git a/contrib/spi/timetravel.sql.in b/contrib/spi/timetravel.sql.in deleted file mode 100644 index 83dc958a88..0000000000 --- a/contrib/spi/timetravel.sql.in +++ /dev/null @@ -1,19 +0,0 @@ -/* contrib/spi/timetravel.sql.in */ - --- Adjust this setting to control where the objects get created. -SET search_path = public; - -CREATE OR REPLACE FUNCTION timetravel() -RETURNS trigger -AS 'MODULE_PATHNAME' -LANGUAGE C; - -CREATE OR REPLACE FUNCTION set_timetravel(name, int4) -RETURNS int4 -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT; - -CREATE OR REPLACE FUNCTION get_timetravel(name) -RETURNS int4 -AS 'MODULE_PATHNAME' -LANGUAGE C RETURNS NULL ON NULL INPUT; diff --git a/contrib/sslinfo/.gitignore b/contrib/sslinfo/.gitignore deleted file mode 100644 index 6ed45c8ce5..0000000000 --- a/contrib/sslinfo/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/sslinfo.sql diff --git a/contrib/sslinfo/Makefile b/contrib/sslinfo/Makefile index a4c3d84297..0dee6ed2f7 100644 --- a/contrib/sslinfo/Makefile +++ b/contrib/sslinfo/Makefile @@ -2,8 +2,9 @@ MODULE_big = sslinfo OBJS = sslinfo.o -DATA_built = sslinfo.sql -DATA = uninstall_sslinfo.sql + +EXTENSION = sslinfo +DATA = sslinfo--1.0.sql sslinfo--unpackaged--1.0.sql ifdef USE_PGXS PG_CONFIG = pg_config diff --git a/contrib/sslinfo/sslinfo--1.0.sql b/contrib/sslinfo/sslinfo--1.0.sql new file mode 100644 index 0000000000..ef745a0a2e --- /dev/null +++ b/contrib/sslinfo/sslinfo--1.0.sql @@ -0,0 +1,37 @@ +/* contrib/sslinfo/sslinfo--1.0.sql */ + +CREATE FUNCTION ssl_client_serial() RETURNS numeric +AS 'MODULE_PATHNAME', 'ssl_client_serial' +LANGUAGE C STRICT; + +CREATE FUNCTION ssl_is_used() RETURNS boolean +AS 'MODULE_PATHNAME', 'ssl_is_used' +LANGUAGE C STRICT; + +CREATE FUNCTION ssl_version() RETURNS text +AS 'MODULE_PATHNAME', 'ssl_version' +LANGUAGE C STRICT; + +CREATE FUNCTION ssl_cipher() RETURNS text +AS 'MODULE_PATHNAME', 'ssl_cipher' +LANGUAGE C STRICT; + +CREATE FUNCTION ssl_client_cert_present() RETURNS boolean +AS 'MODULE_PATHNAME', 'ssl_client_cert_present' +LANGUAGE C STRICT; + +CREATE FUNCTION ssl_client_dn_field(text) RETURNS text +AS 'MODULE_PATHNAME', 'ssl_client_dn_field' +LANGUAGE C STRICT; + +CREATE FUNCTION ssl_issuer_field(text) RETURNS text +AS 'MODULE_PATHNAME', 'ssl_issuer_field' +LANGUAGE C STRICT; + +CREATE FUNCTION ssl_client_dn() RETURNS text +AS 'MODULE_PATHNAME', 'ssl_client_dn' +LANGUAGE C STRICT; + +CREATE FUNCTION ssl_issuer_dn() RETURNS text +AS 'MODULE_PATHNAME', 'ssl_issuer_dn' +LANGUAGE C STRICT; diff --git a/contrib/sslinfo/sslinfo--unpackaged--1.0.sql b/contrib/sslinfo/sslinfo--unpackaged--1.0.sql new file mode 100644 index 0000000000..befa96e908 --- /dev/null +++ b/contrib/sslinfo/sslinfo--unpackaged--1.0.sql @@ -0,0 +1,19 @@ +/* contrib/sslinfo/sslinfo--unpackaged--1.0.sql */ + +ALTER EXTENSION sslinfo ADD function ssl_client_serial(); +ALTER EXTENSION sslinfo ADD function ssl_is_used(); +ALTER EXTENSION sslinfo ADD function ssl_client_cert_present(); +ALTER EXTENSION sslinfo ADD function ssl_client_dn_field(text); +ALTER EXTENSION sslinfo ADD function ssl_issuer_field(text); +ALTER EXTENSION sslinfo ADD function ssl_client_dn(); +ALTER EXTENSION sslinfo ADD function ssl_issuer_dn(); + +-- These functions were not in 9.0: + +CREATE FUNCTION ssl_version() RETURNS text +AS 'MODULE_PATHNAME', 'ssl_version' +LANGUAGE C STRICT; + +CREATE FUNCTION ssl_cipher() RETURNS text +AS 'MODULE_PATHNAME', 'ssl_cipher' +LANGUAGE C STRICT; diff --git a/contrib/sslinfo/sslinfo.control b/contrib/sslinfo/sslinfo.control new file mode 100644 index 0000000000..1d2f058f6e --- /dev/null +++ b/contrib/sslinfo/sslinfo.control @@ -0,0 +1,5 @@ +# sslinfo extension +comment = 'information about SSL certificates' +default_version = '1.0' +module_pathname = '$libdir/sslinfo' +relocatable = true diff --git a/contrib/sslinfo/sslinfo.sql.in b/contrib/sslinfo/sslinfo.sql.in deleted file mode 100644 index 66cbe3ea66..0000000000 --- a/contrib/sslinfo/sslinfo.sql.in +++ /dev/null @@ -1,40 +0,0 @@ -/* contrib/sslinfo/sslinfo.sql.in */ - --- Adjust this setting to control where the objects get created. -SET search_path = public; - -CREATE OR REPLACE FUNCTION ssl_client_serial() RETURNS numeric -AS 'MODULE_PATHNAME', 'ssl_client_serial' -LANGUAGE C STRICT; - -CREATE OR REPLACE FUNCTION ssl_is_used() RETURNS boolean -AS 'MODULE_PATHNAME', 'ssl_is_used' -LANGUAGE C STRICT; - -CREATE OR REPLACE FUNCTION ssl_version() RETURNS text -AS 'MODULE_PATHNAME', 'ssl_version' -LANGUAGE C STRICT; - -CREATE OR REPLACE FUNCTION ssl_cipher() RETURNS text -AS 'MODULE_PATHNAME', 'ssl_cipher' -LANGUAGE C STRICT; - -CREATE OR REPLACE FUNCTION ssl_client_cert_present() RETURNS boolean -AS 'MODULE_PATHNAME', 'ssl_client_cert_present' -LANGUAGE C STRICT; - -CREATE OR REPLACE FUNCTION ssl_client_dn_field(text) RETURNS text -AS 'MODULE_PATHNAME', 'ssl_client_dn_field' -LANGUAGE C STRICT; - -CREATE OR REPLACE FUNCTION ssl_issuer_field(text) RETURNS text -AS 'MODULE_PATHNAME', 'ssl_issuer_field' -LANGUAGE C STRICT; - -CREATE OR REPLACE FUNCTION ssl_client_dn() RETURNS text -AS 'MODULE_PATHNAME', 'ssl_client_dn' -LANGUAGE C STRICT; - -CREATE OR REPLACE FUNCTION ssl_issuer_dn() RETURNS text -AS 'MODULE_PATHNAME', 'ssl_issuer_dn' -LANGUAGE C STRICT; diff --git a/contrib/sslinfo/uninstall_sslinfo.sql b/contrib/sslinfo/uninstall_sslinfo.sql deleted file mode 100644 index 9ac572c8f9..0000000000 --- a/contrib/sslinfo/uninstall_sslinfo.sql +++ /dev/null @@ -1,14 +0,0 @@ -/* contrib/sslinfo/uninstall_sslinfo.sql */ - --- Adjust this setting to control where the objects get dropped. -SET search_path = public; - -DROP FUNCTION ssl_client_serial(); -DROP FUNCTION ssl_is_used(); -DROP FUNCTION ssl_cipher(); -DROP FUNCTION ssl_version(); -DROP FUNCTION ssl_client_cert_present(); -DROP FUNCTION ssl_client_dn_field(text); -DROP FUNCTION ssl_issuer_field(text); -DROP FUNCTION ssl_client_dn(); -DROP FUNCTION ssl_issuer_dn(); diff --git a/contrib/tablefunc/.gitignore b/contrib/tablefunc/.gitignore index b28639637b..19b6c5ba42 100644 --- a/contrib/tablefunc/.gitignore +++ b/contrib/tablefunc/.gitignore @@ -1,3 +1,2 @@ -/tablefunc.sql # Generated subdirectories /results/ diff --git a/contrib/tablefunc/Makefile b/contrib/tablefunc/Makefile index a5c2882866..eb108931ec 100644 --- a/contrib/tablefunc/Makefile +++ b/contrib/tablefunc/Makefile @@ -1,8 +1,10 @@ # contrib/tablefunc/Makefile MODULES = tablefunc -DATA_built = tablefunc.sql -DATA = uninstall_tablefunc.sql + +EXTENSION = tablefunc +DATA = tablefunc--1.0.sql tablefunc--unpackaged--1.0.sql + REGRESS = tablefunc LDFLAGS_SL += $(filter -lm, $(LIBS)) diff --git a/contrib/tablefunc/expected/tablefunc.out b/contrib/tablefunc/expected/tablefunc.out index 15ef758ed7..7ad4336ada 100644 --- a/contrib/tablefunc/expected/tablefunc.out +++ b/contrib/tablefunc/expected/tablefunc.out @@ -1,10 +1,4 @@ --- --- first, define the functions. Turn off echoing so that expected file --- does not depend on contents of tablefunc.sql. --- -SET client_min_messages = warning; -\set ECHO none -RESET client_min_messages; +CREATE EXTENSION tablefunc; -- -- normal_rand() -- no easy way to do this for regression testing diff --git a/contrib/tablefunc/sql/tablefunc.sql b/contrib/tablefunc/sql/tablefunc.sql index 8846a4218e..bf874f26ad 100644 --- a/contrib/tablefunc/sql/tablefunc.sql +++ b/contrib/tablefunc/sql/tablefunc.sql @@ -1,12 +1,4 @@ --- --- first, define the functions. Turn off echoing so that expected file --- does not depend on contents of tablefunc.sql. --- -SET client_min_messages = warning; -\set ECHO none -\i tablefunc.sql -\set ECHO all -RESET client_min_messages; +CREATE EXTENSION tablefunc; -- -- normal_rand() diff --git a/contrib/tablefunc/tablefunc.sql.in b/contrib/tablefunc/tablefunc--1.0.sql index 54cba5ed3e..7bf117cb5c 100644 --- a/contrib/tablefunc/tablefunc.sql.in +++ b/contrib/tablefunc/tablefunc--1.0.sql @@ -1,15 +1,12 @@ -/* contrib/tablefunc/tablefunc.sql.in */ +/* contrib/tablefunc/tablefunc--1.0.sql */ --- Adjust this setting to control where the objects get created. -SET search_path = public; - -CREATE OR REPLACE FUNCTION normal_rand(int4, float8, float8) +CREATE FUNCTION normal_rand(int4, float8, float8) RETURNS setof float8 AS 'MODULE_PATHNAME','normal_rand' LANGUAGE C VOLATILE STRICT; -- the generic crosstab function: -CREATE OR REPLACE FUNCTION crosstab(text) +CREATE FUNCTION crosstab(text) RETURNS setof record AS 'MODULE_PATHNAME','crosstab' LANGUAGE C STABLE STRICT; @@ -39,50 +36,50 @@ CREATE TYPE tablefunc_crosstab_4 AS category_4 TEXT ); -CREATE OR REPLACE FUNCTION crosstab2(text) +CREATE FUNCTION crosstab2(text) RETURNS setof tablefunc_crosstab_2 AS 'MODULE_PATHNAME','crosstab' LANGUAGE C STABLE STRICT; -CREATE OR REPLACE FUNCTION crosstab3(text) +CREATE FUNCTION crosstab3(text) RETURNS setof tablefunc_crosstab_3 AS 'MODULE_PATHNAME','crosstab' LANGUAGE C STABLE STRICT; -CREATE OR REPLACE FUNCTION crosstab4(text) +CREATE FUNCTION crosstab4(text) RETURNS setof tablefunc_crosstab_4 AS 'MODULE_PATHNAME','crosstab' LANGUAGE C STABLE STRICT; -- obsolete: -CREATE OR REPLACE FUNCTION crosstab(text,int) +CREATE FUNCTION crosstab(text,int) RETURNS setof record AS 'MODULE_PATHNAME','crosstab' LANGUAGE C STABLE STRICT; -CREATE OR REPLACE FUNCTION crosstab(text,text) +CREATE FUNCTION crosstab(text,text) RETURNS setof record AS 'MODULE_PATHNAME','crosstab_hash' LANGUAGE C STABLE STRICT; -CREATE OR REPLACE FUNCTION connectby(text,text,text,text,int,text) +CREATE FUNCTION connectby(text,text,text,text,int,text) RETURNS setof record AS 'MODULE_PATHNAME','connectby_text' LANGUAGE C STABLE STRICT; -CREATE OR REPLACE FUNCTION connectby(text,text,text,text,int) +CREATE FUNCTION connectby(text,text,text,text,int) RETURNS setof record AS 'MODULE_PATHNAME','connectby_text' LANGUAGE C STABLE STRICT; -- These 2 take the name of a field to ORDER BY as 4th arg (for sorting siblings) -CREATE OR REPLACE FUNCTION connectby(text,text,text,text,text,int,text) +CREATE FUNCTION connectby(text,text,text,text,text,int,text) RETURNS setof record AS 'MODULE_PATHNAME','connectby_text_serial' LANGUAGE C STABLE STRICT; -CREATE OR REPLACE FUNCTION connectby(text,text,text,text,text,int) +CREATE FUNCTION connectby(text,text,text,text,text,int) RETURNS setof record AS 'MODULE_PATHNAME','connectby_text_serial' LANGUAGE C STABLE STRICT; diff --git a/contrib/tablefunc/tablefunc--unpackaged--1.0.sql b/contrib/tablefunc/tablefunc--unpackaged--1.0.sql new file mode 100644 index 0000000000..20e09816e9 --- /dev/null +++ b/contrib/tablefunc/tablefunc--unpackaged--1.0.sql @@ -0,0 +1,16 @@ +/* contrib/tablefunc/tablefunc--unpackaged--1.0.sql */ + +ALTER EXTENSION tablefunc ADD function normal_rand(integer,double precision,double precision); +ALTER EXTENSION tablefunc ADD function crosstab(text); +ALTER EXTENSION tablefunc ADD type tablefunc_crosstab_2; +ALTER EXTENSION tablefunc ADD type tablefunc_crosstab_3; +ALTER EXTENSION tablefunc ADD type tablefunc_crosstab_4; +ALTER EXTENSION tablefunc ADD function crosstab2(text); +ALTER EXTENSION tablefunc ADD function crosstab3(text); +ALTER EXTENSION tablefunc ADD function crosstab4(text); +ALTER EXTENSION tablefunc ADD function crosstab(text,integer); +ALTER EXTENSION tablefunc ADD function crosstab(text,text); +ALTER EXTENSION tablefunc ADD function connectby(text,text,text,text,integer,text); +ALTER EXTENSION tablefunc ADD function connectby(text,text,text,text,integer); +ALTER EXTENSION tablefunc ADD function connectby(text,text,text,text,text,integer,text); +ALTER EXTENSION tablefunc ADD function connectby(text,text,text,text,text,integer); diff --git a/contrib/tablefunc/tablefunc.control b/contrib/tablefunc/tablefunc.control new file mode 100644 index 0000000000..248b0a77a2 --- /dev/null +++ b/contrib/tablefunc/tablefunc.control @@ -0,0 +1,5 @@ +# tablefunc extension +comment = 'functions that manipulate whole tables, including crosstab' +default_version = '1.0' +module_pathname = '$libdir/tablefunc' +relocatable = true diff --git a/contrib/tablefunc/uninstall_tablefunc.sql b/contrib/tablefunc/uninstall_tablefunc.sql deleted file mode 100644 index b1ec916447..0000000000 --- a/contrib/tablefunc/uninstall_tablefunc.sql +++ /dev/null @@ -1,32 +0,0 @@ -/* contrib/tablefunc/uninstall_tablefunc.sql */ - --- Adjust this setting to control where the objects get dropped. -SET search_path = public; - -DROP FUNCTION connectby(text,text,text,text,text,int); - -DROP FUNCTION connectby(text,text,text,text,text,int,text); - -DROP FUNCTION connectby(text,text,text,text,int); - -DROP FUNCTION connectby(text,text,text,text,int,text); - -DROP FUNCTION crosstab(text,text); - -DROP FUNCTION crosstab(text,int); - -DROP FUNCTION crosstab4(text); - -DROP FUNCTION crosstab3(text); - -DROP FUNCTION crosstab2(text); - -DROP TYPE tablefunc_crosstab_4; - -DROP TYPE tablefunc_crosstab_3; - -DROP TYPE tablefunc_crosstab_2; - -DROP FUNCTION crosstab(text); - -DROP FUNCTION normal_rand(int4, float8, float8); diff --git a/contrib/test_parser/.gitignore b/contrib/test_parser/.gitignore index c07f518855..19b6c5ba42 100644 --- a/contrib/test_parser/.gitignore +++ b/contrib/test_parser/.gitignore @@ -1,3 +1,2 @@ -/test_parser.sql # Generated subdirectories /results/ diff --git a/contrib/test_parser/Makefile b/contrib/test_parser/Makefile index ad4e0ec9b8..b9766cb023 100644 --- a/contrib/test_parser/Makefile +++ b/contrib/test_parser/Makefile @@ -2,8 +2,10 @@ MODULE_big = test_parser OBJS = test_parser.o -DATA_built = test_parser.sql -DATA = uninstall_test_parser.sql + +EXTENSION = test_parser +DATA = test_parser--1.0.sql test_parser--unpackaged--1.0.sql + REGRESS = test_parser ifdef USE_PGXS diff --git a/contrib/test_parser/expected/test_parser.out b/contrib/test_parser/expected/test_parser.out index 3d0fd4210f..8a49bc01e3 100644 --- a/contrib/test_parser/expected/test_parser.out +++ b/contrib/test_parser/expected/test_parser.out @@ -1,10 +1,4 @@ --- --- first, define the parser. Turn off echoing so that expected file --- does not depend on contents of this file. --- -SET client_min_messages = warning; -\set ECHO none -RESET client_min_messages; +CREATE EXTENSION test_parser; -- make test configuration using parser CREATE TEXT SEARCH CONFIGURATION testcfg (PARSER = testparser); ALTER TEXT SEARCH CONFIGURATION testcfg ADD MAPPING FOR word WITH simple; diff --git a/contrib/test_parser/sql/test_parser.sql b/contrib/test_parser/sql/test_parser.sql index 97c2cb5a5d..1f21504602 100644 --- a/contrib/test_parser/sql/test_parser.sql +++ b/contrib/test_parser/sql/test_parser.sql @@ -1,12 +1,4 @@ --- --- first, define the parser. Turn off echoing so that expected file --- does not depend on contents of this file. --- -SET client_min_messages = warning; -\set ECHO none -\i test_parser.sql -\set ECHO all -RESET client_min_messages; +CREATE EXTENSION test_parser; -- make test configuration using parser diff --git a/contrib/test_parser/test_parser.sql.in b/contrib/test_parser/test_parser--1.0.sql index bab97a2987..5b8b04bb05 100644 --- a/contrib/test_parser/test_parser.sql.in +++ b/contrib/test_parser/test_parser--1.0.sql @@ -1,24 +1,21 @@ -/* contrib/test_parser/test_parser.sql.in */ +/* contrib/test_parser/test_parser--1.0.sql */ --- Adjust this setting to control where the objects get created. -SET search_path = public; - -CREATE OR REPLACE FUNCTION testprs_start(internal, int4) +CREATE FUNCTION testprs_start(internal, int4) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION testprs_getlexeme(internal, internal, internal) +CREATE FUNCTION testprs_getlexeme(internal, internal, internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION testprs_end(internal) +CREATE FUNCTION testprs_end(internal) RETURNS void AS 'MODULE_PATHNAME' LANGUAGE C STRICT; -CREATE OR REPLACE FUNCTION testprs_lextype(internal) +CREATE FUNCTION testprs_lextype(internal) RETURNS internal AS 'MODULE_PATHNAME' LANGUAGE C STRICT; diff --git a/contrib/test_parser/test_parser--unpackaged--1.0.sql b/contrib/test_parser/test_parser--unpackaged--1.0.sql new file mode 100644 index 0000000000..e240ab2b5b --- /dev/null +++ b/contrib/test_parser/test_parser--unpackaged--1.0.sql @@ -0,0 +1,7 @@ +/* contrib/test_parser/test_parser--unpackaged--1.0.sql */ + +ALTER EXTENSION test_parser ADD function testprs_start(internal,integer); +ALTER EXTENSION test_parser ADD function testprs_getlexeme(internal,internal,internal); +ALTER EXTENSION test_parser ADD function testprs_end(internal); +ALTER EXTENSION test_parser ADD function testprs_lextype(internal); +ALTER EXTENSION test_parser ADD text search parser testparser; diff --git a/contrib/test_parser/test_parser.control b/contrib/test_parser/test_parser.control new file mode 100644 index 0000000000..36b26b2087 --- /dev/null +++ b/contrib/test_parser/test_parser.control @@ -0,0 +1,5 @@ +# test_parser extension +comment = 'example of a custom parser for full-text search' +default_version = '1.0' +module_pathname = '$libdir/test_parser' +relocatable = true diff --git a/contrib/test_parser/uninstall_test_parser.sql b/contrib/test_parser/uninstall_test_parser.sql deleted file mode 100644 index 042f46b251..0000000000 --- a/contrib/test_parser/uninstall_test_parser.sql +++ /dev/null @@ -1,14 +0,0 @@ -/* contrib/test_parser/uninstall_test_parser.sql */ - --- Adjust this setting to control where the objects get dropped. -SET search_path = public; - -DROP TEXT SEARCH PARSER testparser; - -DROP FUNCTION testprs_start(internal, int4); - -DROP FUNCTION testprs_getlexeme(internal, internal, internal); - -DROP FUNCTION testprs_end(internal); - -DROP FUNCTION testprs_lextype(internal); diff --git a/contrib/tsearch2/.gitignore b/contrib/tsearch2/.gitignore index 1d34309d00..19b6c5ba42 100644 --- a/contrib/tsearch2/.gitignore +++ b/contrib/tsearch2/.gitignore @@ -1,3 +1,2 @@ -/tsearch2.sql # Generated subdirectories /results/ diff --git a/contrib/tsearch2/Makefile b/contrib/tsearch2/Makefile index 8748d30b19..d260fd0030 100644 --- a/contrib/tsearch2/Makefile +++ b/contrib/tsearch2/Makefile @@ -1,8 +1,10 @@ # contrib/tsearch2/Makefile MODULES = tsearch2 -DATA_built = tsearch2.sql -DATA = uninstall_tsearch2.sql + +EXTENSION = tsearch2 +DATA = tsearch2--1.0.sql tsearch2--unpackaged--1.0.sql + REGRESS = tsearch2 ifdef USE_PGXS diff --git a/contrib/tsearch2/expected/tsearch2.out b/contrib/tsearch2/expected/tsearch2.out index 18b591e0aa..972f764c14 100644 --- a/contrib/tsearch2/expected/tsearch2.out +++ b/contrib/tsearch2/expected/tsearch2.out @@ -1,10 +1,4 @@ --- --- first, define the datatype. Turn off echoing so that expected file --- does not depend on contents of tsearch2.sql. --- -SET client_min_messages = warning; -\set ECHO none -RESET client_min_messages; +CREATE EXTENSION tsearch2; --tsvector SELECT '1'::tsvector; tsvector diff --git a/contrib/tsearch2/expected/tsearch2_1.out b/contrib/tsearch2/expected/tsearch2_1.out index f7cb0963b8..349733b6fd 100644 --- a/contrib/tsearch2/expected/tsearch2_1.out +++ b/contrib/tsearch2/expected/tsearch2_1.out @@ -1,10 +1,4 @@ --- --- first, define the datatype. Turn off echoing so that expected file --- does not depend on contents of tsearch2.sql. --- -SET client_min_messages = warning; -\set ECHO none -RESET client_min_messages; +CREATE EXTENSION tsearch2; --tsvector SELECT '1'::tsvector; tsvector diff --git a/contrib/tsearch2/sql/tsearch2.sql b/contrib/tsearch2/sql/tsearch2.sql index 99d808a1b3..665882ff88 100644 --- a/contrib/tsearch2/sql/tsearch2.sql +++ b/contrib/tsearch2/sql/tsearch2.sql @@ -1,12 +1,4 @@ --- --- first, define the datatype. Turn off echoing so that expected file --- does not depend on contents of tsearch2.sql. --- -SET client_min_messages = warning; -\set ECHO none -\i tsearch2.sql -\set ECHO all -RESET client_min_messages; +CREATE EXTENSION tsearch2; --tsvector SELECT '1'::tsvector; diff --git a/contrib/tsearch2/tsearch2.sql.in b/contrib/tsearch2/tsearch2--1.0.sql index f7f7dfdec8..369507e8f5 100644 --- a/contrib/tsearch2/tsearch2.sql.in +++ b/contrib/tsearch2/tsearch2--1.0.sql @@ -1,7 +1,4 @@ -/* contrib/tsearch2/tsearch2.sql.in */ - --- Adjust this setting to control where the objects get created. -SET search_path = public; +/* contrib/tsearch2/tsearch2--1.0.sql */ -- These domains are just to catch schema-qualified references to the -- old data types. @@ -438,7 +435,7 @@ create type tsdebug as ( "tsvector" tsvector ); -CREATE or replace FUNCTION _get_parser_from_curcfg() +CREATE FUNCTION _get_parser_from_curcfg() RETURNS text as $$select prsname::text from pg_catalog.pg_ts_parser p join pg_ts_config c on cfgparser = p.oid where c.oid = show_curcfg();$$ LANGUAGE SQL RETURNS NULL ON NULL INPUT IMMUTABLE; @@ -464,25 +461,25 @@ where t.tokid = p.tokid $$ LANGUAGE SQL RETURNS NULL ON NULL INPUT; -CREATE OR REPLACE FUNCTION numnode(tsquery) +CREATE FUNCTION numnode(tsquery) RETURNS int4 as 'tsquery_numnode' LANGUAGE INTERNAL RETURNS NULL ON NULL INPUT IMMUTABLE; -CREATE OR REPLACE FUNCTION tsquery_and(tsquery,tsquery) +CREATE FUNCTION tsquery_and(tsquery,tsquery) RETURNS tsquery as 'tsquery_and' LANGUAGE INTERNAL RETURNS NULL ON NULL INPUT IMMUTABLE; -CREATE OR REPLACE FUNCTION tsquery_or(tsquery,tsquery) +CREATE FUNCTION tsquery_or(tsquery,tsquery) RETURNS tsquery as 'tsquery_or' LANGUAGE INTERNAL RETURNS NULL ON NULL INPUT IMMUTABLE; -CREATE OR REPLACE FUNCTION tsquery_not(tsquery) +CREATE FUNCTION tsquery_not(tsquery) RETURNS tsquery as 'tsquery_not' LANGUAGE INTERNAL @@ -490,24 +487,24 @@ CREATE OR REPLACE FUNCTION tsquery_not(tsquery) --------------rewrite subsystem -CREATE OR REPLACE FUNCTION rewrite(tsquery, text) +CREATE FUNCTION rewrite(tsquery, text) RETURNS tsquery as 'tsquery_rewrite_query' LANGUAGE INTERNAL RETURNS NULL ON NULL INPUT IMMUTABLE; -CREATE OR REPLACE FUNCTION rewrite(tsquery, tsquery, tsquery) +CREATE FUNCTION rewrite(tsquery, tsquery, tsquery) RETURNS tsquery as 'tsquery_rewrite' LANGUAGE INTERNAL RETURNS NULL ON NULL INPUT IMMUTABLE; -CREATE OR REPLACE FUNCTION rewrite_accum(tsquery,tsquery[]) +CREATE FUNCTION rewrite_accum(tsquery,tsquery[]) RETURNS tsquery AS 'MODULE_PATHNAME', 'tsa_rewrite_accum' LANGUAGE C; -CREATE OR REPLACE FUNCTION rewrite_finish(tsquery) +CREATE FUNCTION rewrite_finish(tsquery) RETURNS tsquery as 'MODULE_PATHNAME', 'tsa_rewrite_finish' LANGUAGE C; @@ -519,13 +516,13 @@ CREATE AGGREGATE rewrite ( FINALFUNC = rewrite_finish ); -CREATE OR REPLACE FUNCTION tsq_mcontains(tsquery, tsquery) +CREATE FUNCTION tsq_mcontains(tsquery, tsquery) RETURNS bool as 'tsq_mcontains' LANGUAGE INTERNAL RETURNS NULL ON NULL INPUT IMMUTABLE; -CREATE OR REPLACE FUNCTION tsq_mcontained(tsquery, tsquery) +CREATE FUNCTION tsq_mcontained(tsquery, tsquery) RETURNS bool as 'tsq_mcontained' LANGUAGE INTERNAL diff --git a/contrib/tsearch2/tsearch2--unpackaged--1.0.sql b/contrib/tsearch2/tsearch2--unpackaged--1.0.sql new file mode 100644 index 0000000000..a3fcc8572a --- /dev/null +++ b/contrib/tsearch2/tsearch2--unpackaged--1.0.sql @@ -0,0 +1,100 @@ +/* contrib/tsearch2/tsearch2--unpackaged--1.0.sql */ + +ALTER EXTENSION tsearch2 ADD type @[email protected]; +ALTER EXTENSION tsearch2 ADD type @[email protected]; +ALTER EXTENSION tsearch2 ADD type @[email protected]; +ALTER EXTENSION tsearch2 ADD type gtsq; +ALTER EXTENSION tsearch2 ADD function lexize(oid,text); +ALTER EXTENSION tsearch2 ADD function lexize(text,text); +ALTER EXTENSION tsearch2 ADD function lexize(text); +ALTER EXTENSION tsearch2 ADD function set_curdict(integer); +ALTER EXTENSION tsearch2 ADD function set_curdict(text); +ALTER EXTENSION tsearch2 ADD function dex_init(internal); +ALTER EXTENSION tsearch2 ADD function dex_lexize(internal,internal,integer); +ALTER EXTENSION tsearch2 ADD function snb_en_init(internal); +ALTER EXTENSION tsearch2 ADD function snb_lexize(internal,internal,integer); +ALTER EXTENSION tsearch2 ADD function snb_ru_init_koi8(internal); +ALTER EXTENSION tsearch2 ADD function snb_ru_init_utf8(internal); +ALTER EXTENSION tsearch2 ADD function snb_ru_init(internal); +ALTER EXTENSION tsearch2 ADD function spell_init(internal); +ALTER EXTENSION tsearch2 ADD function spell_lexize(internal,internal,integer); +ALTER EXTENSION tsearch2 ADD function syn_init(internal); +ALTER EXTENSION tsearch2 ADD function syn_lexize(internal,internal,integer); +ALTER EXTENSION tsearch2 ADD function @[email protected]_init(internal); +ALTER EXTENSION tsearch2 ADD function thesaurus_lexize(internal,internal,integer,internal); +ALTER EXTENSION tsearch2 ADD type tokentype; +ALTER EXTENSION tsearch2 ADD function token_type(integer); +ALTER EXTENSION tsearch2 ADD function token_type(text); +ALTER EXTENSION tsearch2 ADD function token_type(); +ALTER EXTENSION tsearch2 ADD function set_curprs(integer); +ALTER EXTENSION tsearch2 ADD function set_curprs(text); +ALTER EXTENSION tsearch2 ADD type tokenout; +ALTER EXTENSION tsearch2 ADD function parse(oid,text); +ALTER EXTENSION tsearch2 ADD function parse(text,text); +ALTER EXTENSION tsearch2 ADD function parse(text); +ALTER EXTENSION tsearch2 ADD function @[email protected]_start(internal,integer); +ALTER EXTENSION tsearch2 ADD function prsd_getlexeme(internal,internal,internal); +ALTER EXTENSION tsearch2 ADD function @[email protected]_end(internal); +ALTER EXTENSION tsearch2 ADD function @[email protected]_lextype(internal); +ALTER EXTENSION tsearch2 ADD function prsd_headline(internal,internal,internal); +ALTER EXTENSION tsearch2 ADD function set_curcfg(integer); +ALTER EXTENSION tsearch2 ADD function set_curcfg(text); +ALTER EXTENSION tsearch2 ADD function show_curcfg(); +ALTER EXTENSION tsearch2 ADD function @[email protected](tsvector); +ALTER EXTENSION tsearch2 ADD function to_tsvector(oid,text); +ALTER EXTENSION tsearch2 ADD function to_tsvector(text,text); +ALTER EXTENSION tsearch2 ADD function @[email protected]_tsvector(text); +ALTER EXTENSION tsearch2 ADD function @[email protected](tsvector); +ALTER EXTENSION tsearch2 ADD function @[email protected](tsvector,"char"); +ALTER EXTENSION tsearch2 ADD function concat(tsvector,tsvector); +ALTER EXTENSION tsearch2 ADD function @[email protected](tsquery); +ALTER EXTENSION tsearch2 ADD function to_tsquery(oid,text); +ALTER EXTENSION tsearch2 ADD function to_tsquery(text,text); +ALTER EXTENSION tsearch2 ADD function @[email protected]_tsquery(text); +ALTER EXTENSION tsearch2 ADD function plainto_tsquery(oid,text); +ALTER EXTENSION tsearch2 ADD function plainto_tsquery(text,text); +ALTER EXTENSION tsearch2 ADD function @[email protected]_tsquery(text); +ALTER EXTENSION tsearch2 ADD function tsearch2(); +ALTER EXTENSION tsearch2 ADD function rank(real[],tsvector,tsquery); +ALTER EXTENSION tsearch2 ADD function rank(real[],tsvector,tsquery,integer); +ALTER EXTENSION tsearch2 ADD function rank(tsvector,tsquery); +ALTER EXTENSION tsearch2 ADD function rank(tsvector,tsquery,integer); +ALTER EXTENSION tsearch2 ADD function rank_cd(real[],tsvector,tsquery); +ALTER EXTENSION tsearch2 ADD function rank_cd(real[],tsvector,tsquery,integer); +ALTER EXTENSION tsearch2 ADD function rank_cd(tsvector,tsquery); +ALTER EXTENSION tsearch2 ADD function rank_cd(tsvector,tsquery,integer); +ALTER EXTENSION tsearch2 ADD function headline(oid,text,tsquery,text); +ALTER EXTENSION tsearch2 ADD function headline(oid,text,tsquery); +ALTER EXTENSION tsearch2 ADD function headline(text,text,tsquery,text); +ALTER EXTENSION tsearch2 ADD function headline(text,text,tsquery); +ALTER EXTENSION tsearch2 ADD function headline(text,tsquery,text); +ALTER EXTENSION tsearch2 ADD function headline(text,tsquery); +ALTER EXTENSION tsearch2 ADD operator family gist_tsvector_ops using gist; +ALTER EXTENSION tsearch2 ADD operator class gist_tsvector_ops using gist; +ALTER EXTENSION tsearch2 ADD type statinfo; +ALTER EXTENSION tsearch2 ADD function stat(text); +ALTER EXTENSION tsearch2 ADD function stat(text,text); +ALTER EXTENSION tsearch2 ADD function reset_tsearch(); +ALTER EXTENSION tsearch2 ADD function get_covers(tsvector,tsquery); +ALTER EXTENSION tsearch2 ADD type tsdebug; +ALTER EXTENSION tsearch2 ADD function _get_parser_from_curcfg(); +ALTER EXTENSION tsearch2 ADD function @[email protected]_debug(text); +ALTER EXTENSION tsearch2 ADD function @[email protected](tsquery); +ALTER EXTENSION tsearch2 ADD function @[email protected]_and(tsquery,tsquery); +ALTER EXTENSION tsearch2 ADD function @[email protected]_or(tsquery,tsquery); +ALTER EXTENSION tsearch2 ADD function @[email protected]_not(tsquery); +ALTER EXTENSION tsearch2 ADD function rewrite(tsquery,text); +ALTER EXTENSION tsearch2 ADD function rewrite(tsquery,tsquery,tsquery); +ALTER EXTENSION tsearch2 ADD function rewrite_accum(tsquery,tsquery[]); +ALTER EXTENSION tsearch2 ADD function rewrite_finish(tsquery); +ALTER EXTENSION tsearch2 ADD function rewrite(tsquery[]); +ALTER EXTENSION tsearch2 ADD function @[email protected]_mcontains(tsquery,tsquery); +ALTER EXTENSION tsearch2 ADD function @[email protected]_mcontained(tsquery,tsquery); +ALTER EXTENSION tsearch2 ADD operator family gist_tp_tsquery_ops using gist; +ALTER EXTENSION tsearch2 ADD operator class gist_tp_tsquery_ops using gist; +ALTER EXTENSION tsearch2 ADD operator family gin_tsvector_ops using gin; +ALTER EXTENSION tsearch2 ADD operator class gin_tsvector_ops using gin; +ALTER EXTENSION tsearch2 ADD operator family @[email protected]_ops using btree; +ALTER EXTENSION tsearch2 ADD operator class @[email protected]_ops using btree; +ALTER EXTENSION tsearch2 ADD operator family @[email protected]_ops using btree; +ALTER EXTENSION tsearch2 ADD operator class @[email protected]_ops using btree; diff --git a/contrib/tsearch2/tsearch2.control b/contrib/tsearch2/tsearch2.control new file mode 100644 index 0000000000..3e11bcfbe8 --- /dev/null +++ b/contrib/tsearch2/tsearch2.control @@ -0,0 +1,7 @@ +# tsearch2 extension +comment = 'compatibility package for pre-8.3 text search functions' +default_version = '1.0' +module_pathname = '$libdir/tsearch2' +# this is not relocatable because the tsearch2--unpackaged--1.0.sql script +# has to use @extschema@ to avoid conflict with items in pg_catalog +relocatable = false diff --git a/contrib/tsearch2/uninstall_tsearch2.sql b/contrib/tsearch2/uninstall_tsearch2.sql deleted file mode 100644 index f444a218e6..0000000000 --- a/contrib/tsearch2/uninstall_tsearch2.sql +++ /dev/null @@ -1,96 +0,0 @@ -/* contrib/tsearch2/uninstall_tsearch2.sql */ - --- Adjust this setting to control where the objects get dropped. -SET search_path = public, pg_catalog; - -DROP DOMAIN tsvector CASCADE; -DROP DOMAIN tsquery CASCADE; -DROP DOMAIN gtsvector CASCADE; -DROP DOMAIN gtsq CASCADE; - -DROP TYPE tokentype CASCADE; -DROP TYPE tokenout CASCADE; -DROP TYPE statinfo CASCADE; -DROP TYPE tsdebug CASCADE; - -DROP OPERATOR CLASS tsquery_ops USING btree CASCADE; - -DROP OPERATOR CLASS tsvector_ops USING btree CASCADE; - -DROP OPERATOR CLASS gin_tsvector_ops USING gin CASCADE; - -DROP OPERATOR CLASS gist_tp_tsquery_ops USING gist CASCADE; - -DROP OPERATOR CLASS gist_tsvector_ops USING gist CASCADE; - -DROP FUNCTION lexize(oid, text) ; -DROP FUNCTION lexize(text, text); -DROP FUNCTION lexize(text); -DROP FUNCTION set_curdict(int); -DROP FUNCTION set_curdict(text); -DROP FUNCTION dex_init(internal); -DROP FUNCTION dex_lexize(internal,internal,int4); -DROP FUNCTION snb_en_init(internal); -DROP FUNCTION snb_lexize(internal,internal,int4); -DROP FUNCTION snb_ru_init_koi8(internal); -DROP FUNCTION snb_ru_init_utf8(internal); -DROP FUNCTION snb_ru_init(internal); -DROP FUNCTION spell_init(internal); -DROP FUNCTION spell_lexize(internal,internal,int4); -DROP FUNCTION syn_init(internal); -DROP FUNCTION syn_lexize(internal,internal,int4); -DROP FUNCTION thesaurus_init(internal); -DROP FUNCTION thesaurus_lexize(internal,internal,int4,internal); -DROP FUNCTION set_curprs(int); -DROP FUNCTION set_curprs(text); -DROP FUNCTION prsd_start(internal,int4); -DROP FUNCTION prsd_getlexeme(internal,internal,internal); -DROP FUNCTION prsd_end(internal); -DROP FUNCTION prsd_lextype(internal); -DROP FUNCTION prsd_headline(internal,internal,internal); -DROP FUNCTION set_curcfg(int); -DROP FUNCTION set_curcfg(text); -DROP FUNCTION show_curcfg(); -DROP FUNCTION length(tsvector); -DROP FUNCTION to_tsvector(oid, text); -DROP FUNCTION to_tsvector(text, text); -DROP FUNCTION to_tsvector(text); -DROP FUNCTION strip(tsvector); -DROP FUNCTION setweight(tsvector,"char"); -DROP FUNCTION concat(tsvector,tsvector); -DROP FUNCTION querytree(tsquery); -DROP FUNCTION to_tsquery(oid, text); -DROP FUNCTION to_tsquery(text, text); -DROP FUNCTION to_tsquery(text); -DROP FUNCTION plainto_tsquery(oid, text); -DROP FUNCTION plainto_tsquery(text, text); -DROP FUNCTION plainto_tsquery(text); -DROP FUNCTION tsearch2() CASCADE; -DROP FUNCTION rank(float4[], tsvector, tsquery); -DROP FUNCTION rank(float4[], tsvector, tsquery, int4); -DROP FUNCTION rank(tsvector, tsquery); -DROP FUNCTION rank(tsvector, tsquery, int4); -DROP FUNCTION rank_cd(float4[], tsvector, tsquery); -DROP FUNCTION rank_cd(float4[], tsvector, tsquery, int4); -DROP FUNCTION rank_cd(tsvector, tsquery); -DROP FUNCTION rank_cd(tsvector, tsquery, int4); -DROP FUNCTION headline(oid, text, tsquery, text); -DROP FUNCTION headline(oid, text, tsquery); -DROP FUNCTION headline(text, text, tsquery, text); -DROP FUNCTION headline(text, text, tsquery); -DROP FUNCTION headline(text, tsquery, text); -DROP FUNCTION headline(text, tsquery); -DROP FUNCTION get_covers(tsvector,tsquery); -DROP FUNCTION _get_parser_from_curcfg(); -DROP FUNCTION numnode(tsquery); -DROP FUNCTION tsquery_and(tsquery,tsquery); -DROP FUNCTION tsquery_or(tsquery,tsquery); -DROP FUNCTION tsquery_not(tsquery); -DROP FUNCTION rewrite(tsquery, text); -DROP FUNCTION rewrite(tsquery, tsquery, tsquery); -DROP AGGREGATE rewrite (tsquery[]); -DROP FUNCTION rewrite_accum(tsquery,tsquery[]); -DROP FUNCTION rewrite_finish(tsquery); -DROP FUNCTION tsq_mcontains(tsquery, tsquery); -DROP FUNCTION tsq_mcontained(tsquery, tsquery); -DROP FUNCTION reset_tsearch(); diff --git a/contrib/unaccent/.gitignore b/contrib/unaccent/.gitignore index 6d74a7617f..19b6c5ba42 100644 --- a/contrib/unaccent/.gitignore +++ b/contrib/unaccent/.gitignore @@ -1,3 +1,2 @@ -/unaccent.sql # Generated subdirectories /results/ diff --git a/contrib/unaccent/Makefile b/contrib/unaccent/Makefile index 254155dcca..13cd8538d3 100644 --- a/contrib/unaccent/Makefile +++ b/contrib/unaccent/Makefile @@ -3,9 +3,10 @@ MODULE_big = unaccent OBJS = unaccent.o -DATA_built = unaccent.sql -DATA = uninstall_unaccent.sql +EXTENSION = unaccent +DATA = unaccent--1.0.sql unaccent--unpackaged--1.0.sql DATA_TSEARCH = unaccent.rules + REGRESS = unaccent # Adjust REGRESS_OPTS because we need a UTF8 database diff --git a/contrib/unaccent/expected/unaccent.out b/contrib/unaccent/expected/unaccent.out index a09e00fe5b..b93105e9c7 100644 --- a/contrib/unaccent/expected/unaccent.out +++ b/contrib/unaccent/expected/unaccent.out @@ -1,6 +1,4 @@ -SET client_min_messages = warning; -\set ECHO none -RESET client_min_messages; +CREATE EXTENSION unaccent; -- must have a UTF8 database SELECT getdatabaseencoding(); getdatabaseencoding diff --git a/contrib/unaccent/sql/unaccent.sql b/contrib/unaccent/sql/unaccent.sql index ede938d479..310213994f 100644 --- a/contrib/unaccent/sql/unaccent.sql +++ b/contrib/unaccent/sql/unaccent.sql @@ -1,8 +1,4 @@ -SET client_min_messages = warning; -\set ECHO none -\i unaccent.sql -\set ECHO all -RESET client_min_messages; +CREATE EXTENSION unaccent; -- must have a UTF8 database SELECT getdatabaseencoding(); diff --git a/contrib/unaccent/unaccent.sql.in b/contrib/unaccent/unaccent--1.0.sql index 6d712e7bb8..b5909e0b55 100644 --- a/contrib/unaccent/unaccent.sql.in +++ b/contrib/unaccent/unaccent--1.0.sql @@ -1,24 +1,21 @@ -/* contrib/unaccent/unaccent.sql.in */ +/* contrib/unaccent/unaccent--1.0.sql */ --- Adjust this setting to control where the objects get created. -SET search_path = public; - -CREATE OR REPLACE FUNCTION unaccent(regdictionary, text) +CREATE FUNCTION unaccent(regdictionary, text) RETURNS text AS 'MODULE_PATHNAME', 'unaccent_dict' LANGUAGE C STABLE STRICT; -CREATE OR REPLACE FUNCTION unaccent(text) +CREATE FUNCTION unaccent(text) RETURNS text AS 'MODULE_PATHNAME', 'unaccent_dict' LANGUAGE C STABLE STRICT; -CREATE OR REPLACE FUNCTION unaccent_init(internal) +CREATE FUNCTION unaccent_init(internal) RETURNS internal AS 'MODULE_PATHNAME', 'unaccent_init' LANGUAGE C; -CREATE OR REPLACE FUNCTION unaccent_lexize(internal,internal,internal,internal) +CREATE FUNCTION unaccent_lexize(internal,internal,internal,internal) RETURNS internal AS 'MODULE_PATHNAME', 'unaccent_lexize' LANGUAGE C; diff --git a/contrib/unaccent/unaccent--unpackaged--1.0.sql b/contrib/unaccent/unaccent--unpackaged--1.0.sql new file mode 100644 index 0000000000..7b36d29512 --- /dev/null +++ b/contrib/unaccent/unaccent--unpackaged--1.0.sql @@ -0,0 +1,8 @@ +/* contrib/unaccent/unaccent--unpackaged--1.0.sql */ + +ALTER EXTENSION unaccent ADD function unaccent(regdictionary,text); +ALTER EXTENSION unaccent ADD function unaccent(text); +ALTER EXTENSION unaccent ADD function unaccent_init(internal); +ALTER EXTENSION unaccent ADD function unaccent_lexize(internal,internal,internal,internal); +ALTER EXTENSION unaccent ADD text search template unaccent; +ALTER EXTENSION unaccent ADD text search dictionary unaccent; diff --git a/contrib/unaccent/unaccent.control b/contrib/unaccent/unaccent.control new file mode 100644 index 0000000000..200d2ae7bb --- /dev/null +++ b/contrib/unaccent/unaccent.control @@ -0,0 +1,5 @@ +# unaccent extension +comment = 'text search dictionary that removes accents' +default_version = '1.0' +module_pathname = '$libdir/unaccent' +relocatable = true diff --git a/contrib/unaccent/uninstall_unaccent.sql b/contrib/unaccent/uninstall_unaccent.sql deleted file mode 100644 index 6879d4f74c..0000000000 --- a/contrib/unaccent/uninstall_unaccent.sql +++ /dev/null @@ -1,11 +0,0 @@ -/* contrib/unaccent/uninstall_unaccent.sql */ - --- Adjust this setting to control where the objects get dropped. -SET search_path = public; - -DROP FUNCTION unaccent(regdictionary, text); -DROP FUNCTION unaccent(text); -DROP TEXT SEARCH DICTIONARY unaccent; -DROP TEXT SEARCH TEMPLATE unaccent; -DROP FUNCTION unaccent_init(internal); -DROP FUNCTION unaccent_lexize(internal,internal,internal,internal); diff --git a/contrib/uuid-ossp/.gitignore b/contrib/uuid-ossp/.gitignore deleted file mode 100644 index ece095ad7b..0000000000 --- a/contrib/uuid-ossp/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/uuid-ossp.sql diff --git a/contrib/uuid-ossp/Makefile b/contrib/uuid-ossp/Makefile index 77ea87409f..9b2d2e3ff9 100644 --- a/contrib/uuid-ossp/Makefile +++ b/contrib/uuid-ossp/Makefile @@ -2,8 +2,9 @@ MODULE_big = uuid-ossp OBJS = uuid-ossp.o -DATA_built = uuid-ossp.sql -DATA = uninstall_uuid-ossp.sql + +EXTENSION = uuid-ossp +DATA = uuid-ossp--1.0.sql uuid-ossp--unpackaged--1.0.sql SHLIB_LINK += $(OSSP_UUID_LIBS) diff --git a/contrib/uuid-ossp/uninstall_uuid-ossp.sql b/contrib/uuid-ossp/uninstall_uuid-ossp.sql deleted file mode 100644 index 0fafb2721f..0000000000 --- a/contrib/uuid-ossp/uninstall_uuid-ossp.sql +++ /dev/null @@ -1,16 +0,0 @@ -/* contrib/uuid-ossp/uninstall_uuid-ossp.sql */ - --- Adjust this setting to control where the objects get dropped. -SET search_path = public; - -DROP FUNCTION uuid_nil(); -DROP FUNCTION uuid_ns_dns(); -DROP FUNCTION uuid_ns_url(); -DROP FUNCTION uuid_ns_oid(); -DROP FUNCTION uuid_ns_x500(); - -DROP FUNCTION uuid_generate_v1(); -DROP FUNCTION uuid_generate_v1mc(); -DROP FUNCTION uuid_generate_v3(namespace uuid, name text); -DROP FUNCTION uuid_generate_v4(); -DROP FUNCTION uuid_generate_v5(namespace uuid, name text); diff --git a/contrib/uuid-ossp/uuid-ossp.sql.in b/contrib/uuid-ossp/uuid-ossp--1.0.sql index 71212cde48..43ae0b38a0 100644 --- a/contrib/uuid-ossp/uuid-ossp.sql.in +++ b/contrib/uuid-ossp/uuid-ossp--1.0.sql @@ -1,54 +1,51 @@ -/* contrib/uuid-ossp/uuid-ossp.sql.in */ +/* contrib/uuid-ossp/uuid-ossp--1.0.sql */ --- Adjust this setting to control where the objects get created. -SET search_path = public; - -CREATE OR REPLACE FUNCTION uuid_nil() +CREATE FUNCTION uuid_nil() RETURNS uuid AS 'MODULE_PATHNAME', 'uuid_nil' IMMUTABLE STRICT LANGUAGE C; -CREATE OR REPLACE FUNCTION uuid_ns_dns() +CREATE FUNCTION uuid_ns_dns() RETURNS uuid AS 'MODULE_PATHNAME', 'uuid_ns_dns' IMMUTABLE STRICT LANGUAGE C; -CREATE OR REPLACE FUNCTION uuid_ns_url() +CREATE FUNCTION uuid_ns_url() RETURNS uuid AS 'MODULE_PATHNAME', 'uuid_ns_url' IMMUTABLE STRICT LANGUAGE C; -CREATE OR REPLACE FUNCTION uuid_ns_oid() +CREATE FUNCTION uuid_ns_oid() RETURNS uuid AS 'MODULE_PATHNAME', 'uuid_ns_oid' IMMUTABLE STRICT LANGUAGE C; -CREATE OR REPLACE FUNCTION uuid_ns_x500() +CREATE FUNCTION uuid_ns_x500() RETURNS uuid AS 'MODULE_PATHNAME', 'uuid_ns_x500' IMMUTABLE STRICT LANGUAGE C; -CREATE OR REPLACE FUNCTION uuid_generate_v1() +CREATE FUNCTION uuid_generate_v1() RETURNS uuid AS 'MODULE_PATHNAME', 'uuid_generate_v1' VOLATILE STRICT LANGUAGE C; -CREATE OR REPLACE FUNCTION uuid_generate_v1mc() +CREATE FUNCTION uuid_generate_v1mc() RETURNS uuid AS 'MODULE_PATHNAME', 'uuid_generate_v1mc' VOLATILE STRICT LANGUAGE C; -CREATE OR REPLACE FUNCTION uuid_generate_v3(namespace uuid, name text) +CREATE FUNCTION uuid_generate_v3(namespace uuid, name text) RETURNS uuid AS 'MODULE_PATHNAME', 'uuid_generate_v3' IMMUTABLE STRICT LANGUAGE C; -CREATE OR REPLACE FUNCTION uuid_generate_v4() +CREATE FUNCTION uuid_generate_v4() RETURNS uuid AS 'MODULE_PATHNAME', 'uuid_generate_v4' VOLATILE STRICT LANGUAGE C; -CREATE OR REPLACE FUNCTION uuid_generate_v5(namespace uuid, name text) +CREATE FUNCTION uuid_generate_v5(namespace uuid, name text) RETURNS uuid AS 'MODULE_PATHNAME', 'uuid_generate_v5' IMMUTABLE STRICT LANGUAGE C; diff --git a/contrib/uuid-ossp/uuid-ossp--unpackaged--1.0.sql b/contrib/uuid-ossp/uuid-ossp--unpackaged--1.0.sql new file mode 100644 index 0000000000..bc984dd8c0 --- /dev/null +++ b/contrib/uuid-ossp/uuid-ossp--unpackaged--1.0.sql @@ -0,0 +1,12 @@ +/* contrib/uuid-ossp/uuid-ossp--unpackaged--1.0.sql */ + +ALTER EXTENSION "uuid-ossp" ADD function uuid_nil(); +ALTER EXTENSION "uuid-ossp" ADD function uuid_ns_dns(); +ALTER EXTENSION "uuid-ossp" ADD function uuid_ns_url(); +ALTER EXTENSION "uuid-ossp" ADD function uuid_ns_oid(); +ALTER EXTENSION "uuid-ossp" ADD function uuid_ns_x500(); +ALTER EXTENSION "uuid-ossp" ADD function uuid_generate_v1(); +ALTER EXTENSION "uuid-ossp" ADD function uuid_generate_v1mc(); +ALTER EXTENSION "uuid-ossp" ADD function uuid_generate_v3(namespace uuid, name text); +ALTER EXTENSION "uuid-ossp" ADD function uuid_generate_v4(); +ALTER EXTENSION "uuid-ossp" ADD function uuid_generate_v5(namespace uuid, name text); diff --git a/contrib/uuid-ossp/uuid-ossp.control b/contrib/uuid-ossp/uuid-ossp.control new file mode 100644 index 0000000000..f52ae99d41 --- /dev/null +++ b/contrib/uuid-ossp/uuid-ossp.control @@ -0,0 +1,5 @@ +# uuid-ossp extension +comment = 'generate universally unique identifiers (UUIDs)' +default_version = '1.0' +module_pathname = '$libdir/uuid-ossp' +relocatable = true diff --git a/contrib/xml2/.gitignore b/contrib/xml2/.gitignore index 5ef9dbf4d4..19b6c5ba42 100644 --- a/contrib/xml2/.gitignore +++ b/contrib/xml2/.gitignore @@ -1,3 +1,2 @@ -/pgxml.sql # Generated subdirectories /results/ diff --git a/contrib/xml2/Makefile b/contrib/xml2/Makefile index 57b4cbfac5..ad325723c9 100644 --- a/contrib/xml2/Makefile +++ b/contrib/xml2/Makefile @@ -1,15 +1,15 @@ # contrib/xml2/Makefile MODULE_big = pgxml - OBJS = xpath.o xslt_proc.o -SHLIB_LINK += $(filter -lxslt, $(LIBS)) $(filter -lxml2, $(LIBS)) +EXTENSION = xml2 +DATA = xml2--1.0.sql xml2--unpackaged--1.0.sql -DATA_built = pgxml.sql -DATA = uninstall_pgxml.sql REGRESS = xml2 +SHLIB_LINK += $(filter -lxslt, $(LIBS)) $(filter -lxml2, $(LIBS)) + ifdef USE_PGXS PG_CONFIG = pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) diff --git a/contrib/xml2/expected/xml2.out b/contrib/xml2/expected/xml2.out index 8ce04d0b84..3bf676fb40 100644 --- a/contrib/xml2/expected/xml2.out +++ b/contrib/xml2/expected/xml2.out @@ -1,10 +1,4 @@ --- --- first, define the functions. Turn off echoing so that expected file --- does not depend on contents of pgxml.sql. --- -SET client_min_messages = warning; -\set ECHO none -RESET client_min_messages; +CREATE EXTENSION xml2; select query_to_xml('select 1 as x',true,false,''); query_to_xml --------------------------------------------------------------- diff --git a/contrib/xml2/expected/xml2_1.out b/contrib/xml2/expected/xml2_1.out index d2d243ada7..fda626e08c 100644 --- a/contrib/xml2/expected/xml2_1.out +++ b/contrib/xml2/expected/xml2_1.out @@ -1,10 +1,4 @@ --- --- first, define the functions. Turn off echoing so that expected file --- does not depend on contents of pgxml.sql. --- -SET client_min_messages = warning; -\set ECHO none -RESET client_min_messages; +CREATE EXTENSION xml2; select query_to_xml('select 1 as x',true,false,''); query_to_xml --------------------------------------------------------------- diff --git a/contrib/xml2/sql/xml2.sql b/contrib/xml2/sql/xml2.sql index 5b3cc997f5..4a996af716 100644 --- a/contrib/xml2/sql/xml2.sql +++ b/contrib/xml2/sql/xml2.sql @@ -1,12 +1,4 @@ --- --- first, define the functions. Turn off echoing so that expected file --- does not depend on contents of pgxml.sql. --- -SET client_min_messages = warning; -\set ECHO none -\i pgxml.sql -\set ECHO all -RESET client_min_messages; +CREATE EXTENSION xml2; select query_to_xml('select 1 as x',true,false,''); diff --git a/contrib/xml2/uninstall_pgxml.sql b/contrib/xml2/uninstall_pgxml.sql deleted file mode 100644 index 1696390f80..0000000000 --- a/contrib/xml2/uninstall_pgxml.sql +++ /dev/null @@ -1,31 +0,0 @@ -/* contrib/xml2/uninstall_pgxml.sql */ - --- Adjust this setting to control where the objects get dropped. -SET search_path = public; - -DROP FUNCTION xslt_process(text,text); - -DROP FUNCTION xslt_process(text,text,text); - -DROP FUNCTION xpath_table(text,text,text,text,text); - -DROP FUNCTION xpath_nodeset(text,text,text); - -DROP FUNCTION xpath_nodeset(text,text); - -DROP FUNCTION xpath_list(text,text); - -DROP FUNCTION xpath_list(text,text,text); - -DROP FUNCTION xpath_bool(text,text); - -DROP FUNCTION xpath_number(text,text); - -DROP FUNCTION xpath_nodeset(text,text,text,text); - -DROP FUNCTION xpath_string(text,text); - -DROP FUNCTION xml_encode_special_chars(text); - --- deprecated old name for xml_is_well_formed -DROP FUNCTION xml_valid(text); diff --git a/contrib/xml2/pgxml.sql.in b/contrib/xml2/xml2--1.0.sql index 8c3d420afd..bc27dc89ca 100644 --- a/contrib/xml2/pgxml.sql.in +++ b/contrib/xml2/xml2--1.0.sql @@ -1,73 +1,70 @@ -/* contrib/xml2/pgxml.sql.in */ - --- Adjust this setting to control where the objects get created. -SET search_path = public; +/* contrib/xml2/xml2--1.0.sql */ --SQL for XML parser -- deprecated old name for xml_is_well_formed -CREATE OR REPLACE FUNCTION xml_valid(text) RETURNS bool +CREATE FUNCTION xml_valid(text) RETURNS bool AS 'xml_is_well_formed' LANGUAGE INTERNAL STRICT STABLE; -CREATE OR REPLACE FUNCTION xml_encode_special_chars(text) RETURNS text +CREATE FUNCTION xml_encode_special_chars(text) RETURNS text AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION xpath_string(text,text) RETURNS text +CREATE FUNCTION xpath_string(text,text) RETURNS text AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION xpath_nodeset(text,text,text,text) RETURNS text +CREATE FUNCTION xpath_nodeset(text,text,text,text) RETURNS text AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION xpath_number(text,text) RETURNS float4 +CREATE FUNCTION xpath_number(text,text) RETURNS float4 AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION xpath_bool(text,text) RETURNS boolean +CREATE FUNCTION xpath_bool(text,text) RETURNS boolean AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -- List function -CREATE OR REPLACE FUNCTION xpath_list(text,text,text) RETURNS text +CREATE FUNCTION xpath_list(text,text,text) RETURNS text AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION xpath_list(text,text) RETURNS text +CREATE FUNCTION xpath_list(text,text) RETURNS text AS 'SELECT xpath_list($1,$2,'','')' LANGUAGE SQL STRICT IMMUTABLE; -- Wrapper functions for nodeset where no tags needed -CREATE OR REPLACE FUNCTION xpath_nodeset(text,text) +CREATE FUNCTION xpath_nodeset(text,text) RETURNS text AS 'SELECT xpath_nodeset($1,$2,'''','''')' LANGUAGE SQL STRICT IMMUTABLE; -CREATE OR REPLACE FUNCTION xpath_nodeset(text,text,text) +CREATE FUNCTION xpath_nodeset(text,text,text) RETURNS text AS 'SELECT xpath_nodeset($1,$2,'''',$3)' LANGUAGE SQL STRICT IMMUTABLE; -- Table function -CREATE OR REPLACE FUNCTION xpath_table(text,text,text,text,text) +CREATE FUNCTION xpath_table(text,text,text,text,text) RETURNS setof record AS 'MODULE_PATHNAME' LANGUAGE C STRICT STABLE; -- XSLT functions -CREATE OR REPLACE FUNCTION xslt_process(text,text,text) +CREATE FUNCTION xslt_process(text,text,text) RETURNS text AS 'MODULE_PATHNAME' LANGUAGE C STRICT VOLATILE; -- the function checks for the correct argument count -CREATE OR REPLACE FUNCTION xslt_process(text,text) +CREATE FUNCTION xslt_process(text,text) RETURNS text AS 'MODULE_PATHNAME' LANGUAGE C STRICT IMMUTABLE; diff --git a/contrib/xml2/xml2--unpackaged--1.0.sql b/contrib/xml2/xml2--unpackaged--1.0.sql new file mode 100644 index 0000000000..2cd40d7960 --- /dev/null +++ b/contrib/xml2/xml2--unpackaged--1.0.sql @@ -0,0 +1,15 @@ +/* contrib/xml2/xml2--unpackaged--1.0.sql */ + +ALTER EXTENSION xml2 ADD function xslt_process(text,text); +ALTER EXTENSION xml2 ADD function xslt_process(text,text,text); +ALTER EXTENSION xml2 ADD function xpath_table(text,text,text,text,text); +ALTER EXTENSION xml2 ADD function xpath_nodeset(text,text,text); +ALTER EXTENSION xml2 ADD function xpath_nodeset(text,text); +ALTER EXTENSION xml2 ADD function xpath_list(text,text); +ALTER EXTENSION xml2 ADD function xpath_list(text,text,text); +ALTER EXTENSION xml2 ADD function xpath_bool(text,text); +ALTER EXTENSION xml2 ADD function xpath_number(text,text); +ALTER EXTENSION xml2 ADD function xpath_nodeset(text,text,text,text); +ALTER EXTENSION xml2 ADD function xpath_string(text,text); +ALTER EXTENSION xml2 ADD function xml_encode_special_chars(text); +ALTER EXTENSION xml2 ADD function xml_valid(text); diff --git a/contrib/xml2/xml2.control b/contrib/xml2/xml2.control new file mode 100644 index 0000000000..004649d652 --- /dev/null +++ b/contrib/xml2/xml2.control @@ -0,0 +1,5 @@ +# xml2 extension +comment = 'XPath querying and XSLT' +default_version = '1.0' +module_pathname = '$libdir/pgxml' +relocatable = true diff --git a/doc/src/sgml/acronyms.sgml b/doc/src/sgml/acronyms.sgml index d1ef489e36..8f6752f05d 100644 --- a/doc/src/sgml/acronyms.sgml +++ b/doc/src/sgml/acronyms.sgml @@ -485,7 +485,7 @@ <term><acronym>PGXS</acronym></term> <listitem> <para> - <link linkend="xfunc-c-pgxs"><productname>PostgreSQL</> Extension System</link> + <link linkend="extend-pgxs"><productname>PostgreSQL</> Extension System</link> </para> </listitem> </varlistentry> diff --git a/doc/src/sgml/backup.sgml b/doc/src/sgml/backup.sgml index 62830cb6ca..0fbf1225aa 100644 --- a/doc/src/sgml/backup.sgml +++ b/doc/src/sgml/backup.sgml @@ -1086,9 +1086,10 @@ restore_command = 'cp /mnt/server/archivedir/%f %p' the junior DBA dropped your main transaction table), just specify the required stopping point in <filename>recovery.conf</>. You can specify the stop point, known as the <quote>recovery target</>, either by - date/time or by completion of a specific transaction ID. As of this - writing only the date/time option is very usable, since there are no tools - to help you identify with any accuracy which transaction ID to use. + date/time, named restore point or by completion of a specific transaction + ID. As of this writing only the date/time and named restore point options + are very usable, since there are no tools to help you identify with any + accuracy which transaction ID to use. </para> <note> diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index f4cb8857a7..1ea2ba496e 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -104,6 +104,11 @@ </row> <row> + <entry><link linkend="catalog-pg-collation"><structname>pg_collation</structname></link></entry> + <entry>collations (locale information)</entry> + </row> + + <row> <entry><link linkend="catalog-pg-conversion"><structname>pg_conversion</structname></link></entry> <entry>encoding conversion information</entry> </row> @@ -139,6 +144,11 @@ </row> <row> + <entry><link linkend="catalog-pg-extension"><structname>pg_extension</structname></link></entry> + <entry>installed extensions</entry> + </row> + + <row> <entry><link linkend="catalog-pg-foreign-data-wrapper"><structname>pg_foreign_data_wrapper</structname></link></entry> <entry>foreign-data wrapper definitions</entry> </row> @@ -1114,6 +1124,16 @@ </row> <row> + <entry><structfield>attcollation</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-collation"><structname>pg_collation</structname></link>.oid</literal></entry> + <entry> + The defined collation of the column, zero if the column does + not have a collatable type. + </entry> + </row> + + <row> <entry><structfield>attacl</structfield></entry> <entry><type>aclitem[]</type></entry> <entry></entry> @@ -1875,6 +1895,13 @@ </row> <row> + <entry><structfield>convalidated</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>Has the constraint been validated? Can only be false for foreign keys</entry> + </row> + + <row> <entry><structfield>conrelid</structfield></entry> <entry><type>oid</type></entry> <entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.oid</literal></entry> @@ -2050,6 +2077,95 @@ </sect1> + <sect1 id="catalog-pg-collation"> + <title><structname>pg_collation</structname></title> + + <indexterm zone="catalog-pg-collation"> + <primary>pg_collation</primary> + </indexterm> + + <para> + The catalog <structname>pg_collation</structname> describes the + available collations, which are essentially mappings from an SQL + name to operating system locale categories. + See <xref linkend="locale"> for more information. + </para> + + <table> + <title><structname>pg_collation</> Columns</title> + + <tgroup cols="4"> + <thead> + <row> + <entry>Name</entry> + <entry>Type</entry> + <entry>References</entry> + <entry>Description</entry> + </row> + </thead> + + <tbody> + <row> + <entry><structfield>collname</structfield></entry> + <entry><type>name</type></entry> + <entry></entry> + <entry>Collation name (unique per namespace and encoding)</entry> + </row> + + <row> + <entry><structfield>collnamespace</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-namespace"><structname>pg_namespace</structname></link>.oid</literal></entry> + <entry> + The OID of the namespace that contains this collation + </entry> + </row> + + <row> + <entry><structfield>collowner</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-authid"><structname>pg_authid</structname></link>.oid</literal></entry> + <entry>Owner of the collation</entry> + </row> + + <row> + <entry><structfield>collencoding</structfield></entry> + <entry><type>int4</type></entry> + <entry></entry> + <entry> + Encoding to which the collation is applicable. SQL-level + commands such as <command>ALTER COLLATION</command> only + operate on the collation belonging to the current database + encoding. But this field is necessary because when this + catalog is initialized, the encoding of future databases is not + yet known. For practical purposes, collations that do not + match the current database encoding should be considered + invalid or invisible. It could be useful, however, to create + collations whose encoding does not match the database encoding + in template databases. This would currently have to be done + manually. + </entry> + </row> + + <row> + <entry><structfield>collcollate</structfield></entry> + <entry><type>name</type></entry> + <entry></entry> + <entry>LC_COLLATE for this collation object</entry> + </row> + + <row> + <entry><structfield>collctype</structfield></entry> + <entry><type>name</type></entry> + <entry></entry> + <entry>LC_CTYPE for this collation object</entry> + </row> + </tbody> + </tgroup> + </table> + + </sect1> + <sect1 id="catalog-pg-conversion"> <title><structname>pg_conversion</structname></title> @@ -2595,6 +2711,21 @@ </varlistentry> <varlistentry> + <term><symbol>DEPENDENCY_EXTENSION</> (<literal>e</>)</term> + <listitem> + <para> + The dependent object is a member of the <firstterm>extension</> that is + the referenced object (see + <link linkend="catalog-pg-extension"><structname>pg_extension</structname></link>). + The dependent object can be dropped only via + <command>DROP EXTENSION</> on the referenced object. Functionally + this dependency type acts the same as an internal dependency, but + it's kept separate for clarity and to simplify <application>pg_dump</>. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><symbol>DEPENDENCY_PIN</> (<literal>p</>)</term> <listitem> <para> @@ -2763,6 +2894,101 @@ </sect1> + <sect1 id="catalog-pg-extension"> + <title><structname>pg_extension</structname></title> + + <indexterm zone="catalog-pg-extension"> + <primary>pg_extension</primary> + </indexterm> + + <para> + The catalog <structname>pg_extension</structname> stores information + about the installed extensions. See <xref linkend="extend-extensions"> + for details about extensions. + </para> + + <table> + <title><structname>pg_extension</> Columns</title> + + <tgroup cols="4"> + <thead> + <row> + <entry>Name</entry> + <entry>Type</entry> + <entry>References</entry> + <entry>Description</entry> + </row> + </thead> + + <tbody> + <row> + <entry><structfield>extname</structfield></entry> + <entry><type>name</type></entry> + <entry></entry> + <entry>Name of the extension</entry> + </row> + + <row> + <entry><structfield>extowner</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-authid"><structname>pg_authid</structname></link>.oid</literal></entry> + <entry>Owner of the extension</entry> + </row> + + <row> + <entry><structfield>extnamespace</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-namespace"><structname>pg_namespace</structname></link>.oid</literal></entry> + <entry>Schema containing the extension's exported objects</entry> + </row> + + <row> + <entry><structfield>extrelocatable</structfield></entry> + <entry><type>bool</type></entry> + <entry></entry> + <entry>True if extension can be relocated to another schema</entry> + </row> + + <row> + <entry><structfield>extversion</structfield></entry> + <entry><type>text</type></entry> + <entry></entry> + <entry>Version name for the extension</entry> + </row> + + <row> + <entry><structfield>extconfig</structfield></entry> + <entry><type>oid[]</type></entry> + <entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.oid</literal></entry> + <entry>Array of <type>regclass</> OIDs for the extension's configuration + table(s), or <literal>NULL</> if none</entry> + </row> + + <row> + <entry><structfield>extcondition</structfield></entry> + <entry><type>text[]</type></entry> + <entry></entry> + <entry>Array of <literal>WHERE</>-clause filter conditions for the + extension's configuration table(s), or <literal>NULL</> if none</entry> + </row> + + </tbody> + </tgroup> + </table> + + <para> + Note that unlike most catalogs with a <quote>namespace</> column, + <structfield>extnamespace</structfield> is not meant to imply + that the extension belongs to that schema. Extension names are never + schema-qualified. Rather, <structfield>extnamespace</structfield> + indicates the schema that contains most or all of the extension's + objects. If <structfield>extrelocatable</structfield> is true, then + this schema must in fact contain all schema-qualifiable objects + belonging to the extension. + </para> + </sect1> + + <sect1 id="catalog-pg-foreign-data-wrapper"> <title><structname>pg_foreign_data_wrapper</structname></title> @@ -3137,6 +3363,16 @@ </row> <row> + <entry><structfield>indcollation</structfield></entry> + <entry><type>oidvector</type></entry> + <entry><literal><link linkend="catalog-pg-collation"><structname>pg_collation</structname></link>.oid</literal></entry> + <entry> + For each column in the index key, this contains the OID of the + collation to use for the index. + </entry> + </row> + + <row> <entry><structfield>indclass</structfield></entry> <entry><type>oidvector</type></entry> <entry><literal><link linkend="catalog-pg-opclass"><structname>pg_opclass</structname></link>.oid</literal></entry> @@ -5878,6 +6114,21 @@ </row> <row> + <entry><structfield>typcollation</structfield></entry> + <entry><type>oid</type></entry> + <entry><literal><link linkend="catalog-pg-collation"><structname>pg_collation</structname></link>.oid</literal></entry> + <entry><para> + <structfield>typcollation</structfield> specifies the collation + of the type. If a type does not support collations, this will + be zero, collation analysis at parse time is skipped, and + the use of <literal>COLLATE</literal> clauses with the type is + invalid. A base type that supports collations will have + <symbol>DEFAULT_COLLATION_OID</symbol> here. A domain can have + another collation OID, if one was defined for the domain. + </para></entry> + </row> + + <row> <entry><structfield>typdefaultbin</structfield></entry> <entry><type>pg_node_tree</type></entry> <entry></entry> @@ -6093,6 +6344,16 @@ <tbody> <row> + <entry><link linkend="view-pg-available-extensions"><structname>pg_available_extensions</structname></link></entry> + <entry>available extensions</entry> + </row> + + <row> + <entry><link linkend="view-pg-available-extension-versions"><structname>pg_available_extension_versions</structname></link></entry> + <entry>available versions of extensions</entry> + </row> + + <row> <entry><link linkend="view-pg-cursors"><structname>pg_cursors</structname></link></entry> <entry>open cursors</entry> </row> @@ -6187,6 +6448,150 @@ </table> </sect1> + <sect1 id="view-pg-available-extensions"> + <title><structname>pg_available_extensions</structname></title> + + <indexterm zone="view-pg-available-extensions"> + <primary>pg_available_extensions</primary> + </indexterm> + + <para> + The <structname>pg_available_extensions</structname> view lists the + extensions that are available for installation. This view can only + be read by superusers. See also the + <link linkend="catalog-pg-extension"><structname>pg_extension</structname></link> + catalog, which shows the extensions currently installed. + </para> + + <table> + <title><structname>pg_available_extensions</> Columns</title> + + <tgroup cols="3"> + <thead> + <row> + <entry>Name</entry> + <entry>Type</entry> + <entry>Description</entry> + </row> + </thead> + + <tbody> + <row> + <entry><structfield>name</structfield></entry> + <entry><type>name</type></entry> + <entry>Extension name</entry> + </row> + + <row> + <entry><structfield>default_version</structfield></entry> + <entry><type>text</type></entry> + <entry>Name of default version, or <literal>NULL</literal> if none is + specified</entry> + </row> + + <row> + <entry><structfield>installed_version</structfield></entry> + <entry><type>text</type></entry> + <entry>Currently installed version of the extension, + or <literal>NULL</literal> if not installed</entry> + </row> + + <row> + <entry><structfield>comment</structfield></entry> + <entry><type>text</type></entry> + <entry>Comment string from the extension's control file</entry> + </row> + </tbody> + </tgroup> + </table> + + <para> + The <structname>pg_available_extensions</structname> view is read only. + </para> + </sect1> + + <sect1 id="view-pg-available-extension-versions"> + <title><structname>pg_available_extension_versions</structname></title> + + <indexterm zone="view-pg-available-extension-versions"> + <primary>pg_available_extension_versions</primary> + </indexterm> + + <para> + The <structname>pg_available_extension_versions</structname> view lists the + specific extension versions that are available for installation. This view + can only be read by superusers. See also the <link + linkend="catalog-pg-extension"><structname>pg_extension</structname></link> + catalog, which shows the extensions currently installed. + </para> + + <table> + <title><structname>pg_available_extension_versions</> Columns</title> + + <tgroup cols="3"> + <thead> + <row> + <entry>Name</entry> + <entry>Type</entry> + <entry>Description</entry> + </row> + </thead> + + <tbody> + <row> + <entry><structfield>name</structfield></entry> + <entry><type>name</type></entry> + <entry>Extension name</entry> + </row> + + <row> + <entry><structfield>version</structfield></entry> + <entry><type>text</type></entry> + <entry>Version name</entry> + </row> + + <row> + <entry><structfield>installed</structfield></entry> + <entry><type>bool</type></entry> + <entry>True if this version of this extension is currently + installed</entry> + </row> + + <row> + <entry><structfield>relocatable</structfield></entry> + <entry><type>bool</type></entry> + <entry>True if extension can be relocated to another schema</entry> + </row> + + <row> + <entry><structfield>schema</structfield></entry> + <entry><type>name</type></entry> + <entry>Name of the schema that the extension must be installed into, + or <literal>NULL</literal> if partially or fully relocatable</entry> + </row> + + <row> + <entry><structfield>requires</structfield></entry> + <entry><type>name[]</type></entry> + <entry>Names of prerequisite extensions, + or <literal>NULL</literal> if none</entry> + </row> + + <row> + <entry><structfield>comment</structfield></entry> + <entry><type>text</type></entry> + <entry>Comment string from the extension's control file</entry> + </row> + </tbody> + </tgroup> + </table> + + <para> + The <structname>pg_available_extension_versions</structname> view is read + only. + </para> + </sect1> + <sect1 id="view-pg-cursors"> <title><structname>pg_cursors</structname></title> diff --git a/doc/src/sgml/charset.sgml b/doc/src/sgml/charset.sgml index 644c711dcc..046c3d1416 100644 --- a/doc/src/sgml/charset.sgml +++ b/doc/src/sgml/charset.sgml @@ -304,6 +304,171 @@ initdb --locale=sv_SE </sect1> + <sect1 id="collation"> + <title>Collation Support</title> + + <para> + The collation support allows specifying the sort order and certain + other locale aspects of data per column or per operation at run + time. This alleviates the problem that the + <symbol>LC_COLLATE</symbol> and <symbol>LC_CTYPE</symbol> settings + of a database cannot be changed after its creation. + </para> + + <note> + <para> + The collation support feature is currently only known to work on + Linux/glibc and Mac OS X platforms. + </para> + </note> + + <sect2> + <title>Concepts</title> + + <para> + Conceptually, every datum of a collatable data type has a + collation. (Collatable data types in the base system are + <type>text</type>, <type>varchar</type>, and <type>char</type>. + User-defined base types can also be marked collatable.) If the + datum is a column reference, the collation of the datum is the + defined collation of the column. If the datum is a constant, the + collation is the default collation of the data type of the + constant. The collation of more complex expressions is derived + from the input collations as described below. + </para> + + <para> + The collation of a datum can also be the <quote>default</quote> + collation, which reverts to the locale settings defined for the + database. In some cases, a datum can also have no known + collation. In such cases, ordering operations and other + operations that need to know the collation will fail. + </para> + + <para> + When the database system has to perform an ordering or a + comparison, it considers the collation of the input data. This + happens in two situations: an <literal>ORDER BY</literal> clause + and a function or operator call such as <literal><</literal>. + The collation to apply for the performance of the <literal>ORDER + BY</literal> clause is simply the collation of the sort key. The + collation to apply for a function or operator call is derived from + the arguments, as described below. Additionally, collations are + taken into account by functions that convert between lower and + upper case letters, that is, <function>lower</function>, + <function>upper</function>, and <function>initcap</function>. + </para> + + <para> + For a function call, the collation that is derived from combining + the argument collations is both used for performing any + comparisons or ordering and for the collation of the function + result, if the result type is collatable. + </para> + + <para> + The <firstterm>collation derivation</firstterm> of a datum can be + implicit or explicit. This distinction affects how collations are + combined when multiple different collations appear in an + expression. An explicit collation derivation arises when a + <literal>COLLATE</literal> clause is used; all other collation + derivations are implicit. When multiple collations need to be + combined, for example in a function call, the following rules are + used: + + <orderedlist> + <listitem> + <para> + If any input item has an explicit collation derivation, then + all explicitly derived collations among the input items must be + the same, otherwise an error is raised. If an explicitly + derived collation is present, that is the result of the + collation combination. + </para> + </listitem> + + <listitem> + <para> + Otherwise, all input items must have the same implicit + collation derivation or the default collation. If an + implicitly derived collation is present, that is the result of + the collation combination. Otherwise, the result is the + default collation. + </para> + </listitem> + </orderedlist> + + For example, take this table definition: +<programlisting> +CREATE TABLE test1 ( + a text COLLATE "x", + ... +); +</programlisting> + + Then in +<programlisting> +SELECT a || 'foo' FROM test1; +</programlisting> + the result collation of the <literal>||</literal> operator is + <literal>"x"</literal> because it combines an implicitly derived + collation with the default collation. But in +<programlisting> +SELECT a || ('foo' COLLATE "y") FROM test1; +</programlisting> + the result collation is <literal>"y"</literal> because the explicit + collation derivation overrides the implicit one. + </para> + </sect2> + + <sect2> + <title>Managing Collations</title> + + <para> + A collation is an SQL schema object that maps an SQL name to + operating system locales. In particular, it maps to a combination + of <symbol>LC_COLLATE</symbol> and <symbol>LC_CTYPE</symbol>. (As + the name would indicate, the main purpose of a collation is to set + <symbol>LC_COLLATE</symbol>, which controls the sort order. But + it is rarely necessary in practice to have an + <symbol>LC_CTYPE</symbol> setting that is different from + <symbol>LC_COLLATE</symbol>, so it is more convenient to collect + these under one concept than to create another infrastructure for + setting <symbol>LC_CTYPE</symbol> per datum.) Also, a collation + is tied to a character encoding. The same collation name may + exist for different encodings. + </para> + + <para> + When a database system is initialized, <command>initdb</command> + populates the system catalog <literal>pg_collation</literal> with + collations based on all the locales it finds on the operating + system at the time. For example, the operating system might + provide a locale named <literal>de_DE.utf8</literal>. + <command>initdb</command> would then create a collation named + <literal>de_DE.utf8</literal> for encoding <literal>UTF8</literal> + that has both <symbol>LC_COLLATE</symbol> and + <symbol>LC_CTYPE</symbol> set to <literal>de_DE.utf8</literal>. + It will also create a collation with the <literal>.utf8</literal> + tag stripped off the name. So you could also use the collation + under the name <literal>de_DE</literal>, which is less cumbersome + to write and makes the name less encoding-dependent. Note that, + nevertheless, the initial set of collation names is + platform-dependent. + </para> + + <para> + In case a collation is needed that has different values for + <symbol>LC_COLLATE</symbol> and <symbol>LC_CTYPE</symbol>, a new + collation may be created using + the <xref linkend="sql-createcollation"> command. That command + can also be used to create a new collation from an existing + collation, which can be useful to be able to use operating-system + independent collation names in applications. + </para> + </sect2> + </sect1> + <sect1 id="multibyte"> <title>Character Set Support</title> diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index 5a43774b33..cee09c7681 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -2006,6 +2006,10 @@ SET ENABLE_SEQSCAN TO OFF; This parameter can only be set in the <filename>postgresql.conf</> file or on the server command line. </para> + <para> + You should also consider setting <varname>hot_standby_feedback</> + as an alternative to using this parameter. + </para> </listitem> </varlistentry> </variablelist> @@ -2098,6 +2102,45 @@ SET ENABLE_SEQSCAN TO OFF; </listitem> </varlistentry> + <varlistentry id="guc-wal-receiver-status-interval" xreflabel="wal_receiver_status_interval"> + <term><varname>wal_receiver_status_interval</varname> (<type>integer</type>)</term> + <indexterm> + <primary><varname>wal_receiver_status_interval</> configuration parameter</primary> + </indexterm> + <listitem> + <para> + Specifies the minimum frequency, in seconds, for the WAL receiver + process on the standby to send information about replication progress + to the primary, where they can be seen using the + <literal>pg_stat_replication</literal> view. The standby will report + the last transaction log position it has written, the last position it + has flushed to disk, and the last position it has applied. Updates are + sent each time the write or flush positions changed, or at least as + often as specified by this parameter. Thus, the apply position may + lag slightly behind the true position. Setting this parameter to zero + disables status updates completely. This parameter can only be set in + the <filename>postgresql.conf</> file or on the server command line. + The default value is 10 seconds. + </para> + </listitem> + </varlistentry> + + <varlistentry id="guc-hot-standby-feedback" xreflabel="hot_standby"> + <term><varname>hot_standby_feedback</varname> (<type>boolean</type>)</term> + <indexterm> + <primary><varname>hot_standby_feedback</> configuration parameter</primary> + </indexterm> + <listitem> + <para> + Specifies whether or not a hot standby will send feedback to the primary + about queries currently executing on the standby. This parameter can + be used to eliminate query cancels caused by cleanup records, though + it can cause database bloat on the primary for some workloads. + The default value is <literal>off</literal>. + </para> + </listitem> + </varlistentry> + </variablelist> </sect2> </sect1> @@ -5165,15 +5208,15 @@ dynamic_library_path = 'C:\tools\postgresql;H:\my_project\lib;$libdir' </listitem> </varlistentry> - <varlistentry id="guc-max-predicate-locks-per-transaction" xreflabel="max_predicate_locks_per_transaction"> - <term><varname>max_predicate_locks_per_transaction</varname> (<type>integer</type>)</term> + <varlistentry id="guc-max-pred-locks-per-transaction" xreflabel="max_pred_locks_per_transaction"> + <term><varname>max_pred_locks_per_transaction</varname> (<type>integer</type>)</term> <indexterm> - <primary><varname>max_predicate_locks_per_transaction</> configuration parameter</primary> + <primary><varname>max_pred_locks_per_transaction</> configuration parameter</primary> </indexterm> <listitem> <para> The shared predicate lock table tracks locks on - <varname>max_predicate_locks_per_transaction</varname> * (<xref + <varname>max_pred_locks_per_transaction</varname> * (<xref linkend="guc-max-connections"> + <xref linkend="guc-max-prepared-transactions">) objects (e.g., tables); hence, no more than this many distinct objects can be locked at diff --git a/doc/src/sgml/contrib-spi.sgml b/doc/src/sgml/contrib-spi.sgml index 85980f6e55..55e5ce2649 100644 --- a/doc/src/sgml/contrib-spi.sgml +++ b/doc/src/sgml/contrib-spi.sgml @@ -17,8 +17,13 @@ below) while creating a trigger. </para> + <para> + Each of the groups of functions described below is provided as a + separately-installable extension. + </para> + <sect2> - <title>refint.c — Functions for Implementing Referential Integrity</title> + <title>refint — Functions for Implementing Referential Integrity</title> <para> <function>check_primary_key()</> and @@ -59,7 +64,7 @@ </sect2> <sect2> - <title>timetravel.c — Functions for Implementing Time Travel</title> + <title>timetravel — Functions for Implementing Time Travel</title> <para> Long ago, <productname>PostgreSQL</> had a built-in time travel feature @@ -152,7 +157,7 @@ CREATE TABLE mytab ( </sect2> <sect2> - <title>autoinc.c — Functions for Autoincrementing Fields</title> + <title>autoinc — Functions for Autoincrementing Fields</title> <para> <function>autoinc()</> is a trigger that stores the next value of @@ -179,7 +184,7 @@ CREATE TABLE mytab ( </sect2> <sect2> - <title>insert_username.c — Functions for Tracking Who Changed a Table</title> + <title>insert_username — Functions for Tracking Who Changed a Table</title> <para> <function>insert_username()</> is a trigger that stores the current @@ -200,7 +205,7 @@ CREATE TABLE mytab ( </sect2> <sect2> - <title>moddatetime.c — Functions for Tracking Last Modification Time</title> + <title>moddatetime — Functions for Tracking Last Modification Time</title> <para> <function>moddatetime()</> is a trigger that stores the current diff --git a/doc/src/sgml/contrib.sgml b/doc/src/sgml/contrib.sgml index c781c5608d..adf09ca872 100644 --- a/doc/src/sgml/contrib.sgml +++ b/doc/src/sgml/contrib.sgml @@ -46,38 +46,45 @@ <para> Many modules supply new user-defined functions, operators, or types. To make use of one of these modules, after you have installed the code - you need to register the new objects in the database - system by running the SQL commands in the <literal>.sql</> file - supplied by the module. For example, + you need to register the new SQL objects in the database system. + In <productname>PostgreSQL</> 9.1 and later, this is done by executing + a <xref linkend="sql-createextension"> command. In a fresh database, + you can simply do <programlisting> -psql -d dbname -f <replaceable>SHAREDIR</>/contrib/<replaceable>module</>.sql +CREATE EXTENSION <replaceable>module_name</>; </programlisting> - Here, <replaceable>SHAREDIR</> means the installation's <quote>share</> - directory (<literal>pg_config --sharedir</> will tell you what this is). - In most cases the script must be run by a database superuser. - </para> - - <para> - You need to run the <literal>.sql</> file in each database that you want + This command must be run by a database superuser. This registers the + new SQL objects in the current database only, so you need to run this + command in each database that you want the module's facilities to be available in. Alternatively, run it in - database <literal>template1</> so that the module will be copied into + database <literal>template1</> so that the extension will be copied into subsequently-created databases by default. </para> <para> - You can modify the first command in the <literal>.sql</> file to determine - which schema within the database the module's objects will be created in. - By default, they will be placed in <literal>public</>. + Many modules allow you to install their objects in a schema of your + choice. To do that, add <literal>SCHEMA + <replaceable>schema_name</></literal> to the <command>CREATE EXTENSION</> + command. By default, the objects will be placed in your current creation + target schema, typically <literal>public</>. </para> <para> - After a major-version upgrade of <productname>PostgreSQL</>, run the - installation script again, even though the module's objects might have - been brought forward from the old installation by dump and restore. - This ensures that any new functions will be available and any needed - corrections will be applied. + If your database was brought forward by dump and reload from a pre-9.1 + version of <productname>PostgreSQL</>, and you had been using the pre-9.1 + version of the module in it, you should instead do + +<programlisting> +CREATE EXTENSION <replaceable>module_name</> FROM unpackaged; +</programlisting> + + This will update the pre-9.1 objects of the module into a proper + <firstterm>extension</> object. Future updates to the module will be + managed by <xref linkend="sql-alterextension">. + For more information about extension updates, see + <xref linkend="extend-extensions">. </para> &adminpack; diff --git a/doc/src/sgml/dict-int.sgml b/doc/src/sgml/dict-int.sgml index b1d69e1a4c..17f98c05fa 100644 --- a/doc/src/sgml/dict-int.sgml +++ b/doc/src/sgml/dict-int.sgml @@ -47,8 +47,8 @@ <title>Usage</title> <para> - Running the installation script creates a text search template - <literal>intdict_template</> and a dictionary <literal>intdict</> + Installing the <literal>dict_int</> extension creates a text search + template <literal>intdict_template</> and a dictionary <literal>intdict</> based on it, with the default parameters. You can alter the parameters, for example diff --git a/doc/src/sgml/dict-xsyn.sgml b/doc/src/sgml/dict-xsyn.sgml index e77889e388..23c5d983c1 100644 --- a/doc/src/sgml/dict-xsyn.sgml +++ b/doc/src/sgml/dict-xsyn.sgml @@ -87,8 +87,8 @@ word syn1 syn2 syn3 <title>Usage</title> <para> - Running the installation script creates a text search template - <literal>xsyn_template</> and a dictionary <literal>xsyn</> + Installing the <literal>dict_xsyn</> extension creates a text search + template <literal>xsyn_template</> and a dictionary <literal>xsyn</> based on it, with default parameters. You can alter the parameters, for example diff --git a/doc/src/sgml/earthdistance.sgml b/doc/src/sgml/earthdistance.sgml index b43907615a..5b50da0510 100644 --- a/doc/src/sgml/earthdistance.sgml +++ b/doc/src/sgml/earthdistance.sgml @@ -10,7 +10,7 @@ <para> The <filename>earthdistance</> module provides two different approaches to calculating great circle distances on the surface of the Earth. The one - described first depends on the <filename>cube</> package (which + described first depends on the <filename>cube</> module (which <emphasis>must</> be installed before <filename>earthdistance</> can be installed). The second one is based on the built-in <type>point</> data type, using longitude and latitude for the coordinates. diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml index 2033ae21ba..63a917d3c9 100644 --- a/doc/src/sgml/extend.sgml +++ b/doc/src/sgml/extend.sgml @@ -38,6 +38,11 @@ operator classes for indexes (starting in <xref linkend="xindex">) </para> </listitem> + <listitem> + <para> + packages of related objects (starting in <xref linkend="extend-extensions">) + </para> + </listitem> </itemizedlist> </para> @@ -273,67 +278,868 @@ &xoper; &xindex; - <sect1 id="extend-Cpp"> - <title>Using C++ for Extensibility</title> - <indexterm zone="extend-Cpp"> - <primary>C++</primary> + <sect1 id="extend-extensions"> + <title>Packaging Related Objects into an Extension</title> + + <indexterm zone="extend-extensions"> + <primary>extension</primary> </indexterm> <para> - It is possible to use a compiler in C++ mode to build - <productname>PostgreSQL</productname> extensions by following these - guidelines: + A useful extension to <productname>PostgreSQL</> typically includes + multiple SQL objects; for example, a new datatype will require new + functions, new operators, and probably new index operator classes. + It is helpful to collect all these objects into a single package + to simplify database management. <productname>PostgreSQL</> calls + such a package an <firstterm>extension</>. To define an extension, + you need at least a <firstterm>script file</> that contains the + <acronym>SQL</> commands to create the extension's objects, and a + <firstterm>control file</> that specifies a few basic properties + of the extension itself. If the extension includes C code, there + will typically also be a shared library file into which the C code + has been built. Once you have these files, a simple + <xref linkend="sql-createextension"> command loads the objects into + your database. + </para> + + <para> + The main advantage of using an extension, rather than just running the + <acronym>SQL</> script to load a bunch of <quote>loose</> objects + into your database, is that <productname>PostgreSQL</> will then + understand that the objects of the extension go together. You can + drop all the objects with a single <xref linkend="sql-dropextension"> + command (no need to maintain a separate <quote>uninstall</> script). + Even more useful, <application>pg_dump</> knows that it should not + dump the individual member objects of the extension — it will + just include a <command>CREATE EXTENSION</> command in dumps, instead. + This vastly simplifies migration to a new version of the extension + that might contain more or different objects than the old version. + Note however that you must have the extension's control, script, and + other files available when loading such a dump into a new database. + </para> + + <para> + <productname>PostgreSQL</> will not let you drop an individual object + contained in an extension, except by dropping the whole extension. + Also, while you can change the definition of an extension member object + (for example, via <command>CREATE OR REPLACE FUNCTION</command> for a + function), bear in mind that the modified definition will not be dumped + by <application>pg_dump</>. Such a change is usually only sensible if + you concurrently make the same change in the extension's script file. + (But there are special provisions for tables containing configuration + data; see below.) + </para> + + <para> + The extension mechanism also has provisions for packaging modification + scripts that adjust the definitions of the SQL objects contained in an + extension. For example, if version 1.1 of an extension adds one function + and changes the body of another function compared to 1.0, the extension + author can provide an <firstterm>update script</> that makes just those + two changes. The <command>ALTER EXTENSION UPDATE</> command can then + be used to apply these changes and track which version of the extension + is actually installed in a given database. + </para> + + <para> + The kinds of SQL objects that can be members of an extension are shown in + the description of <xref linkend="sql-alterextension">. Notably, objects + that are database-cluster-wide, such as databases, roles, and tablespaces, + cannot be extension members since an extension is only known within one + database. (Although an extension script is not prohibited from creating + such objects, if it does so they will not be tracked as part of the + extension.) Also notice that while a table can be a member of an + extension, its subsidiary objects such as indexes are not directly + considered members of the extension. + </para> + + <sect2> + <title>Extension Files</title> + + <indexterm> + <primary>control file</primary> + </indexterm> + + <para> + The <xref linkend="sql-createextension"> command relies on a control + file for each extension, which must be named the same as the extension + with a suffix of <literal>.control</>, and must be placed in the + installation's <literal>SHAREDIR/extension</literal> directory. There + must also be at least one <acronym>SQL</> script file, which follows the + naming pattern + <literal><replaceable>extension</>--<replaceable>version</>.sql</literal> + (for example, <literal>foo--1.0.sql</> for version <literal>1.0</> of + extension <literal>foo</>). By default, the script file(s) are also + placed in the <literal>SHAREDIR/extension</literal> directory; but the + control file can specify a different directory for the script file(s). + </para> + + <para> + The file format for an extension control file is the same as for the + <filename>postgresql.conf</> file, namely a list of + <replaceable>parameter_name</> <literal>=</> <replaceable>value</> + assignments, one per line. Blank lines and comments introduced by + <literal>#</> are allowed. Be sure to quote any value that is not + a single word or number. + </para> + + <para> + A control file can set the following parameters: + </para> + + <variablelist> + <varlistentry> + <term><varname>directory</varname> (<type>string</type>)</term> + <listitem> + <para> + The directory containing the extension's <acronym>SQL</> script + file(s). Unless an absolute path is given, the name is relative to + the installation's <literal>SHAREDIR</literal> directory. The + default behavior is equivalent to specifying + <literal>directory = 'extension'</>. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><varname>default_version</varname> (<type>string</type>)</term> + <listitem> + <para> + The default version of the extension (the one that will be installed + if no version is specified in <command>CREATE EXTENSION</>). Although + this can be omitted, that will result in <command>CREATE EXTENSION</> + failing if no <literal>VERSION</> option appears, so you generally + don't want to do that. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><varname>comment</varname> (<type>string</type>)</term> + <listitem> + <para> + A comment (any string) about the extension. Alternatively, + the comment can be set by means of the <xref linkend="sql-comment"> + command in the script file. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><varname>encoding</varname> (<type>string</type>)</term> + <listitem> + <para> + The character set encoding used by the script file(s). This should + be specified if the script files contain any non-ASCII characters. + Otherwise the files will be assumed to be in the database encoding. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><varname>module_pathname</varname> (<type>string</type>)</term> + <listitem> + <para> + The value of this parameter will be substituted for each occurrence + of <literal>MODULE_PATHNAME</> in the script file(s). If it is not + set, no substitution is made. Typically, this is set to + <literal>$libdir/<replaceable>shared_library_name</></literal> and + then <literal>MODULE_PATHNAME</> is used in <command>CREATE + FUNCTION</> commands for C-language functions, so that the script + files do not need to hard-wire the name of the shared library. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><varname>requires</varname> (<type>string</type>)</term> + <listitem> + <para> + A list of names of extensions that this extension depends on, + for example <literal>requires = 'foo, bar'</literal>. Those + extensions must be installed before this one can be installed. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><varname>relocatable</varname> (<type>boolean</type>)</term> + <listitem> + <para> + An extension is <firstterm>relocatable</> if it is possible to move + its contained objects into a different schema after initial creation + of the extension. The default is <literal>false</>, i.e. the + extension is not relocatable. + See below for more information. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><varname>schema</varname> (<type>string</type>)</term> + <listitem> + <para> + This parameter can only be set for non-relocatable extensions. + It forces the extension to be loaded into exactly the named schema + and not any other. See below for more information. + </para> + </listitem> + </varlistentry> + </variablelist> + + <para> + In addition to the primary control file + <literal><replaceable>extension</>.control</literal>, + an extension can have secondary control files named in the style + <literal><replaceable>extension</>--<replaceable>version</>.control</literal>. + If supplied, these must be located in the script file directory. + Secondary control files follow the same format as the primary control + file. Any parameters set in a secondary control file override the + primary control file when installing or updating to that version of + the extension. However, the parameters <varname>directory</> and + <varname>default_version</> cannot be set in a secondary control file. + </para> + + <para> + An extension's <acronym>SQL</> script files can contain any SQL commands, + except for transaction control commands (<command>BEGIN</>, + <command>COMMIT</>, etc) and commands that cannot be executed inside a + transaction block (such as <command>VACUUM</>). This is because the + script files are implicitly executed within a transaction block. + </para> + + <para> + While the script files can contain any characters allowed by the specified + encoding, control files should contain only plain ASCII, because there + is no way for <productname>PostgreSQL</> to know what encoding a + control file is in. In practice this is only an issue if you want to + use non-ASCII characters in the extension's comment. Recommended + practice in that case is to not use the control file <varname>comment</> + parameter, but instead use <command>COMMENT ON EXTENSION</> + within a script file to set the comment. + </para> + + </sect2> + + <sect2> + <title>Extension Relocatability</title> + + <para> + Users often wish to load the objects contained in an extension into a + different schema than the extension's author had in mind. There are + three supported levels of relocatability: + </para> <itemizedlist> <listitem> <para> - All functions accessed by the backend must present a C interface - to the backend; these C functions can then call C++ functions. - For example, <literal>extern C</> linkage is required for - backend-accessed functions. This is also necessary for any - functions that are passed as pointers between the backend and - C++ code. - </para> - </listitem> - <listitem> - <para> - Free memory using the appropriate deallocation method. For example, - most backend memory is allocated using <function>palloc()</>, so use - <function>pfree()</> to free it, i.e. using C++ - <function>delete()</> in such cases will fail. + A fully relocatable extension can be moved into another schema + at any time, even after it's been loaded into a database. + This is done with the <command>ALTER EXTENSION SET SCHEMA</> + command, which automatically renames all the member objects into + the new schema. Normally, this is only possible if the extension + contains no internal assumptions about what schema any of its + objects are in. Also, the extension's objects must all be in one + schema to begin with (ignoring objects that do not belong to any + schema, such as procedural languages). Mark a fully relocatable + extension by setting <literal>relocatable = true</> in its control + file. </para> </listitem> + <listitem> <para> - Prevent exceptions from propagating into the C code (use a - catch-all block at the top level of all <literal>extern C</> - functions). This is necessary even if the C++ code does not - throw any exceptions because events like out-of-memory still - throw exceptions. Any exceptions must be caught and appropriate - errors passed back to the C interface. If possible, compile C++ - with <option>-fno-exceptions</> to eliminate exceptions entirely; - in such cases, you must check for failures in your C++ code, e.g. - check for NULL returned by <function>new()</>. + An extension might be relocatable during installation but not + afterwards. This is typically the case if the extension's script + file needs to reference the target schema explicitly, for example + in setting <literal>search_path</> properties for SQL functions. + For such an extension, set <literal>relocatable = false</> in its + control file, and use <literal>@extschema@</> to refer to the target + schema in the script file. All occurrences of this string will be + replaced by the actual target schema's name before the script is + executed. The user can set the target schema using the + <literal>SCHEMA</> option of <command>CREATE EXTENSION</>. </para> </listitem> + <listitem> <para> - If calling backend functions from C++ code, be sure that the - C++ call stack contains only plain old data structures - (<acronym>POD</>). This is necessary because backend errors - generate a distant <function>longjmp()</> that does not properly - unroll a C++ call stack with non-POD objects. + If the extension does not support relocation at all, set + <literal>relocatable = false</> in its control file, and also set + <literal>schema</> to the name of the intended target schema. This + will prevent use of the <literal>SCHEMA</> option of <command>CREATE + EXTENSION</>, unless it specifies the same schema named in the control + file. This choice is typically necessary if the extension contains + internal assumptions about schema names that can't be replaced by + uses of <literal>@extschema@</>. The <literal>@extschema@</> + substitution mechanism is available in this case too, although it is + of limited use since the schema name is determined by the control file. </para> </listitem> </itemizedlist> + + <para> + In all cases, the script file will be executed with + <xref linkend="guc-search-path"> initially set to point to the target + schema; that is, <command>CREATE EXTENSION</> does the equivalent of + this: +<programlisting> +SET LOCAL search_path TO @extschema@; +</programlisting> + This allows the objects created by the script file to go into the target + schema. The script file can change <varname>search_path</> if it wishes, + but that is generally undesirable. <varname>search_path</> is restored + to its previous setting upon completion of <command>CREATE EXTENSION</>. + </para> + + <para> + The target schema is determined by the <varname>schema</> parameter in + the control file if that is given, otherwise by the <literal>SCHEMA</> + option of <command>CREATE EXTENSION</> if that is given, otherwise the + current default object creation schema (the first one in the caller's + <varname>search_path</>). When the control file <varname>schema</> + parameter is used, the target schema will be created if it doesn't + already exist, but in the other two cases it must already exist. + </para> + + <para> + If any prerequisite extensions are listed in <varname>requires</varname> + in the control file, their target schemas are appended to the initial + setting of <varname>search_path</>. This allows their objects to be + visible to the new extension's script file. + </para> + + <para> + Although a non-relocatable extension can contain objects spread across + multiple schemas, it is usually desirable to place all the objects meant + for external use into a single schema, which is considered the extension's + target schema. Such an arrangement works conveniently with the default + setting of <varname>search_path</> during creation of dependent + extensions. + </para> + </sect2> + + <sect2> + <title>Extension Configuration Tables</title> + + <para> + Some extensions include configuration tables, which contain data that + might be added or changed by the user after installation of the + extension. Ordinarily, if a table is part of an extension, neither + the table's definition nor its content will be dumped by + <application>pg_dump</>. But that behavior is undesirable for a + configuration table; any data changes made by the user need to be + included in dumps, or the extension will behave differently after a dump + and reload. + </para> + + <para> + To solve this problem, an extension's script file can mark a table + it has created as a configuration table, which will cause + <application>pg_dump</> to include the table's contents (not its + definition) in dumps. To do that, call the function + <function>pg_extension_config_dump(regclass, text)</> after creating the + table, for example +<programlisting> +CREATE TABLE my_config (key text, value text); + +SELECT pg_catalog.pg_extension_config_dump('my_config', ''); +</programlisting> + Any number of tables can be marked this way. + </para> + + <para> + When the second argument of <function>pg_extension_config_dump</> is + an empty string, the entire contents of the table are dumped by + <application>pg_dump</>. This is usually only correct if the table + is initially empty as created by the extension script. If there is + a mixture of initial data and user-provided data in the table, + the second argument of <function>pg_extension_config_dump</> provides + a <literal>WHERE</> condition that selects the data to be dumped. + For example, you might do +<programlisting> +CREATE TABLE my_config (key text, value text, standard_entry boolean); + +SELECT pg_catalog.pg_extension_config_dump('my_config', 'WHERE NOT standard_entry'); +</programlisting> + and then make sure that <structfield>standard_entry</> is true only + in the rows created by the extension's script. + </para> + + <para> + More complicated situations, such as initially-provided rows that might + be modified by users, can be handled by creating triggers on the + configuration table to ensure that modified rows are marked correctly. + </para> + </sect2> + + <sect2> + <title>Extension Updates</title> + + <para> + One advantage of the extension mechanism is that it provides convenient + ways to manage updates to the SQL commands that define an extension's + objects. This is done by associating a version name or number with + each released version of the extension's installation script. + In addition, if you want users to be able to update their databases + dynamically from one version to the next, you should provide + <firstterm>update scripts</> that make the necessary changes to go from + one version to the next. Update scripts have names following the pattern + <literal><replaceable>extension</>--<replaceable>oldversion</>--<replaceable>newversion</>.sql</literal> + (for example, <literal>foo--1.0--1.1.sql</> contains the commands to modify + version <literal>1.0</> of extension <literal>foo</> into version + <literal>1.1</>). + </para> + + <para> + Given that a suitable update script is available, the command + <command>ALTER EXTENSION UPDATE</> will update an installed extension + to the specified new version. The update script is run in the same + environment that <command>CREATE EXTENSION</> provides for installation + scripts: in particular, <varname>search_path</> is set up in the same + way, and any new objects created by the script are automatically added + to the extension. + </para> + + <para> + If an extension has secondary control files, the control parameters + that are used for an update script are those associated with the script's + target (new) version. + </para> + + <para> + The update mechanism can be used to solve an important special case: + converting a <quote>loose</> collection of objects into an extension. + Before the extension mechanism was added to + <productname>PostgreSQL</productname> (in 9.1), many people wrote + extension modules that simply created assorted unpackaged objects. + Given an existing database containing such objects, how can we convert + the objects into a properly packaged extension? Dropping them and then + doing a plain <command>CREATE EXTENSION</> is one way, but it's not + desirable if the objects have dependencies (for example, if there are + table columns of a data type created by the extension). The way to fix + this situation is to create an empty extension, then use <command>ALTER + EXTENSION ADD</> to attach each pre-existing object to the extension, + then finally create any new objects that are in the current extension + version but were not in the unpackaged release. <command>CREATE + EXTENSION</> supports this case with its <literal>FROM</> <replaceable + class="parameter">old_version</> option, which causes it to not run the + normal installation script for the target version, but instead the update + script named + <literal><replaceable>extension</>--<replaceable>old_version</>--<replaceable>target_version</>.sql</literal>. + The choice of the dummy version name to use as <replaceable + class="parameter">old_version</> is up to the extension author, though + <literal>unpackaged</> is a common convention. If you have multiple + prior versions you need to be able to update into extension style, use + multiple dummy version names to identify them. + </para> + + <para> + <command>ALTER EXTENSION</> is able to execute sequences of update + script files to achieve a requested update. For example, if only + <literal>foo--1.0--1.1.sql</> and <literal>foo--1.1--2.0.sql</> are + available, <command>ALTER EXTENSION</> will apply them in sequence if an + update to version <literal>2.0</> is requested when <literal>1.0</> is + currently installed. + </para> + + <para> + <productname>PostgreSQL</> doesn't assume anything about the properties + of version names: for example, it does not know whether <literal>1.1</> + follows <literal>1.0</>. It just matches up the available version names + and follows the path that requires applying the fewest update scripts. + (A version name can actually be any string that doesn't contain + <literal>--</> or leading or trailing <literal>-</>.) + </para> + + <para> + Sometimes it is useful to provide <quote>downgrade</> scripts, for + example <literal>foo--1.1--1.0.sql</> to allow reverting the changes + associated with version <literal>1.1</>. If you do that, be careful + of the possibility that a downgrade script might unexpectedly + get applied because it yields a shorter path. The risky case is where + there is a <quote>fast path</> update script that jumps ahead several + versions as well as a downgrade script to the fast path's start point. + It might take fewer steps to apply the downgrade and then the fast + path than to move ahead one version at a time. If the downgrade script + drops any irreplaceable objects, this will yield undesirable results. + </para> + + <para> + To check for unexpected update paths, use this command: +<programlisting> +SELECT * FROM pg_extension_update_paths('<replaceable>extension_name</>'); +</programlisting> + This shows each pair of distinct known version names for the specified + extension, together with the update path sequence that would be taken to + get from the source version to the target version, or <literal>NULL</> if + there is no available update path. The path is shown in textual form + with <literal>--</> separators. You can use + <literal>regexp_split_to_array(path,'--')</> if you prefer an array + format. + </para> + </sect2> + + <sect2> + <title>Extension Example</title> + + <para> + Here is a complete example of an <acronym>SQL</>-only + extension, a two-element composite type that can store any type of value + in its slots, which are named <quote>k</> and <quote>v</>. Non-text + values are automatically coerced to text for storage. + </para> + + <para> + The script file <filename>pair--1.0.sql</> looks like this: + +<programlisting><![CDATA[ +CREATE TYPE pair AS ( k text, v text ); + +CREATE OR REPLACE FUNCTION pair(anyelement, text) +RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair'; + +CREATE OR REPLACE FUNCTION pair(text, anyelement) +RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair'; + +CREATE OR REPLACE FUNCTION pair(anyelement, anyelement) +RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair'; + +CREATE OR REPLACE FUNCTION pair(text, text) +RETURNS pair LANGUAGE SQL AS 'SELECT ROW($1, $2)::pair;'; + +CREATE OPERATOR ~> (LEFTARG = text, RIGHTARG = anyelement, PROCEDURE = pair); +CREATE OPERATOR ~> (LEFTARG = anyelement, RIGHTARG = text, PROCEDURE = pair); +CREATE OPERATOR ~> (LEFTARG = anyelement, RIGHTARG = anyelement, PROCEDURE = pair); +CREATE OPERATOR ~> (LEFTARG = text, RIGHTARG = text, PROCEDURE = pair); +]]> +</programlisting> + </para> + + <para> + The control file <filename>pair.control</> looks like this: + +<programlisting> +# pair extension +comment = 'A key/value pair data type' +default_version = '1.0' +relocatable = true +</programlisting> + </para> + + <para> + While you hardly need a makefile to install these two files into the + correct directory, you could use a <filename>Makefile</> containing this: + +<programlisting> +EXTENSION = pair +DATA = pair--1.0.sql + +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +</programlisting> + + This makefile relies on <acronym>PGXS</acronym>, which is described + in <xref linkend="extend-pgxs">. The command <literal>make install</> + will install the control and script files into the correct + directory as reported by <application>pg_config</>. + </para> + + <para> + Once the files are installed, use the + <xref linkend="sql-createextension"> command to load the objects into + any particular database. + </para> + </sect2> + </sect1> + + <sect1 id="extend-pgxs"> + <title>Extension Building Infrastructure</title> + + <indexterm zone="extend-pgxs"> + <primary>pgxs</primary> + </indexterm> + + <para> + If you are thinking about distributing your + <productname>PostgreSQL</> extension modules, setting up a + portable build system for them can be fairly difficult. Therefore + the <productname>PostgreSQL</> installation provides a build + infrastructure for extensions, called <acronym>PGXS</acronym>, so + that simple extension modules can be built simply against an + already installed server. <acronym>PGXS</acronym> is mainly intended + for extensions that include C code, although it can be used for + pure-SQL extensions too. Note that <acronym>PGXS</acronym> is not + intended to be a universal build system framework that can be used + to build any software interfacing to <productname>PostgreSQL</>; + it simply automates common build rules for simple server extension + modules. For more complicated packages, you might need to write your + own build system. + </para> + + <para> + To use the <acronym>PGXS</acronym> infrastructure for your extension, + you must write a simple makefile. + In the makefile, you need to set some variables + and finally include the global <acronym>PGXS</acronym> makefile. + Here is an example that builds an extension module named + <literal>isbn_issn</literal>, consisting of a shared library containing + some C code, an extension control file, a SQL script, and a documentation + text file: +<programlisting> +MODULES = isbn_issn +EXTENSION = isbn_issn +DATA = isbn_issn--1.0.sql +DOCS = README.isbn_issn + +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +</programlisting> + The last three lines should always be the same. Earlier in the + file, you assign variables or add custom + <application>make</application> rules. + </para> + + <para> + Set one of these three variables to specify what is built: + + <variablelist> + <varlistentry> + <term><varname>MODULES</varname></term> + <listitem> + <para> + list of shared-library objects to be built from source files with same + stem (do not include library suffixes in this list) + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><varname>MODULE_big</varname></term> + <listitem> + <para> + a shared library to build from multiple source files + (list object files in <varname>OBJS</varname>) + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><varname>PROGRAM</varname></term> + <listitem> + <para> + an executable program to build + (list object files in <varname>OBJS</varname>) + </para> + </listitem> + </varlistentry> + </variablelist> + + The following variables can also be set: + + <variablelist> + <varlistentry> + <term><varname>EXTENSION</varname></term> + <listitem> + <para> + extension name(s); for each name you must provide an + <literal><replaceable>extension</replaceable>.control</literal> file, + which will be installed into + <literal><replaceable>prefix</replaceable>/share/extension</literal> + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><varname>MODULEDIR</varname></term> + <listitem> + <para> + subdirectory of <literal><replaceable>prefix</>/share</literal> + into which DATA and DOCS files should be installed + (if not set, default is <literal>extension</literal> if + <varname>EXTENSION</varname> is set, + or <literal>contrib</literal> if not) + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><varname>DATA</varname></term> + <listitem> + <para> + random files to install into <literal><replaceable>prefix</replaceable>/share/$MODULEDIR</literal> + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><varname>DATA_built</varname></term> + <listitem> + <para> + random files to install into + <literal><replaceable>prefix</replaceable>/share/$MODULEDIR</literal>, + which need to be built first + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><varname>DATA_TSEARCH</varname></term> + <listitem> + <para> + random files to install under + <literal><replaceable>prefix</replaceable>/share/tsearch_data</literal> + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><varname>DOCS</varname></term> + <listitem> + <para> + random files to install under + <literal><replaceable>prefix</replaceable>/doc/$MODULEDIR</literal> + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><varname>SCRIPTS</varname></term> + <listitem> + <para> + script files (not binaries) to install into + <literal><replaceable>prefix</replaceable>/bin</literal> + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><varname>SCRIPTS_built</varname></term> + <listitem> + <para> + script files (not binaries) to install into + <literal><replaceable>prefix</replaceable>/bin</literal>, + which need to be built first + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><varname>REGRESS</varname></term> + <listitem> + <para> + list of regression test cases (without suffix), see below + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><varname>EXTRA_CLEAN</varname></term> + <listitem> + <para> + extra files to remove in <literal>make clean</literal> + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><varname>PG_CPPFLAGS</varname></term> + <listitem> + <para> + will be added to <varname>CPPFLAGS</varname> + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><varname>PG_LIBS</varname></term> + <listitem> + <para> + will be added to <varname>PROGRAM</varname> link line + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><varname>SHLIB_LINK</varname></term> + <listitem> + <para> + will be added to <varname>MODULE_big</varname> link line + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><varname>PG_CONFIG</varname></term> + <listitem> + <para> + path to <application>pg_config</> program for the + <productname>PostgreSQL</productname> installation to build against + (typically just <literal>pg_config</> to use the first one in your + <varname>PATH</>) + </para> + </listitem> + </varlistentry> + </variablelist> </para> <para> - In summary, it is best to place C++ code behind a wall of - <literal>extern C</> functions that interface to the backend, - and avoid exception, memory, and call stack leakage. + Put this makefile as <literal>Makefile</literal> in the directory + which holds your extension. Then you can do + <literal>make</literal> to compile, and then <literal>make + install</literal> to install your module. By default, the extension is + compiled and installed for the + <productname>PostgreSQL</productname> installation that + corresponds to the first <command>pg_config</command> program + found in your <varname>PATH</>. You can use a different installation by + setting <varname>PG_CONFIG</varname> to point to its + <command>pg_config</command> program, either within the makefile + or on the <literal>make</literal> command line. </para> + + <caution> + <para> + Changing <varname>PG_CONFIG</varname> only works when building + against <productname>PostgreSQL</productname> 8.3 or later. + With older releases it does not work to set it to anything except + <literal>pg_config</>; you must alter your <varname>PATH</> + to select the installation to build against. + </para> + </caution> + + <para> + The scripts listed in the <varname>REGRESS</> variable are used for + regression testing of your module, which can be invoked by <literal>make + installcheck</literal> after doing <literal>make install</>. For this to + work you must have a running <productname>PostgreSQL</productname> server. + The script files listed in <varname>REGRESS</> must appear in a + subdirectory named <literal>sql/</literal> in your extension's directory. + These files must have extension <literal>.sql</literal>, which must not be + included in the <varname>REGRESS</varname> list in the makefile. For each + test there should also be a file containing the expected output in a + subdirectory named <literal>expected/</literal>, with the same stem and + extension <literal>.out</literal>. <literal>make installcheck</literal> + executes each test script with <application>psql</>, and compares the + resulting output to the matching expected file. Any differences will be + written to the file <literal>regression.diffs</literal> in <command>diff + -c</command> format. Note that trying to run a test that is missing its + expected file will be reported as <quote>trouble</quote>, so make sure you + have all expected files. + </para> + + <tip> + <para> + The easiest way to create the expected files is to create empty files, + then do a test run (which will of course report differences). Inspect + the actual result files found in the <literal>results/</literal> + directory, then copy them to <literal>expected/</literal> if they match + what you expect from the test. + </para> + + </tip> </sect1> </chapter> diff --git a/doc/src/sgml/external-projects.sgml b/doc/src/sgml/external-projects.sgml index 4deedb7f63..ef516b4214 100644 --- a/doc/src/sgml/external-projects.sgml +++ b/doc/src/sgml/external-projects.sgml @@ -19,8 +19,8 @@ similar to <ulink url="http://sourceforge.net">SourceForge.net</> in its feature set, providing mailing lists, forums, bug tracking, SCM, and web hosting. If you have a <productname>PostgreSQL</>-related open source - project that you would like to have hosted at PgFoundy, please feel free - to create a new project. + project that you would like to have hosted at PgFoundry, please feel free + to create a new project there. </para> <sect1 id="external-interfaces"> @@ -134,6 +134,22 @@ </table> </sect1> + <sect1 id="external-admin-tools"> + <title>Administration Tools</title> + + <indexterm> + <primary>administration tools</primary> + <secondary>externally maintained</secondary> + </indexterm> + + <para> + There are several administration tools available for + <productname>PostgreSQL</>. The most popular is + <application><ulink url="http://www.pgadmin.org/">pgAdmin III</ulink></>, + and there are several commercially available ones as well. + </para> + </sect1> + <sect1 id="external-pl"> <title>Procedural Languages</title> @@ -223,32 +239,24 @@ <title>Extensions</title> <indexterm> - <primary>extensions</primary> + <primary>extension</primary> + <secondary>externally maintained</secondary> </indexterm> <para> <productname>PostgreSQL</> is designed to be easily extensible. For - this reason, extensions loaded into the database can function just - like features that are packaged with the database. The + this reason, extensions loaded into the database can function + just like features that are built in. The <filename>contrib/</> directory shipped with the source code - contains a large number of extensions. The <filename>README</> file - in that directory contains a summary. They include conversion - tools, full-text indexing, <acronym>XML</> tools, and additional - data types and indexing methods. Other extensions are developed + contains several extensions, which are described in + <xref linkend="contrib">. Other extensions are developed independently, like <application><ulink url="http://www.postgis.org/">PostGIS</ulink></>. Even - <productname>PostgreSQL</> replication solutions are developed + <productname>PostgreSQL</> replication solutions can be developed externally. For example, <application> <ulink url="http://www.slony.info">Slony-I</ulink></> is a popular master/standby replication solution that is developed independently from the core project. </para> - - <para> - There are several administration tools available for - <productname>PostgreSQL</>. The most popular is - <application><ulink url="http://www.pgadmin.org/">pgAdmin III</ulink></>, - and there are several commercially available ones as well. - </para> </sect1> </appendix> diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 2bbada0d06..70a1bd9ee9 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -13060,6 +13060,12 @@ SELECT relname FROM pg_class WHERE pg_table_is_visible(oid); <tbody> <row> + <entry><literal><function>pg_collation_is_visible(<parameter>collation_oid</parameter>)</function></literal> + </entry> + <entry><type>boolean</type></entry> + <entry>is collation visible in search path</entry> + </row> + <row> <entry><literal><function>pg_conversion_is_visible(<parameter>conversion_oid</parameter>)</function></literal> </entry> <entry><type>boolean</type></entry> @@ -13124,6 +13130,9 @@ SELECT relname FROM pg_class WHERE pg_table_is_visible(oid); </table> <indexterm> + <primary>pg_collation_is_visible</primary> + </indexterm> + <indexterm> <primary>pg_conversion_is_visible</primary> </indexterm> <indexterm> @@ -13256,7 +13265,7 @@ SELECT pg_type_is_visible('myschema.widget'::regtype); <tbody> <row> - <entry><literal><function>format_type(<parameter>type_oid</parameter>, <parameter>typemod</>)</function></literal></entry> + <entry><literal><function>format_type(<parameter>type_oid</parameter> [, <parameter>typemod</> [, <parameter>collation_oid</> ]])</function></literal></entry> <entry><type>text</type></entry> <entry>get SQL name of a data type</entry> </row> @@ -13392,7 +13401,9 @@ SELECT pg_type_is_visible('myschema.widget'::regtype); <para> <function>format_type</function> returns the SQL name of a data type that is identified by its type OID and possibly a type modifier. Pass NULL - for the type modifier if no specific modifier is known. + for the type modifier or omit the argument if no specific modifier is known. + If a collation is given as third argument, a <literal>COLLATE</> clause + followed by a formatted collation name is appended. </para> <para> @@ -13915,6 +13926,9 @@ SELECT set_config('log_statement_stats', 'off', false); <primary>backup</primary> </indexterm> <indexterm> + <primary>pg_create_restore_point</primary> + </indexterm> + <indexterm> <primary>pg_current_xlog_insert_location</primary> </indexterm> <indexterm> @@ -13953,6 +13967,13 @@ SELECT set_config('log_statement_stats', 'off', false); <tbody> <row> <entry> + <literal><function>pg_create_restore_point(<parameter>name</> <type>text</>)</function></literal> + </entry> + <entry><type>text</type></entry> + <entry>Create a named point for performing restore (restricted to superusers)</entry> + </row> + <row> + <entry> <literal><function>pg_current_xlog_insert_location()</function></literal> </entry> <entry><type>text</type></entry> @@ -14174,6 +14195,64 @@ postgres=# SELECT * FROM pg_xlogfile_name_offset(pg_stop_backup()); </table> <para> + The functions shown in <xref + linkend="functions-recovery-control-table"> control the progress of recovery. + These functions may be executed only during recovery. + </para> + + <table id="functions-recovery-control-table"> + <title>Recovery Control Functions</title> + <tgroup cols="3"> + <thead> + <row><entry>Name</entry> <entry>Return Type</entry> <entry>Description</entry> + </row> + </thead> + + <tbody> + <row> + <entry> + <literal><function>pg_is_xlog_replay_paused()</function></literal> + </entry> + <entry><type>bool</type></entry> + <entry>True if recovery is paused. + </entry> + </row> + <row> + <entry> + <literal><function>pg_xlog_replay_pause()</function></literal> + </entry> + <entry><type>void</type></entry> + <entry>Pauses recovery immediately. + </entry> + </row> + <row> + <entry> + <literal><function>pg_xlog_replay_resume()</function></literal> + </entry> + <entry><type>void</type></entry> + <entry>Restarts recovery if it was paused. + </entry> + </row> + </tbody> + </tgroup> + </table> + + <para> + While recovery is paused no further database changes are applied. + If in hot standby, all new queries will see the same consistent snapshot + of the database, and no further query conflicts will be generated until + recovery is resumed. + </para> + + <para> + If streaming replication is disabled, the paused state may continue + indefinitely without problem. While streaming replication is in + progress WAL records will continue to be received, which will + eventually fill available disk space, depending upon the duration of + the pause, the rate of WAL generation and available disk space. + </para> + + <para> The functions shown in <xref linkend="functions-admin-dbsize"> calculate the disk space usage of database objects. </para> diff --git a/doc/src/sgml/high-availability.sgml b/doc/src/sgml/high-availability.sgml index a89296905b..37ba43b5fd 100644 --- a/doc/src/sgml/high-availability.sgml +++ b/doc/src/sgml/high-availability.sgml @@ -615,8 +615,9 @@ protocol to make nodes agree on a serializable transactional order. </para> <para> - Standby mode is exited and the server switches to normal operation, - when a trigger file is found (<varname>trigger_file</>). Before failover, + Standby mode is exited and the server switches to normal operation + when <command>pg_ctl promote</> is run or a trigger file is found + (<varname>trigger_file</>). Before failover, any WAL immediately available in the archive or in <filename>pg_xlog</> will be restored, but no attempt is made to connect to the master. </para> @@ -685,11 +686,7 @@ protocol to make nodes agree on a serializable transactional order. If you're setting up the standby server for high availability purposes, set up WAL archiving, connections and authentication like the primary server, because the standby server will work as a primary server after - failover. You will also need to set <varname>trigger_file</> to make - it possible to fail over. - If you're setting up the standby server for reporting - purposes, with no plans to fail over to it, <varname>trigger_file</> - is not required. + failover. </para> <para> @@ -710,7 +707,6 @@ protocol to make nodes agree on a serializable transactional order. standby_mode = 'on' primary_conninfo = 'host=192.168.1.50 port=5432 user=foo password=foopass' restore_command = 'cp /path/to/archive/%f %p' -trigger_file = '/path/to/trigger_file' archive_cleanup_command = 'pg_archivecleanup /path/to/archive %r' </programlisting> </para> @@ -949,13 +945,15 @@ primary_conninfo = 'host=192.168.1.50 port=5432 user=foo password=foopass' </para> <para> - To trigger failover of a log-shipping standby server, create a trigger + To trigger failover of a log-shipping standby server, + run <command>pg_ctl promote</> or create a trigger file with the filename and path specified by the <varname>trigger_file</> - setting in <filename>recovery.conf</>. If <varname>trigger_file</> is - not given, there is no way to exit recovery in the standby and promote - it to a master. That can be useful for e.g reporting servers that are + setting in <filename>recovery.conf</>. If you're planning to use + <command>pg_ctl promote</> to fail over, <varname>trigger_file</> is + not required. If you're setting up the reporting servers that are only used to offload read-only queries from the primary, not for high - availability purposes. + availability purposes, you don't need to exit recovery in the standby + and promote it to a master. </para> </sect1> @@ -1486,23 +1484,6 @@ if (!triggered) </para> <para> - The most common reason for conflict between standby queries and WAL replay - is <quote>early cleanup</>. Normally, <productname>PostgreSQL</> allows - cleanup of old row versions when there are no transactions that need to - see them to ensure correct visibility of data according to MVCC rules. - However, this rule can only be applied for transactions executing on the - master. So it is possible that cleanup on the master will remove row - versions that are still visible to a transaction on the standby. - </para> - - <para> - Experienced users should note that both row version cleanup and row version - freezing will potentially conflict with standby queries. Running a manual - <command>VACUUM FREEZE</> is likely to cause conflicts even on tables with - no updated or deleted rows. - </para> - - <para> Once the delay specified by <varname>max_standby_archive_delay</> or <varname>max_standby_streaming_delay</> has been exceeded, conflicting queries will be cancelled. This usually results just in a cancellation @@ -1529,6 +1510,23 @@ if (!triggered) </para> <para> + The most common reason for conflict between standby queries and WAL replay + is <quote>early cleanup</>. Normally, <productname>PostgreSQL</> allows + cleanup of old row versions when there are no transactions that need to + see them to ensure correct visibility of data according to MVCC rules. + However, this rule can only be applied for transactions executing on the + master. So it is possible that cleanup on the master will remove row + versions that are still visible to a transaction on the standby. + </para> + + <para> + Experienced users should note that both row version cleanup and row version + freezing will potentially conflict with standby queries. Running a manual + <command>VACUUM FREEZE</> is likely to cause conflicts even on tables with + no updated or deleted rows. + </para> + + <para> Users should be clear that tables that are regularly and heavily updated on the primary server will quickly cause cancellation of longer running queries on the standby. In such cases the setting of a finite value for @@ -1539,12 +1537,10 @@ if (!triggered) <para> Remedial possibilities exist if the number of standby-query cancellations - is found to be unacceptable. The first option is to connect to the - primary server and keep a query active for as long as needed to - run queries on the standby. This prevents <command>VACUUM</> from removing - recently-dead rows and so cleanup conflicts do not occur. - This could be done using <xref linkend="dblink"> and - <function>pg_sleep()</>, or via other mechanisms. If you do this, you + is found to be unacceptable. The first option is to set the parameter + <varname>hot_standby_feedback</>, which prevents <command>VACUUM</> from + removing recently-dead rows and so cleanup conflicts do not occur. + If you do this, you should note that this will delay cleanup of dead rows on the primary, which may result in undesirable table bloat. However, the cleanup situation will be no worse than if the standby queries were running diff --git a/doc/src/sgml/hstore.sgml b/doc/src/sgml/hstore.sgml index 6ccc9d664b..f00b06aa7a 100644 --- a/doc/src/sgml/hstore.sgml +++ b/doc/src/sgml/hstore.sgml @@ -554,12 +554,6 @@ SELECT key, count(*) FROM <title>Compatibility</title> <para> - <emphasis>When upgrading from older versions, always load the new - version of this module into the database before restoring a dump. - Otherwise, many new features will be unavailable.</emphasis> - </para> - - <para> As of PostgreSQL 9.0, <type>hstore</> uses a different internal representation than previous versions. This presents no obstacle for dump/restore upgrades since the text representation (used in the dump) is diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml index 8fec13ee1d..15fbe0d614 100644 --- a/doc/src/sgml/indices.sgml +++ b/doc/src/sgml/indices.sgml @@ -921,6 +921,7 @@ CREATE INDEX <replaceable>name</replaceable> ON <replaceable>table</replaceable> defining two operator classes for the data type and then selecting the proper class when making an index. The operator class determines the basic sort ordering (which can then be modified by adding sort options + <literal>COLLATE</literal>, <literal>ASC</>/<literal>DESC</> and/or <literal>NULLS FIRST</>/<literal>NULLS LAST</>). </para> @@ -1002,6 +1003,47 @@ SELECT am.amname AS index_method, </sect1> + <sect1 id="indexes-collations"> + <title>Collations and Indexes</title> + + <para> + An index can only support one collation for one column or + expression. If multiple collations are of interest, multiple + indexes may be created. + </para> + + <para> + Consider these statements: +<programlisting> +CREATE TABLE test1c ( + id integer, + content varchar COLLATE "x" +); + +CREATE INDEX test1c_content_index ON test1c (content); +</programlisting> + The created index automatically follows the collation of the + underlying column, and so a query of the form +<programlisting> +SELECT * FROM test1c WHERE content = <replaceable>constant</replaceable>; +</programlisting> + could use the index. + </para> + + <para> + If in addition, a query of the form, say, +<programlisting> +SELECT * FROM test1c WHERE content > <replaceable>constant</replaceable> COLLATE "y"; +</programlisting> + is of interest, an additional index could be created that supports + the <literal>"y"</literal> collation, like so: +<programlisting> +CREATE INDEX test1c_content_index ON test1c (content COLLATE "y"); +</programlisting> + </para> + </sect1> + + <sect1 id="indexes-examine"> <title>Examining Index Usage</title> diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml index 5861595c29..52407a741f 100644 --- a/doc/src/sgml/information_schema.sgml +++ b/doc/src/sgml/information_schema.sgml @@ -498,6 +498,140 @@ </para> </sect1> + <sect1 id="infoschema-character-sets"> + <title><literal>character_sets</literal></title> + + <para> + The view <literal>character_sets</literal> identifies the character + sets available in the current database. Since PostgreSQL does not + support multiple character sets within one database, this view only + shows one, which is the database encoding. + </para> + + <para> + Take note of how the following terms are used in the SQL standard: + <variablelist> + <varlistentry> + <term>character repertoire</term> + <listitem> + <para> + An abstract collection of characters, for + example <literal>UNICODE</literal>, <literal>UCS</literal>, or + <literal>LATIN1</literal>. Not exposed as an SQL object, but + visible in this view. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term>character encoding form</term> + <listitem> + <para> + An encoding of some character repertoire. Most older character + repertoires only use one encoding form, and so there are no + separate names for them (e.g., <literal>LATIN1</literal> is an + encoding form applicable to the <literal>LATIN1</literal> + repertoire). But for example Unicode has the encoding forms + <literal>UTF8</literal>, <literal>UTF16</literal>, etc. (not + all supported by PostgreSQL). Encoding forms are not exposed + as an SQL object, but are visible in this view. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term>character set</term> + <listitem> + <para> + A named SQL object that identifies a character repertoire, a + character encoding, and a default collation. A predefined + character set would typically have the same name as an encoding + form, but users could define other names. For example, the + character set <literal>UTF8</literal> would typically identify + the character repertoire <literal>UCS</literal>, encoding + form <literal>UTF8</literal>, and some default collation. + </para> + </listitem> + </varlistentry> + </variablelist> + + You can think of an <quote>encoding</quote> in PostgreSQL either as + a character set or a character encoding form. They will have the + same name, and there can only be one in one database. + </para> + + <table> + <title><literal>character_sets</literal> Columns</title> + + <tgroup cols="3"> + <thead> + <row> + <entry>Name</entry> + <entry>Data Type</entry> + <entry>Description</entry> + </row> + </thead> + + <tbody> + <row> + <entry><literal>character_set_catalog</literal></entry> + <entry><literal>sql_identifier</literal></entry> + <entry>Character sets are currently not implemented as schema objects, so this column is null.</entry> + </row> + + <row> + <entry><literal>character_set_schema</literal></entry> + <entry><literal>sql_identifier</literal></entry> + <entry>Character sets are currently not implemented as schema objects, so this column is null.</entry> + </row> + + <row> + <entry><literal>character_set_name</literal></entry> + <entry><literal>sql_identifier</literal></entry> + <entry>Name of the character set, currently implemented as showing the name of the database encoding</entry> + </row> + + <row> + <entry><literal>character_repertoire</literal></entry> + <entry><literal>sql_identifier</literal></entry> + <entry>Character repertoire, showing <literal>UCS</literal> if the encoding is <literal>UTF8</literal>, else just the encoding name</entry> + </row> + + <row> + <entry><literal>form_of_use</literal></entry> + <entry><literal>sql_identifier</literal></entry> + <entry>Character encoding form, same as the database encoding</entry> + </row> + + <row> + <entry><literal>default_collate_catalog</literal></entry> + <entry><literal>sql_identifier</literal></entry> + <entry>Name of the database containing the default collation (always the current database, if any collation is identified)</entry> + </row> + + <row> + <entry><literal>default_collate_schema</literal></entry> + <entry><literal>sql_identifier</literal></entry> + <entry>Name of the schema containing the default collation</entry> + </row> + + <row> + <entry><literal>default_collate_name</literal></entry> + <entry><literal>sql_identifier</literal></entry> + <entry> + Name of the default collation. The default collation is + identified as the collation that matches + the <literal>COLLATE</literal> and <literal>CTYPE</literal> + settings of the current database. If there is no such + collation, then this column and the associated schema and + catalog columns are null. + </entry> + </row> + </tbody> + </tgroup> + </table> + </sect1> + <sect1 id="infoschema-check-constraint-routine-usage"> <title><literal>check_constraint_routine_usage</literal></title> @@ -615,6 +749,123 @@ </table> </sect1> + <sect1 id="infoschema-collations"> + <title><literal>collations</literal></title> + + <para> + The view <literal>collations</literal> contains the collations + available in the current database. + </para> + + <table> + <title><literal>collations</literal> Columns</title> + + <tgroup cols="3"> + <thead> + <row> + <entry>Name</entry> + <entry>Data Type</entry> + <entry>Description</entry> + </row> + </thead> + + <tbody> + <row> + <entry><literal>collation_catalog</literal></entry> + <entry><literal>sql_identifier</literal></entry> + <entry>Name of the database containing the collation (always the current database)</entry> + </row> + + <row> + <entry><literal>collation_schema</literal></entry> + <entry><literal>sql_identifier</literal></entry> + <entry>Name of the schema containing the collation</entry> + </row> + + <row> + <entry><literal>collation_name</literal></entry> + <entry><literal>sql_identifier</literal></entry> + <entry>Name of the default collation</entry> + </row> + + <row> + <entry><literal>pad_attribute</literal></entry> + <entry><literal>character_data</literal></entry> + <entry> + Always <literal>NO PAD</literal> (The alternative <literal>PAD + SPACE</literal> is not supported by PostgreSQL.) + </entry> + </row> + </tbody> + </tgroup> + </table> + </sect1> + + <sect1 id="infoschema-collation-character-set-applicability"> + <title><literal>collation_character_set_applicability</literal></title> + + <para> + The view <literal>collation_character_set_applicability</literal> + identifies which character set the available collations are + applicable to. In PostgreSQL, there is only one character set per + database (see explanation + in <xref linkend="infoschema-character-sets">), so this view does + not provide much useful information. + </para> + + <table> + <title><literal>collation_character_set_applicability</literal> Columns</title> + + <tgroup cols="3"> + <thead> + <row> + <entry>Name</entry> + <entry>Data Type</entry> + <entry>Description</entry> + </row> + </thead> + + <tbody> + <row> + <entry><literal>collation_catalog</literal></entry> + <entry><literal>sql_identifier</literal></entry> + <entry>Name of the database containing the collation (always the current database)</entry> + </row> + + <row> + <entry><literal>collation_schema</literal></entry> + <entry><literal>sql_identifier</literal></entry> + <entry>Name of the schema containing the collation</entry> + </row> + + <row> + <entry><literal>collation_name</literal></entry> + <entry><literal>sql_identifier</literal></entry> + <entry>Name of the default collation</entry> + </row> + + <row> + <entry><literal>character_set_catalog</literal></entry> + <entry><literal>sql_identifier</literal></entry> + <entry>Character sets are currently not implemented as schema objects, so this column is null</entry> + </row> + + <row> + <entry><literal>character_set_schema</literal></entry> + <entry><literal>sql_identifier</literal></entry> + <entry>Character sets are currently not implemented as schema objects, so this column is null</entry> + </row> + + <row> + <entry><literal>character_set_name</literal></entry> + <entry><literal>sql_identifier</literal></entry> + <entry>Name of the character set</entry> + </row> + </tbody> + </tgroup> + </table> + </sect1> + <sect1 id="infoschema-column-domain-usage"> <title><literal>column_domain_usage</literal></title> diff --git a/doc/src/sgml/install-windows.sgml b/doc/src/sgml/install-windows.sgml index 8681a7f683..f6d38c1a67 100644 --- a/doc/src/sgml/install-windows.sgml +++ b/doc/src/sgml/install-windows.sgml @@ -48,7 +48,7 @@ <xref linkend="installation"> and the specific notes in <xref linkend="installation-notes-mingw"> and <xref linkend="installation-notes-cygwin">. To produce native 64 bit binaries in these environments, use the tools from - <productname>Mingw64</productname>. These tools can also be used to + <productname>MinGW-w64</productname>. These tools can also be used to cross-compile for 32 bit and 64 bit <productname>Windows</productname> targets on other hosts, such as <productname>Linux</productname> and <productname>Darwin</productname>. diff --git a/doc/src/sgml/installation.sgml b/doc/src/sgml/installation.sgml index 5c719d5a0a..8400ce5d70 100644 --- a/doc/src/sgml/installation.sgml +++ b/doc/src/sgml/installation.sgml @@ -2600,7 +2600,7 @@ cc-1020 cc: ERROR File = pqcomm.c, Line = 427 <para> To build 64 bit binaries using MinGW, install the 64 bit tool set - from <ulink url="http://www.mingw64.org/"></ulink>, put its bin + from <ulink url="http://mingw-w64.sourceforge.net/"></ulink>, put its bin directory in the <envar>PATH</envar>, and run <command>configure</command> with the <command>--host=x86_64-w64-mingw</command> option. diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index 7131fb4ce6..2d15e78fd0 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -3385,15 +3385,17 @@ size_t PQescapeStringConn(PGconn *conn, <listitem> <para> + <function>PQescapeString</> is an older, deprecated version of + <function>PQescapeStringConn</>. <synopsis> size_t PQescapeString (char *to, const char *from, size_t length); </synopsis> </para> <para> - <function>PQescapeString</> is an older, deprecated version of - <function>PQescapeStringConn</>; the difference is that it does - not take <parameter>conn</> or <parameter>error</> parameters. + The only difference from <function>PQescapeStringConn</> is that + <function>PQescapeString</> does not take <structname>PGconn</> + or <parameter>error</> parameters. Because of this, it cannot adjust its behavior depending on the connection properties (such as character encoding) and therefore <emphasis>it might give the wrong results</>. Also, it has no way @@ -3401,7 +3403,7 @@ size_t PQescapeString (char *to, const char *from, size_t length); </para> <para> - <function>PQescapeString</> can be used safely in single-threaded + <function>PQescapeString</> can be used safely in client programs that work with only one <productname>PostgreSQL</> connection at a time (in this case it can find out what it needs to know <quote>behind the scenes</>). In other contexts it is a security @@ -3433,16 +3435,11 @@ unsigned char *PQescapeByteaConn(PGconn *conn, </para> <para> - Certain byte values <emphasis>must</emphasis> be escaped (but all - byte values <emphasis>can</emphasis> be escaped) when used as part - of a <type>bytea</type> literal in an <acronym>SQL</acronym> - statement. In general, to escape a byte, it is converted into the - three digit octal number equal to the octet value, and preceded by - usually two backslashes. The single quote (<literal>'</>) and backslash - (<literal>\</>) characters have special alternative escape - sequences. See <xref linkend="datatype-binary"> for more - information. <function>PQescapeByteaConn</function> performs this - operation, escaping only the minimally required bytes. + Certain byte values must be escaped when used as part of a + <type>bytea</type> literal in an <acronym>SQL</acronym> statement. + <function>PQescapeByteaConn</function> escapes bytes using + either hex encoding or backslash escaping. See <xref + linkend="datatype-binary"> for more information. </para> <para> @@ -3499,20 +3496,13 @@ unsigned char *PQescapeBytea(const unsigned char *from, <para> The only difference from <function>PQescapeByteaConn</> is that <function>PQescapeBytea</> does not take a <structname>PGconn</> - parameter. Because of this, it cannot adjust its behavior - depending on the connection properties (in particular, whether - standard-conforming strings are enabled) and therefore - <emphasis>it might give the wrong results</>. Also, it has no - way to return an error message on failure. - </para> - - <para> - <function>PQescapeBytea</> can be used safely in single-threaded - client programs that work with only one <productname>PostgreSQL</> - connection at a time (in this case it can find out what it needs - to know <quote>behind the scenes</>). In other contexts it is - a security hazard and should be avoided in favor of - <function>PQescapeByteaConn</>. + parameter. Because of this, <function>PQescapeBytea</> can + only be used safely in client programs that use a single + <productname>PostgreSQL</> connection at a time (in this case + it can find out what it needs to know <quote>behind the + scenes</>). It <emphasis>might give the wrong results</> if + used in programs that use multiple database connections (use + <function>PQescapeByteaConn</> in such cases). </para> </listitem> </varlistentry> diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index ca8342155d..58e3459e67 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -267,7 +267,7 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re by backends (that is, not by the background writer), how many times those backends had to execute their own fsync calls (normally the background writer handles those even when the backend does its own - write), and total buffers allocated. + write), total buffers allocated, and time of last statistics reset. </entry> </row> @@ -278,9 +278,9 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re number of transactions committed and rolled back in that database, total disk blocks read, total buffer hits (i.e., block read requests avoided by finding the block already in buffer cache), - number of rows returned, fetched, inserted, updated and deleted, and + number of rows returned, fetched, inserted, updated and deleted, the total number of queries cancelled due to conflict with recovery (on - standby servers). + standby servers), and time of last statistics reset. </entry> </row> @@ -298,8 +298,11 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re <entry><structname>pg_stat_replication</><indexterm><primary>pg_stat_replication</primary></indexterm></entry> <entry>One row per WAL sender process, showing process <acronym>ID</>, user OID, user name, application name, client's address and port number, - time at which the server process began execution, current WAL sender - state and transaction log location. The columns detailing what exactly + time at which the server process began execution, and the current WAL + sender state and transaction log location. In addition, the standby + reports the last transaction log position it received and wrote, the last + position it flushed to disk, and the last position it replayed, and this + information is also displayed here. The columns detailing what exactly the connection is doing are only visible if the user examining the view is a superuser. </entry> @@ -663,6 +666,19 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re </row> <row> + <entry><literal><function>pg_stat_get_db_stat_reset_time</function>(<type>oid</type>)</literal></entry> + <entry><type>timestamptz</type></entry> + <entry> + Time of the last statistics reset for the database. Initialized to the + system time during the first connection to each database. The reset time + is updated when you call <function>pg_stat_reset</function> on the + database, as well as upon execution of + <function>pg_stat_reset_single_table_counters</function> against any + table or index in it. + </entry> + </row> + + <row> <entry><literal><function>pg_stat_get_numscans</function>(<type>oid</type>)</literal></entry> <entry><type>bigint</type></entry> <entry> @@ -1127,6 +1143,16 @@ postgres: <replaceable>user</> <replaceable>database</> <replaceable>host</> <re </row> <row> + <entry><literal><function>pg_stat_get_bgwriter_stat_reset_time()</function></literal></entry> + <entry><type>timestamptz</type></entry> + <entry> + Time of the last statistics reset for the background writer, updated + when executing <function>pg_stat_reset_shared('bgwriter')</function> + on the database cluster. + </entry> + </row> + + <row> <entry><literal><function>pg_stat_get_buf_written_backend()</function></literal></entry> <entry><type>bigint</type></entry> <entry> diff --git a/doc/src/sgml/mvcc.sgml b/doc/src/sgml/mvcc.sgml index f71f978ef2..f42bb091c1 100644 --- a/doc/src/sgml/mvcc.sgml +++ b/doc/src/sgml/mvcc.sgml @@ -604,7 +604,7 @@ ERROR: could not serialize access due to read/write dependencies among transact Consistent use of Serializable transactions can simplify development. The guarantee that any set of concurrent serializable transactions will have the same effect as if they were run one at a time means that if - you can demonstrate that a singe transaction, as written, will do the + you can demonstrate that a single transaction, as written, will do the right thing when run by itself, you can have confidence that it will do the right thing in any mix of serializable transactions, even without any information about what those other transactions might do. It is @@ -670,7 +670,7 @@ ERROR: could not serialize access due to read/write dependencies among transact permanent database writes within Serializable transactions on the master will ensure that all standbys will eventually reach a consistent state, a Repeatable Read transaction run on the standby can sometimes - see a transient state which in inconsistent with any serial execution + see a transient state which is inconsistent with any serial execution of serializable transactions on the master. </para> </warning> diff --git a/doc/src/sgml/pgstatstatements.sgml b/doc/src/sgml/pgstatstatements.sgml index 58b37d243c..8cff1020bb 100644 --- a/doc/src/sgml/pgstatstatements.sgml +++ b/doc/src/sgml/pgstatstatements.sgml @@ -148,7 +148,7 @@ <para> This view, and the function <function>pg_stat_statements_reset</>, are available only in databases they have been specifically installed into - by running the <filename>pg_stat_statements.sql</> install script. + by installing the <literal>pg_stat_statements</> extension. However, statistics are tracked across all databases of the server whenever the <filename>pg_stat_statements</filename> module is loaded into the server, regardless of presence of the view. diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml index a2601e6bc8..c342916ff3 100644 --- a/doc/src/sgml/plpgsql.sgml +++ b/doc/src/sgml/plpgsql.sgml @@ -1263,7 +1263,7 @@ EXECUTE 'UPDATE tbl SET ' <programlisting> EXECUTE format('UPDATE tbl SET %I = %L WHERE key = %L', colname, newvalue, keyvalue); </programlisting> - The <function>format</function> function can be used in conjunction with + The <function>format</function> function can be used in conjunction with the <literal>USING</literal> clause: <programlisting> EXECUTE format('UPDATE tbl SET %I = $1 WHERE key = $2', colname) @@ -1356,19 +1356,15 @@ GET DIAGNOSTICS integer_var = ROW_COUNT; true if it successfully repositions the cursor, false otherwise. </para> </listitem> - <listitem> <para> - A <command>FOR</> statement sets <literal>FOUND</literal> true - if it iterates one or more times, else false. This applies to - all four variants of the <command>FOR</> statement (integer - <command>FOR</> loops, record-set <command>FOR</> loops, - dynamic record-set <command>FOR</> loops, and cursor - <command>FOR</> loops). + A <command>FOR</> or <command>FOREACH</> statement sets + <literal>FOUND</literal> true + if it iterates one or more times, else false. <literal>FOUND</literal> is set this way when the - <command>FOR</> loop exits; inside the execution of the loop, + loop exits; inside the execution of the loop, <literal>FOUND</literal> is not modified by the - <command>FOR</> statement, although it might be changed by the + loop statement, although it might be changed by the execution of other statements within the loop body. </para> </listitem> @@ -1910,9 +1906,9 @@ END CASE; <para> With the <literal>LOOP</>, <literal>EXIT</>, - <literal>CONTINUE</>, <literal>WHILE</>, and <literal>FOR</> - statements, you can arrange for your <application>PL/pgSQL</> - function to repeat a series of commands. + <literal>CONTINUE</>, <literal>WHILE</>, <literal>FOR</>, + and <literal>FOREACH</> statements, you can arrange for your + <application>PL/pgSQL</> function to repeat a series of commands. </para> <sect3> @@ -2238,6 +2234,90 @@ END LOOP <optional> <replaceable>label</replaceable> </optional>; </para> </sect2> + <sect2 id="plpgsql-foreach-array"> + <title>Looping Through Arrays</title> + + <para> + The <literal>FOREACH</> loop is much like a <literal>FOR</> loop, + but instead of iterating through the rows returned by a SQL query, + it iterates through the elements of an array value. + (In general, <literal>FOREACH</> is meant for looping through + components of a composite-valued expression; variants for looping + through composites besides arrays may be added in future.) + The <literal>FOREACH</> statement to loop over an array is: + +<synopsis> +<optional> <<<replaceable>label</replaceable>>> </optional> +FOREACH <replaceable>target</replaceable> <optional> SLICE <replaceable>number</replaceable> </optional> IN ARRAY <replaceable>expression</replaceable> LOOP + <replaceable>statements</replaceable> +END LOOP <optional> <replaceable>label</replaceable> </optional>; +</synopsis> + </para> + + <para> + Without <literal>SLICE</>, or if <literal>SLICE 0</> is specified, + the loop iterates through individual elements of the array produced + by evaluating the <replaceable>expression</replaceable>. + The <replaceable>target</replaceable> variable is assigned each + element value in sequence, and the loop body is executed for each element. + Here is an example of looping through the elements of an integer + array: + +<programlisting> +CREATE FUNCTION sum(int[]) RETURNS int8 AS $$ +DECLARE + s int8 := 0; + x int; +BEGIN + FOREACH x IN ARRAY $1 + LOOP + s := s + x; + END LOOP; + RETURN s; +END; +$$ LANGUAGE plpgsql; +</programlisting> + + The elements are visited in storage order, regardless of the number of + array dimensions. Although the <replaceable>target</replaceable> is + usually just a single variable, it can be a list of variables when + looping through an array of composite values (records). In that case, + for each array element, the variables are assigned from successive + columns of the composite value. + </para> + + <para> + With a positive <literal>SLICE</> value, <literal>FOREACH</> + iterates through slices of the array rather than single elements. + The <literal>SLICE</> value must be an integer constant not larger + than the number of dimensions of the array. The + <replaceable>target</replaceable> variable must be an array, + and it receives successive slices of the array value, where each slice + is of the number of dimensions specified by <literal>SLICE</>. + Here is an example of iterating through one-dimensional slices: + +<programlisting> +CREATE FUNCTION scan_rows(int[]) RETURNS void AS $$ +DECLARE + x int[]; +BEGIN + FOREACH x SLICE 1 IN ARRAY $1 + LOOP + RAISE NOTICE 'row = %', x; + END LOOP; +END; +$$ LANGUAGE plpgsql; + +SELECT scan_rows(ARRAY[[1,2,3],[4,5,6],[7,8,9],[10,11,12]]); + +NOTICE: row = {1,2,3} +NOTICE: row = {4,5,6} +NOTICE: row = {7,8,9} +NOTICE: row = {10,11,12} +</programlisting> + </para> + </sect2> + <sect2 id="plpgsql-error-trapping"> <title>Trapping Errors</title> diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml index b93c268167..c923d3b154 100644 --- a/doc/src/sgml/protocol.sgml +++ b/doc/src/sgml/protocol.sgml @@ -1469,11 +1469,87 @@ The commands accepted in walsender mode are: shutdown), it will send a CommandComplete message before exiting. This might not happen during an abnormal shutdown, of course. </para> + + <para> + The receiving process can send a status update back to the sender at + any time, using the following message format (also in the payload of + a CopyData message): + </para> + + <para> + <variablelist> + <varlistentry> + <term> + Standby status update (F) + </term> + <listitem> + <para> + <variablelist> + <varlistentry> + <term> + Byte1('r') + </term> + <listitem> + <para> + Identifies the message as a receiver status update. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + Byte8 + </term> + <listitem> + <para> + The location of the last WAL byte + 1 received and written to disk + in the standby, in XLogRecPtr format. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + Byte8 + </term> + <listitem> + <para> + The location of the last WAL byte + 1 flushed to disk in + the standby, in XLogRecPtr format. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + Byte8 + </term> + <listitem> + <para> + The location of the last WAL byte + 1 applied in the standby, in + XLogRecPtr format. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term> + Byte8 + </term> + <listitem> + <para> + The server's system clock at the time of transmission, + given in TimestampTz format. + </para> + </listitem> + </varlistentry> + </variablelist> + </para> + </listitem> + </varlistentry> + </variablelist> + </para> </listitem> </varlistentry> <varlistentry> - <term>BASE_BACKUP [<literal>LABEL</literal> <replaceable>'label'</replaceable>] [<literal>PROGRESS</literal>] [<literal>FAST</literal>] [<literal>WAL</literal>]</term> + <term>BASE_BACKUP [<literal>LABEL</literal> <replaceable>'label'</replaceable>] [<literal>PROGRESS</literal>] [<literal>FAST</literal>] [<literal>WAL</literal>] [<literal>NOWAIT</literal>]</term> <listitem> <para> Instructs the server to start streaming a base backup. @@ -1530,6 +1606,19 @@ The commands accepted in walsender mode are: </para> </listitem> </varlistentry> + + <varlistentry> + <term><literal>NOWAIT</literal></term> + <listitem> + <para> + By default, the backup will wait until the last required xlog + segment has been archived, or emit a warning if log archiving is + not enabled. Specifying <literal>NOWAIT</literal> disables both + the waiting and the warning, leaving the client responsible for + ensuring the required log is available. + </para> + </listitem> + </varlistentry> </variablelist> </para> <para> diff --git a/doc/src/sgml/recovery-config.sgml b/doc/src/sgml/recovery-config.sgml index 454ec84f1a..602fbe2c76 100644 --- a/doc/src/sgml/recovery-config.sgml +++ b/doc/src/sgml/recovery-config.sgml @@ -143,6 +143,25 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows <title>Recovery Target Settings</title> <variablelist> + <varlistentry id="recovery-target-name" xreflabel="recovery_target_name"> + <term><varname>recovery_target_name</varname> + (<type>string</type>) + </term> + <indexterm> + <primary><varname>recovery_target_name</> recovery parameter</primary> + </indexterm> + <listitem> + <para> + This parameter specifies the named restore point, created with + <function>pg_create_restore_point()</> to which recovery will proceed. + At most one of <varname>recovery_target_name</>, + <xref linkend="recovery-target-time"> or + <xref linkend="recovery-target-xid"> can be specified. The default is to + recover to the end of the WAL log. + </para> + </listitem> + </varlistentry> + <varlistentry id="recovery-target-time" xreflabel="recovery_target_time"> <term><varname>recovery_target_time</varname> (<type>timestamp</type>) @@ -154,7 +173,8 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows <para> This parameter specifies the time stamp up to which recovery will proceed. - At most one of <varname>recovery_target_time</> and + At most one of <varname>recovery_target_time</>, + <xref linkend="recovery-target-name"> or <xref linkend="recovery-target-xid"> can be specified. The default is to recover to the end of the WAL log. The precise stopping point is also influenced by @@ -176,7 +196,8 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows start, transactions can complete in a different numeric order. The transactions that will be recovered are those that committed before (and optionally including) the specified one. - At most one of <varname>recovery_target_xid</> and + At most one of <varname>recovery_target_xid</>, + <xref linkend="recovery-target-name"> or <xref linkend="recovery-target-time"> can be specified. The default is to recover to the end of the WAL log. The precise stopping point is also influenced by @@ -227,6 +248,31 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows </listitem> </varlistentry> + <varlistentry id="pause-at-recovery-target" + xreflabel="pause_at_recovery_target"> + <term><varname>pause_at_recovery_target</varname> + (<type>boolean</type>) + </term> + <indexterm> + <primary><varname>pause_at_recovery_target</> recovery parameter</primary> + </indexterm> + <listitem> + <para> + Specifies whether recovery should pause when the recovery target + is reached. The default is true, if a recovery target is set. + This is intended to allow queries to be executed against the + database to check if this recovery target is the most desirable + point for recovery. The paused state can be resumed by using + <function>pg_xlog_replay_resume()</> (See + <xref linkend="functions-recovery-control-table">), which then + causes recovery to end. If this recovery target is not the + desired stopping point, then shutdown the server, change the + recovery target settings to a later target and restart to + continue recovery. + </para> + </listitem> + </varlistentry> + </variablelist> </sect1> @@ -297,8 +343,8 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows <listitem> <para> Specifies a trigger file whose presence ends recovery in the - standby. If no trigger file is specified, the standby never exits - recovery. + standby. Even if this value is not set, you can still promote + the standby using <command>pg_ctl promote</>. This setting has no effect if <varname>standby_mode</> is <literal>off</>. </para> </listitem> diff --git a/doc/src/sgml/ref/allfiles.sgml b/doc/src/sgml/ref/allfiles.sgml index c44d11ef91..ac6ac5b3d2 100644 --- a/doc/src/sgml/ref/allfiles.sgml +++ b/doc/src/sgml/ref/allfiles.sgml @@ -7,10 +7,12 @@ Complete list of usable sgml source files in this directory. <!-- SQL commands --> <!entity abort system "abort.sgml"> <!entity alterAggregate system "alter_aggregate.sgml"> +<!entity alterCollation system "alter_collation.sgml"> <!entity alterConversion system "alter_conversion.sgml"> <!entity alterDatabase system "alter_database.sgml"> <!entity alterDefaultPrivileges system "alter_default_privileges.sgml"> <!entity alterDomain system "alter_domain.sgml"> +<!entity alterExtension system "alter_extension.sgml"> <!entity alterForeignDataWrapper system "alter_foreign_data_wrapper.sgml"> <!entity alterForeignTable system "alter_foreign_table.sgml"> <!entity alterFunction system "alter_function.sgml"> @@ -47,9 +49,11 @@ Complete list of usable sgml source files in this directory. <!entity copyTable system "copy.sgml"> <!entity createAggregate system "create_aggregate.sgml"> <!entity createCast system "create_cast.sgml"> +<!entity createCollation system "create_collation.sgml"> <!entity createConversion system "create_conversion.sgml"> <!entity createDatabase system "create_database.sgml"> <!entity createDomain system "create_domain.sgml"> +<!entity createExtension system "create_extension.sgml"> <!entity createForeignDataWrapper system "create_foreign_data_wrapper.sgml"> <!entity createForeignTable system "create_foreign_table.sgml"> <!entity createFunction system "create_function.sgml"> @@ -83,9 +87,11 @@ Complete list of usable sgml source files in this directory. <!entity do system "do.sgml"> <!entity dropAggregate system "drop_aggregate.sgml"> <!entity dropCast system "drop_cast.sgml"> +<!entity dropCollation system "drop_collation.sgml"> <!entity dropConversion system "drop_conversion.sgml"> <!entity dropDatabase system "drop_database.sgml"> <!entity dropDomain system "drop_domain.sgml"> +<!entity dropExtension system "drop_extension.sgml"> <!entity dropForeignDataWrapper system "drop_foreign_data_wrapper.sgml"> <!entity dropForeignTable system "drop_foreign_table.sgml"> <!entity dropFunction system "drop_function.sgml"> diff --git a/doc/src/sgml/ref/alter_collation.sgml b/doc/src/sgml/ref/alter_collation.sgml new file mode 100644 index 0000000000..3aef656a0e --- /dev/null +++ b/doc/src/sgml/ref/alter_collation.sgml @@ -0,0 +1,128 @@ +<!-- +doc/src/sgml/ref/alter_collation.sgml +PostgreSQL documentation +--> + +<refentry id="SQL-ALTERCOLLATION"> + <refmeta> + <refentrytitle>ALTER COLLATION</refentrytitle> + <manvolnum>7</manvolnum> + <refmiscinfo>SQL - Language Statements</refmiscinfo> + </refmeta> + + <refnamediv> + <refname>ALTER COLLATION</refname> + <refpurpose>change the definition of a collation</refpurpose> + </refnamediv> + + <indexterm zone="sql-altercollation"> + <primary>ALTER COLLATION</primary> + </indexterm> + + <refsynopsisdiv> +<synopsis> +ALTER COLLATION <replaceable>name</replaceable> RENAME TO <replaceable>new_name</replaceable> +ALTER COLLATION <replaceable>name</replaceable> OWNER TO <replaceable>new_owner</replaceable> +ALTER COLLATION <replaceable>name</replaceable> SET SCHEMA <replaceable>new_schema</replaceable> +</synopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para> + <command>ALTER COLLATION</command> changes the definition of a + collation. + </para> + + <para> + You must own the collation to use <command>ALTER COLLATION</>. + To alter the owner, you must also be a direct or indirect member of the new + owning role, and that role must have <literal>CREATE</literal> privilege on + the collation's schema. (These restrictions enforce that altering the + owner doesn't do anything you couldn't do by dropping and recreating the + collation. However, a superuser can alter ownership of any collation + anyway.) + </para> + </refsect1> + + <refsect1> + <title>Parameters</title> + + <variablelist> + <varlistentry> + <term><replaceable class="parameter">name</replaceable></term> + <listitem> + <para> + The name (optionally schema-qualified) of an existing collation. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable class="parameter">new_name</replaceable></term> + <listitem> + <para> + The new name of the collation. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable class="parameter">new_owner</replaceable></term> + <listitem> + <para> + The new owner of the collation. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable class="parameter">new_schema</replaceable></term> + <listitem> + <para> + The new schema for the collation. + </para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1> + <title>Examples</title> + + <para> + To rename the collation <literal>de_DE</literal> to + <literal>german</literal>: +<programlisting> +ALTER COLLATION "de_DE" RENAME TO german; +</programlisting> + </para> + + <para> + To change the owner of the collation <literal>en_US</literal> to + <literal>joe</literal>: +<programlisting> +ALTER COLLATION "en_US" OWNER TO joe; +</programlisting> + </para> + </refsect1> + + <refsect1> + <title>Compatibility</title> + + <para> + There is no <command>ALTER COLLATION</command> statement in the SQL + standard. + </para> + </refsect1> + + <refsect1> + <title>See Also</title> + + <simplelist type="inline"> + <member><xref linkend="sql-createcollation"></member> + <member><xref linkend="sql-dropcollation"></member> + </simplelist> + </refsect1> +</refentry> diff --git a/doc/src/sgml/ref/alter_extension.sgml b/doc/src/sgml/ref/alter_extension.sgml new file mode 100644 index 0000000000..d12aee251b --- /dev/null +++ b/doc/src/sgml/ref/alter_extension.sgml @@ -0,0 +1,289 @@ +<!-- +doc/src/sgml/ref/alter_extension.sgml +PostgreSQL documentation +--> + +<refentry id="SQL-ALTEREXTENSION"> + <refmeta> + <refentrytitle>ALTER EXTENSION</refentrytitle> + <manvolnum>7</manvolnum> + <refmiscinfo>SQL - Language Statements</refmiscinfo> + </refmeta> + + <refnamediv> + <refname>ALTER EXTENSION</refname> + <refpurpose> + change the definition of an extension + </refpurpose> + </refnamediv> + + <indexterm zone="sql-alterextension"> + <primary>ALTER EXTENSION</primary> + </indexterm> + + <refsynopsisdiv> +<synopsis> +ALTER EXTENSION <replaceable class="PARAMETER">extension_name</replaceable> UPDATE [ TO <replaceable class="PARAMETER">new_version</replaceable> ] +ALTER EXTENSION <replaceable class="PARAMETER">extension_name</replaceable> SET SCHEMA <replaceable class="PARAMETER">new_schema</replaceable> +ALTER EXTENSION <replaceable class="PARAMETER">extension_name</replaceable> ADD <replaceable class="PARAMETER">member_object</replaceable> +ALTER EXTENSION <replaceable class="PARAMETER">extension_name</replaceable> DROP <replaceable class="PARAMETER">member_object</replaceable> + +<phrase>where <replaceable class="PARAMETER">member_object</replaceable> is:</phrase> + + AGGREGATE <replaceable class="PARAMETER">agg_name</replaceable> (<replaceable class="PARAMETER">agg_type</replaceable> [, ...] ) | + CAST (<replaceable>source_type</replaceable> AS <replaceable>target_type</replaceable>) | + COLLATION <replaceable class="PARAMETER">object_name</replaceable> | + CONVERSION <replaceable class="PARAMETER">object_name</replaceable> | + DOMAIN <replaceable class="PARAMETER">object_name</replaceable> | + FOREIGN DATA WRAPPER <replaceable class="PARAMETER">object_name</replaceable> | + FOREIGN TABLE <replaceable class="PARAMETER">object_name</replaceable> | + FUNCTION <replaceable class="PARAMETER">function_name</replaceable> ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) | + OPERATOR <replaceable class="PARAMETER">operator_name</replaceable> (<replaceable class="PARAMETER">left_type</replaceable>, <replaceable class="PARAMETER">right_type</replaceable>) | + OPERATOR CLASS <replaceable class="PARAMETER">object_name</replaceable> USING <replaceable class="parameter">index_method</replaceable> | + OPERATOR FAMILY <replaceable class="PARAMETER">object_name</replaceable> USING <replaceable class="parameter">index_method</replaceable> | + [ PROCEDURAL ] LANGUAGE <replaceable class="PARAMETER">object_name</replaceable> | + SCHEMA <replaceable class="PARAMETER">object_name</replaceable> | + SEQUENCE <replaceable class="PARAMETER">object_name</replaceable> | + SERVER <replaceable class="PARAMETER">object_name</replaceable> | + TABLE <replaceable class="PARAMETER">object_name</replaceable> | + TEXT SEARCH CONFIGURATION <replaceable class="PARAMETER">object_name</replaceable> | + TEXT SEARCH DICTIONARY <replaceable class="PARAMETER">object_name</replaceable> | + TEXT SEARCH PARSER <replaceable class="PARAMETER">object_name</replaceable> | + TEXT SEARCH TEMPLATE <replaceable class="PARAMETER">object_name</replaceable> | + TYPE <replaceable class="PARAMETER">object_name</replaceable> | + VIEW <replaceable class="PARAMETER">object_name</replaceable> +</synopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para> + <command>ALTER EXTENSION</command> changes the definition of an installed + extension. There are several subforms: + + <variablelist> + <varlistentry> + <term><literal>UPDATE</literal></term> + <listitem> + <para> + This form updates the extension to a newer version. The extension + must supply a suitable update script (or series of scripts) that can + modify the currently-installed version into the requested version. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>SET SCHEMA</literal></term> + <listitem> + <para> + This form moves the extension's objects into another schema. The + extension has to be <firstterm>relocatable</> for this command to + succeed. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>ADD <replaceable class="PARAMETER">member_object</replaceable></literal></term> + <listitem> + <para> + This form adds an existing object to the extension. This is mainly + useful in extension update scripts. The object will subsequently + be treated as a member of the extension; notably, it can only be + dropped by dropping the extension. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>DROP <replaceable class="PARAMETER">member_object</replaceable></literal></term> + <listitem> + <para> + This form removes a member object from the extension. This is mainly + useful in extension update scripts. The object is not dropped, only + disassociated from the extension. + </para> + </listitem> + </varlistentry> + </variablelist> + + See <xref linkend="extend-extensions"> for more information about these + operations. + </para> + + <para> + Only superusers can execute <command>ALTER EXTENSION</command>. + </para> + </refsect1> + + <refsect1> + <title>Parameters</title> + + <para> + <variablelist> + <varlistentry> + <term><replaceable class="PARAMETER">extension_name</replaceable></term> + <listitem> + <para> + The name of an installed extension. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable class="PARAMETER">new_version</replaceable></term> + <listitem> + <para> + The desired new version of the extension. This can be written as + either an identifier or a string literal. If not specified, + <command>ALTER EXTENSION UPDATE</> attempts to update to whatever is + shown as the default version in the extension's control file. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable class="PARAMETER">new_schema</replaceable></term> + <listitem> + <para> + The new schema for the extension. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable class="parameter">object_name</replaceable></term> + <term><replaceable class="parameter">agg_name</replaceable></term> + <term><replaceable class="parameter">function_name</replaceable></term> + <term><replaceable class="parameter">operator_name</replaceable></term> + <listitem> + <para> + The name of an object to be added to or removed from the extension. + Names of tables, + aggregates, domains, foreign tables, functions, operators, + operator classes, operator families, sequences, text search objects, + types, and views can be schema-qualified. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable class="parameter">agg_type</replaceable></term> + <listitem> + <para> + An input data type on which the aggregate function operates. + To reference a zero-argument aggregate function, write <literal>*</> + in place of the list of input data types. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable>source_type</replaceable></term> + <listitem> + <para> + The name of the source data type of the cast. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable>target_type</replaceable></term> + <listitem> + <para> + The name of the target data type of the cast. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable class="parameter">argmode</replaceable></term> + + <listitem> + <para> + The mode of a function argument: <literal>IN</>, <literal>OUT</>, + <literal>INOUT</>, or <literal>VARIADIC</>. + If omitted, the default is <literal>IN</>. + Note that <command>ALTER EXTENSION</command> does not actually pay + any attention to <literal>OUT</> arguments, since only the input + arguments are needed to determine the function's identity. + So it is sufficient to list the <literal>IN</>, <literal>INOUT</>, + and <literal>VARIADIC</> arguments. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable class="parameter">argname</replaceable></term> + + <listitem> + <para> + The name of a function argument. + Note that <command>ALTER EXTENSION</command> does not actually pay + any attention to argument names, since only the argument data + types are needed to determine the function's identity. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable class="parameter">argtype</replaceable></term> + + <listitem> + <para> + The data type(s) of the function's arguments (optionally + schema-qualified), if any. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>PROCEDURAL</literal></term> + + <listitem> + <para> + This is a noise word. + </para> + </listitem> + </varlistentry> + </variablelist> + </para> + </refsect1> + + <refsect1> + <title>Examples</title> + + <para> + To update the <literal>hstore</literal> extension to version 2.0: +<programlisting> +ALTER EXTENSION hstore UPDATE TO '2.0'; +</programlisting> + </para> + + <para> + To change the schema of the <literal>hstore</literal> extension + to <literal>utils</literal>: +<programlisting> +ALTER EXTENSION hstore SET SCHEMA utils; +</programlisting> + </para> + + <para> + To add an existing function to the <literal>hstore</literal> extension: +<programlisting> +ALTER EXTENSION hstore ADD FUNCTION populate_record(anyelement, hstore); +</programlisting> + </para> + </refsect1> + + <refsect1 id="SQL-ALTEREXTENSION-see-also"> + <title>See Also</title> + + <simplelist type="inline"> + <member><xref linkend="sql-createextension"></member> + <member><xref linkend="sql-dropextension"></member> + </simplelist> + </refsect1> +</refentry> diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml index 52f70cea18..b8c4c507a2 100644 --- a/doc/src/sgml/ref/alter_table.sgml +++ b/doc/src/sgml/ref/alter_table.sgml @@ -44,6 +44,8 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable> ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN } ADD <replaceable class="PARAMETER">table_constraint</replaceable> ADD <replaceable class="PARAMETER">table_constraint_using_index</replaceable> + ADD <replaceable class="PARAMETER">table_constraint</replaceable> [ NOT VALID ] + VALIDATE CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> DROP CONSTRAINT [ IF EXISTS ] <replaceable class="PARAMETER">constraint_name</replaceable> [ RESTRICT | CASCADE ] DISABLE TRIGGER [ <replaceable class="PARAMETER">trigger_name</replaceable> | ALL | USER ] ENABLE TRIGGER [ <replaceable class="PARAMETER">trigger_name</replaceable> | ALL | USER ] @@ -227,11 +229,27 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable> </varlistentry> <varlistentry> - <term><literal>ADD <replaceable class="PARAMETER">table_constraint</replaceable></literal></term> + <term><literal>ADD <replaceable class="PARAMETER">table_constraint</replaceable> + [ NOT VALID ]</literal></term> <listitem> <para> This form adds a new constraint to a table using the same syntax as - <xref linkend="SQL-CREATETABLE">. + <xref linkend="SQL-CREATETABLE">. Newly added foreign key constraints can + also be defined as <literal>NOT VALID</literal> to avoid the + potentially lengthy initial check that must otherwise be performed. + Constraint checks are skipped at create table time, so + <xref linkend="SQL-CREATETABLE"> does not contain this option. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>VALIDATE CONSTRAINT</literal></term> + <listitem> + <para> + This form validates a foreign key constraint that was previously created + as <literal>NOT VALID</literal>. Constraints already marked valid do not + cause an error response. </para> </listitem> </varlistentry> @@ -748,9 +766,14 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable> <para> Adding a column with a non-null default or changing the type of an existing column will require the entire table and indexes to be rewritten. - This might take a significant amount of time for a large table; and it will - temporarily require double the disk space. Adding or removing a system - <literal>oid</> column likewise requires rewriting the entire table. + As an exception, if the <literal>USING</> clause does not change the column + contents and the old type is either binary coercible to the new type or + an unconstrained domain over the new type, a table rewrite is not needed, + but any indexes on the affected columns must still be rebuilt. Adding or + removing a system <literal>oid</> column also requires rewriting the entire + table. Table and/or index rebuilds may take a significant amount of time + for a large table; and will temporarily require as much as double the disk + space. </para> <para> @@ -779,9 +802,9 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable> <para> To force an immediate rewrite of the table, you can use <link linkend="SQL-VACUUM">VACUUM FULL</>, <xref linkend="SQL-CLUSTER"> - or one of the forms of ALTER TABLE that forces a rewrite, such as - SET DATA TYPE. This results in no semantically-visible change in the - table, but gets rid of no-longer-useful data. + or one of the forms of ALTER TABLE that forces a rewrite. This results in + no semantically-visible change in the table, but gets rid of + no-longer-useful data. </para> <para> diff --git a/doc/src/sgml/ref/comment.sgml b/doc/src/sgml/ref/comment.sgml index f1a1605df3..2610fd5b8d 100644 --- a/doc/src/sgml/ref/comment.sgml +++ b/doc/src/sgml/ref/comment.sgml @@ -27,10 +27,12 @@ COMMENT ON COLUMN <replaceable class="PARAMETER">table_name</replaceable>.<replaceable class="PARAMETER">column_name</replaceable> | AGGREGATE <replaceable class="PARAMETER">agg_name</replaceable> (<replaceable class="PARAMETER">agg_type</replaceable> [, ...] ) | CAST (<replaceable>source_type</replaceable> AS <replaceable>target_type</replaceable>) | + COLLATION <replaceable class="PARAMETER">object_name</replaceable> | CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> ON <replaceable class="PARAMETER">table_name</replaceable> | CONVERSION <replaceable class="PARAMETER">object_name</replaceable> | DATABASE <replaceable class="PARAMETER">object_name</replaceable> | DOMAIN <replaceable class="PARAMETER">object_name</replaceable> | + EXTENSION <replaceable class="PARAMETER">object_name</replaceable> | FOREIGN TABLE <replaceable class="PARAMETER">object_name</replaceable> | FUNCTION <replaceable class="PARAMETER">function_name</replaceable> ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) | INDEX <replaceable class="PARAMETER">object_name</replaceable> | @@ -244,6 +246,7 @@ COMMENT ON TABLE mytable IS NULL; <programlisting> COMMENT ON AGGREGATE my_aggregate (double precision) IS 'Computes sample variance'; COMMENT ON CAST (text AS int4) IS 'Allow casts from text to int4'; +COMMENT ON COLLATION "fr_CA" IS 'Canadian French'; COMMENT ON COLUMN my_table.my_column IS 'Employee ID number'; COMMENT ON CONVERSION my_conv IS 'Conversion to UTF8'; COMMENT ON DATABASE my_database IS 'Development Database'; diff --git a/doc/src/sgml/ref/create_collation.sgml b/doc/src/sgml/ref/create_collation.sgml new file mode 100644 index 0000000000..9d03ca5a4e --- /dev/null +++ b/doc/src/sgml/ref/create_collation.sgml @@ -0,0 +1,175 @@ +<!-- doc/src/sgml/ref/create_collation.sgml --> + +<refentry id="SQL-CREATECOLLATION"> + <refmeta> + <refentrytitle>CREATE COLLATION</refentrytitle> + <manvolnum>7</manvolnum> + <refmiscinfo>SQL - Language Statements</refmiscinfo> + </refmeta> + + <refnamediv> + <refname>CREATE COLLATION</refname> + <refpurpose>define a new collation</refpurpose> + </refnamediv> + + <indexterm zone="sql-createcollation"> + <primary>CREATE COLLATION</primary> + </indexterm> + + <refsynopsisdiv> +<synopsis> +CREATE COLLATION <replaceable>name</replaceable> ( + [ LOCALE = <replaceable>locale</replaceable>, ] + [ LC_COLLATE = <replaceable>lc_collate</replaceable>, ] + [ LC_CTYPE = <replaceable>lc_ctype</replaceable>, ] +) +CREATE COLLATION <replaceable>name</replaceable> FROM <replaceable>existing_collation</replaceable> +</synopsis> + </refsynopsisdiv> + + <refsect1 id="sql-createcollation-description"> + <title>Description</title> + + <para> + <command>CREATE COLLATION</command> defines a new collation using + the specified operating system locales or from an existing collation. + </para> + + <para> + To be able to create a collation, you must + have <literal>CREATE</literal> privilege on the destination schema. + </para> + </refsect1> + + + <refsect1> + <title>Parameters</title> + + <variablelist> + <varlistentry> + <term><replaceable>name</replaceable></term> + + <listitem> + <para> + The name of the collation. The collation name can be + schema-qualified. If it is not, the collation is defined in the + current schema. The collation name must be unique within a + schema. (The system catalogs can contain collations with the + same name for other encodings, but these are not usable if the + database encoding does not match.) + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable>existing_collation</replaceable></term> + + <listitem> + <para> + The name of an existing collation to copy. The new collation + will have the same properties as the existing one, but they + will become independent objects. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable>locale</replaceable></term> + + <listitem> + <para> + This is a shortcut for setting <symbol>LC_COLLATE</symbol> + and <symbol>LC_CTYPE</symbol> at once. If you specify this, + you cannot specify either of the other parameters. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable>lc_collate</replaceable></term> + + <listitem> + <para> + Use the specified operating system locale for + the <symbol>LC_COLLATE</symbol> locale category. The locale + must be applicable to the current database encoding. + (See <xref linkend="sql-createdatabase"> for the precise + rules.) + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable>lc_ctype</replaceable></term> + + <listitem> + <para> + Use the specified operating system locale for + the <symbol>LC_CTYPE</symbol> locale category. The locale + must be applicable to the current database encoding. + (See <xref linkend="sql-createdatabase"> for the precise + rules.) + </para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> + + + <refsect1 id="sql-createcollation-notes"> + <title>Notes</title> + + <para> + Use <command>DROP COLLATION</command> to remove user-defined collations. + </para> + + <para> + See <xref linkend="collation"> for more information about collation + support in PostgreSQL. + </para> + </refsect1> + + <refsect1 id="sql-createcollation-examples"> + <title>Examples</title> + + <para> + To create a collation from the locale <literal>fr_FR.utf8</literal> + (assuming the current database encoding is <literal>UTF8</literal>): +<programlisting> +CREATE COLLATION french (LOCALE = 'fr_FR.utf8'); +</programlisting> + </para> + + <para> + To create a collation from an existing collation: +<programlisting> +CREATE COLLATION german FROM "de_DE"; +</programlisting> + This can be convenient to be able to use operating-system + independent collation names in applications. + </para> + </refsect1> + + + <refsect1 id="sql-createcollation-compat"> + <title>Compatibility</title> + + <para> + There is a <command>CREATE COLLATION</command> statement in the SQL + standard, but it is limited to copying an existing collation. The + syntax to create a new collation is + a <productname>PostgreSQL</productname> extension. + </para> + </refsect1> + + + <refsect1 id="sql-createcollation-seealso"> + <title>See Also</title> + + <simplelist type="inline"> + <member><xref linkend="sql-altercollation"></member> + <member><xref linkend="sql-dropcollation"></member> + </simplelist> + </refsect1> + +</refentry> diff --git a/doc/src/sgml/ref/create_domain.sgml b/doc/src/sgml/ref/create_domain.sgml index 87a7654d6c..83be889c6d 100644 --- a/doc/src/sgml/ref/create_domain.sgml +++ b/doc/src/sgml/ref/create_domain.sgml @@ -22,6 +22,7 @@ PostgreSQL documentation <refsynopsisdiv> <synopsis> CREATE DOMAIN <replaceable class="parameter">name</replaceable> [ AS ] <replaceable class="parameter">data_type</replaceable> + [ COLLATE <replaceable>collation</replaceable> ] [ DEFAULT <replaceable>expression</replaceable> ] [ <replaceable class="PARAMETER">constraint</replaceable> [ ... ] ] @@ -84,6 +85,17 @@ CREATE DOMAIN <replaceable class="parameter">name</replaceable> [ AS ] <replacea </varlistentry> <varlistentry> + <term><replaceable>collation</replaceable></term> + <listitem> + <para> + An optional collation for the domain. If no collation is + specified, the database default collation is used (which can + be overridden when the domain is used to define a column). + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><literal>DEFAULT <replaceable>expression</replaceable></literal></term> <listitem> diff --git a/doc/src/sgml/ref/create_extension.sgml b/doc/src/sgml/ref/create_extension.sgml new file mode 100644 index 0000000000..d3b5fb009b --- /dev/null +++ b/doc/src/sgml/ref/create_extension.sgml @@ -0,0 +1,168 @@ +<!-- +doc/src/sgml/ref/create_extension.sgml +PostgreSQL documentation +--> + +<refentry id="SQL-CREATEEXTENSION"> + <refmeta> + <refentrytitle>CREATE EXTENSION</refentrytitle> + <manvolnum>7</manvolnum> + <refmiscinfo>SQL - Language Statements</refmiscinfo> + </refmeta> + + <refnamediv> + <refname>CREATE EXTENSION</refname> + <refpurpose>install an extension</refpurpose> + </refnamediv> + + <indexterm zone="sql-createextension"> + <primary>CREATE EXTENSION</primary> + </indexterm> + + <refsynopsisdiv> +<synopsis> +CREATE EXTENSION <replaceable class="parameter">extension_name</replaceable> + [ WITH ] [ SCHEMA <replaceable class="parameter">schema</replaceable> ] + [ VERSION <replaceable class="parameter">version</replaceable> ] + [ FROM <replaceable class="parameter">old_version</replaceable> ] +</synopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para> + <command>CREATE EXTENSION</command> loads a new extension into the current + database. There must not be an extension of the same name already loaded. + </para> + + <para> + Loading an extension essentially amounts to running the extension's script + file. The script will typically create new <acronym>SQL</> objects such as + functions, data types, operators and index support methods. + <command>CREATE EXTENSION</command> additionally records the identities + of all the created objects, so that they can be dropped again if + <command>DROP EXTENSION</command> is issued. + </para> + + <para> + For information about writing new extensions, see + <xref linkend="extend-extensions">. + </para> + + <para> + Only superusers can execute <command>CREATE EXTENSION</command>. + </para> + + </refsect1> + + <refsect1> + <title>Parameters</title> + + <variablelist> + <varlistentry> + <term><replaceable class="parameter">extension_name</replaceable></term> + <listitem> + <para> + The name of the extension to be + installed. <productname>PostgreSQL</productname> will create the + extension using details from the file + <literal>SHAREDIR/extension/</literal><replaceable class="parameter">extension_name</replaceable><literal>.control</literal>. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable class="parameter">schema</replaceable></term> + <listitem> + <para> + The name of the schema in which to install the extension's + objects, given that the extension allows its contents to be + relocated. The named schema must already exist. + If not specified, and the extension's control file does not specify a + schema either, the current default object creation schema is used. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable class="parameter">version</replaceable></term> + <listitem> + <para> + The version of the extension to install. This can be written as + either an identifier or a string literal. The default version is + whatever is specified in the extension's control file. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable class="parameter">old_version</replaceable></term> + <listitem> + <para> + <literal>FROM</> <replaceable class="parameter">old_version</> + must be specified when, and only when, you are attempting to install + an extension that replaces an <quote>old style</> module that is just + a collection of objects not packaged into an extension. This option + causes <command>CREATE EXTENSION</> to run an alternative installation + script that absorbs the existing objects into the extension, instead + of creating new objects. Be careful that <literal>SCHEMA</> specifies + the schema containing these pre-existing objects. + </para> + + <para> + The value to use for <replaceable + class="parameter">old_version</replaceable> is determined by the + extension's author, and might vary if there is more than one version + of the old-style module that can be upgraded into an extension. + For the standard additional modules supplied with pre-9.1 + <productname>PostgreSQL</productname>, use <literal>unpackaged</> + for <replaceable class="parameter">old_version</replaceable> when + updating a module to extension style. + </para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1> + <title>Examples</title> + + <para> + Install the <link linkend="hstore">hstore</link> extension into the + current database: +<programlisting> +CREATE EXTENSION hstore; +</programlisting> + </para> + + <para> + Update a pre-9.1 installation of <literal>hstore</> into + extension style: +<programlisting> +CREATE EXTENSION hstore SCHEMA public FROM unpackaged; +</programlisting> + Be careful to specify the schema in which you installed the existing + <literal>hstore</> objects. + </para> + </refsect1> + + <refsect1> + <title>Compatibility</title> + + <para> + <command>CREATE EXTENSION</command> is a <productname>PostgreSQL</> + extension. + </para> + </refsect1> + + <refsect1> + <title>See Also</title> + + <simplelist type="inline"> + <member><xref linkend="sql-alterextension"></member> + <member><xref linkend="sql-dropextension"></member> + </simplelist> + </refsect1> + +</refentry> diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml index 45c298e371..8ec7abbbd4 100644 --- a/doc/src/sgml/ref/create_index.sgml +++ b/doc/src/sgml/ref/create_index.sgml @@ -22,7 +22,7 @@ PostgreSQL documentation <refsynopsisdiv> <synopsis> CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ <replaceable class="parameter">name</replaceable> ] ON <replaceable class="parameter">table</replaceable> [ USING <replaceable class="parameter">method</replaceable> ] - ( { <replaceable class="parameter">column</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ <replaceable class="parameter">opclass</replaceable> ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] ) + ( { <replaceable class="parameter">column</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ COLLATE <replaceable class="parameter">collation</replaceable> ] [ <replaceable class="parameter">opclass</replaceable> ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] ) [ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> = <replaceable class="PARAMETER">value</replaceable> [, ... ] ) ] [ TABLESPACE <replaceable class="parameter">tablespace</replaceable> ] [ WHERE <replaceable class="parameter">predicate</replaceable> ] @@ -182,6 +182,20 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ <replaceable class="parameter">name</ </varlistentry> <varlistentry> + <term><replaceable class="parameter">collation</replaceable></term> + <listitem> + <para> + The name of the collation to use for the index. By default, + the index uses the collation declared for the column to be + indexed or the result collation of the expression to be + indexed. Indexes with nondefault collations are + available for use by queries that involve expressions using + nondefault collations. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="parameter">opclass</replaceable></term> <listitem> <para> diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml index 5bffe30336..9d2d99ad2e 100644 --- a/doc/src/sgml/ref/create_table.sgml +++ b/doc/src/sgml/ref/create_table.sgml @@ -22,7 +22,7 @@ PostgreSQL documentation <refsynopsisdiv> <synopsis> CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name</replaceable> ( [ - { <replaceable class="PARAMETER">column_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [ <replaceable class="PARAMETER">column_constraint</replaceable> [ ... ] ] + { <replaceable class="PARAMETER">column_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [ COLLATE <replaceable>collation</replaceable> ] [ <replaceable class="PARAMETER">column_constraint</replaceable> [ ... ] ] | <replaceable>table_constraint</replaceable> | LIKE <replaceable>parent_table</replaceable> [ <replaceable>like_option</replaceable> ... ] } [, ... ] @@ -245,6 +245,17 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI </varlistentry> <varlistentry> + <term><literal>COLLATE <replaceable>collation</replaceable></literal></term> + <listitem> + <para> + The <literal>COLLATE</> clause assigns a nondefault collation to + the column. By default, the locale settings of the database are + used. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><literal>INHERITS ( <replaceable>parent_table</replaceable> [, ... ] )</literal></term> <listitem> <para> diff --git a/doc/src/sgml/ref/create_type.sgml b/doc/src/sgml/ref/create_type.sgml index a3c75b51d0..805fc2cf76 100644 --- a/doc/src/sgml/ref/create_type.sgml +++ b/doc/src/sgml/ref/create_type.sgml @@ -45,6 +45,7 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> ( [ , DEFAULT = <replaceable class="parameter">default</replaceable> ] [ , ELEMENT = <replaceable class="parameter">element</replaceable> ] [ , DELIMITER = <replaceable class="parameter">delimiter</replaceable> ] + [ , COLLATABLE = <replaceable class="parameter">collatable</replaceable> ] ) CREATE TYPE <replaceable class="parameter">name</replaceable> @@ -352,6 +353,16 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> with the array element type, not the array type itself. </para> + <para> + If the optional + parameter <replaceable class="parameter">collatable</replaceable> + is true, column definitions and expressions of the type may carry + collation information and allow the use of + the <literal>COLLATE</literal> clause. It is up to the + implementations of the functions operating on the type to actually + make use of the collation information; this does not happen + automatically merely by marking the type collatable. + </para> </refsect2> <refsect2> diff --git a/doc/src/sgml/ref/drop_collation.sgml b/doc/src/sgml/ref/drop_collation.sgml new file mode 100644 index 0000000000..7be9317932 --- /dev/null +++ b/doc/src/sgml/ref/drop_collation.sgml @@ -0,0 +1,110 @@ +<!-- doc/src/sgml/ref/drop_collation.sgml --> + +<refentry id="SQL-DROPCOLLATION"> + <refmeta> + <refentrytitle>DROP COLLATION</refentrytitle> + <manvolnum>7</manvolnum> + <refmiscinfo>SQL - Language Statements</refmiscinfo> + </refmeta> + + <refnamediv> + <refname>DROP COLLATION</refname> + <refpurpose>remove a collation</refpurpose> + </refnamediv> + + <indexterm zone="sql-dropcollation"> + <primary>DROP COLLATION</primary> + </indexterm> + + <refsynopsisdiv> +<synopsis> +DROP COLLATION [ IF EXISTS ] <replaceable>name</replaceable> [ CASCADE | RESTRICT ] +</synopsis> + </refsynopsisdiv> + + <refsect1 id="sql-dropcollation-description"> + <title>Description</title> + + <para> + <command>DROP COLLATION</command> removes a previously defined collation. + To be able to drop a collation, you must own the collation. + </para> + </refsect1> + + <refsect1> + <title>Parameters</title> + + <variablelist> + <varlistentry> + <term><literal>IF EXISTS</literal></term> + <listitem> + <para> + Do not throw an error if the collation does not exist. + A notice is issued in this case. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable>name</replaceable></term> + + <listitem> + <para> + The name of the collation. The collation name can be + schema-qualified. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>CASCADE</literal></term> + <listitem> + <para> + Automatically drop objects that depend on the collation. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>RESTRICT</literal></term> + <listitem> + <para> + Refuse to drop the collation if any objects depend on it. This + is the default. + </para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1 id="sql-dropcollation-examples"> + <title>Examples</title> + + <para> + To drop the collation named <literal>german</>: +<programlisting> +DROP COLLATION german; +</programlisting> + </para> + </refsect1> + + <refsect1 id="sql-dropcollation-compat"> + <title>Compatibility</title> + + <para> + The <command>DROP COLLATION</command> command conforms to the + <acronym>SQL</acronym> standard, apart from the <literal>IF + EXISTS</> option, which is a <productname>PostgreSQL</> extension.. + </para> + </refsect1> + + <refsect1> + <title>See Also</title> + + <simplelist type="inline"> + <member><xref linkend="sql-altercollation"></member> + <member><xref linkend="sql-createcollation"></member> + </simplelist> + </refsect1> + +</refentry> diff --git a/doc/src/sgml/ref/drop_extension.sgml b/doc/src/sgml/ref/drop_extension.sgml new file mode 100644 index 0000000000..1e09ec4c7a --- /dev/null +++ b/doc/src/sgml/ref/drop_extension.sgml @@ -0,0 +1,121 @@ +<!-- +doc/src/sgml/ref/drop_extension.sgml +PostgreSQL documentation +--> + +<refentry id="SQL-DROPEXTENSION"> + <refmeta> + <refentrytitle>DROP EXTENSION</refentrytitle> + <manvolnum>7</manvolnum> + <refmiscinfo>SQL - Language Statements</refmiscinfo> + </refmeta> + + <refnamediv> + <refname>DROP EXTENSION</refname> + <refpurpose>remove an extension</refpurpose> + </refnamediv> + + <indexterm zone="sql-dropextension"> + <primary>DROP EXTENSION</primary> + </indexterm> + + <refsynopsisdiv> +<synopsis> +DROP EXTENSION [ IF EXISTS ] <replaceable class="PARAMETER">extension_name</replaceable> [, ...] [ CASCADE | RESTRICT ] +</synopsis> + </refsynopsisdiv> + + <refsect1> + <title>Description</title> + + <para> + <command>DROP EXTENSION</command> removes extensions from the database. + Dropping an extension causes its component objects to be dropped as well. + </para> + + <para> + An extension can only be dropped by a superuser. + </para> + </refsect1> + + <refsect1> + <title>Parameters</title> + + <variablelist> + + <varlistentry> + <term><literal>IF EXISTS</literal></term> + <listitem> + <para> + Do not throw an error if the extension does not exist. A notice is issued + in this case. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><replaceable class="PARAMETER">extension_name</replaceable></term> + <listitem> + <para> + The name of an installed extension. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>CASCADE</literal></term> + <listitem> + <para> + Automatically drop objects that depend on the extension. + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>RESTRICT</literal></term> + <listitem> + <para> + Refuse to drop the extension if any objects depend on it (other than + its own member objects and other extensions listed in the same + <command>DROP</> command). This is the default. + </para> + </listitem> + </varlistentry> + </variablelist> + </refsect1> + + <refsect1> + <title>Examples</title> + + <para> + To remove the extension <literal>hstore</literal> from the current + database: +<programlisting> +DROP EXTENSION hstore; +</programlisting> + This command will fail if any of <literal>hstore</literal>'s objects + are in use in the database, for example if any tables have columns + of the <type>hstore</> type. Add the <literal>CASCADE</> option to + forcibly remove those dependent objects as well. + </para> + </refsect1> + + <refsect1> + <title>Compatibility</title> + + <para> + <command>DROP EXTENSION</command> is a <productname>PostgreSQL</> + extension. + </para> + </refsect1> + + <refsect1> + <title>See Also</title> + + <simplelist type="inline"> + <member><xref linkend="sql-createextension"></member> + <member><xref linkend="sql-alterextension"></member> + </simplelist> + </refsect1> + +</refentry> diff --git a/doc/src/sgml/ref/pg_ctl-ref.sgml b/doc/src/sgml/ref/pg_ctl-ref.sgml index 28f415da24..307f66b8da 100644 --- a/doc/src/sgml/ref/pg_ctl-ref.sgml +++ b/doc/src/sgml/ref/pg_ctl-ref.sgml @@ -77,6 +77,13 @@ PostgreSQL documentation <cmdsynopsis> <command>pg_ctl</command> + <arg choice="plain">promote</arg> + <arg>-s</arg> + <arg>-D <replaceable>datadir</replaceable></arg> + </cmdsynopsis> + + <cmdsynopsis> + <command>pg_ctl</command> <arg choice="plain">reload</arg> <arg>-s</arg> <arg>-D <replaceable>datadir</replaceable></arg> @@ -184,6 +191,12 @@ PostgreSQL documentation </para> <para> + In <option>promote</option> mode, the standby server that is + running in the specified data directory is commanded to exit + recovery and begin read-write operations. + </para> + + <para> <option>reload</option> mode simply sends the <command>postgres</command> process a <systemitem>SIGHUP</> signal, causing it to reread its configuration files diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index eacae71cdc..ff60a72059 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -1265,6 +1265,7 @@ testdb=> </listitem> </varlistentry> + <varlistentry> <term><literal>\dn[S+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term> @@ -1298,6 +1299,24 @@ testdb=> <varlistentry> + <term><literal>\dO[S+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term> + + <listitem> + <para> + Lists collations. + If <replaceable class="parameter">pattern</replaceable> is + specified, only collations whose names match the pattern are + listed. By default, only user-created objects are shown; + supply a pattern or the <literal>S</literal> modifier to + include system objects. If <literal>+</literal> is appended + to the command name, each object is listed with its associated + description, if any. + </para> + </listitem> + </varlistentry> + + + <varlistentry> <term><literal>\dp [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term> <listitem> <para> @@ -1357,7 +1376,6 @@ testdb=> </listitem> </varlistentry> - <varlistentry> <term><literal>\du[+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term> <listitem> @@ -1371,6 +1389,19 @@ testdb=> </listitem> </varlistentry> + <varlistentry> + <term><literal>\dx[+] [ <link linkend="APP-PSQL-patterns"><replaceable class="parameter">pattern</replaceable></link> ]</literal></term> + <listitem> + <para> + Lists installed extensions. + If <replaceable class="parameter">pattern</replaceable> + is specified, only those extensions whose names match the pattern + are listed. + If the form <literal>\dx+</literal> is used, all the objects belonging + to each matching extension are listed. + </para> + </listitem> + </varlistentry> <varlistentry> <term><literal>\edit</> (or <literal>\e</>) <literal> <optional> <replaceable class="parameter">filename</> </optional> <optional> <replaceable class="parameter">line_number</> </optional> </literal></term> diff --git a/doc/src/sgml/reference.sgml b/doc/src/sgml/reference.sgml index 6ee8e5bcff..9ae80005cd 100644 --- a/doc/src/sgml/reference.sgml +++ b/doc/src/sgml/reference.sgml @@ -35,10 +35,12 @@ &abort; &alterAggregate; + &alterCollation; &alterConversion; &alterDatabase; &alterDefaultPrivileges; &alterDomain; + &alterExtension; &alterForeignDataWrapper; &alterForeignTable; &alterFunction; @@ -75,9 +77,11 @@ ©Table; &createAggregate; &createCast; + &createCollation; &createConversion; &createDatabase; &createDomain; + &createExtension; &createForeignDataWrapper; &createForeignTable; &createFunction; @@ -111,9 +115,11 @@ &do; &dropAggregate; &dropCast; + &dropCollation; &dropConversion; &dropDatabase; &dropDomain; + &dropExtension; &dropForeignDataWrapper; &dropForeignTable; &dropFunction; diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml index 61a87ff74d..6f5e2b1ab8 100644 --- a/doc/src/sgml/regress.sgml +++ b/doc/src/sgml/regress.sgml @@ -236,6 +236,27 @@ gmake check LANG=C MULTIBYTE=EUC_JP existing installation. </para> </sect2> + + <sect2> + <title>Extra tests</title> + + <para> + The regression test suite contains a few test files that are not + run by default, because they might be platform-dependent or take a + very long time to run. You can run these or other extra test + files by setting the variable <envar>EXTRA_TESTS</envar>. For + example, to run the <literal>numeric_big</literal> test: +<screen> +gmake check EXTRA_TESTS=numeric_big +</screen> + To run the collation tests: +<screen> +gmake check EXTRA_TESTS=collate.linux.utf8 LANG=en_US.utf8 +</screen> + This test works only on Linux/glibc platforms and when run in a + UTF-8 locale. + </para> + </sect2> </sect1> <sect1 id="regress-evaluation"> diff --git a/doc/src/sgml/release-9.0.sgml b/doc/src/sgml/release-9.0.sgml index 2288f1b0e6..f33ceea226 100644 --- a/doc/src/sgml/release-9.0.sgml +++ b/doc/src/sgml/release-9.0.sgml @@ -2342,7 +2342,8 @@ whether hex or traditional format is used for <type>bytea</> output. Libpq's <function>PQescapeByteaConn()</> function automatically uses the hex format when connected to <productname>PostgreSQL</> 9.0 - or newer servers. + or newer servers. However, pre-9.0 libpq versions will not + correctly process hex format from newer servers. </para> <para> @@ -3520,9 +3521,8 @@ if TG_OP = 'INSERT' and NEW.col1 = ... then <listitem> <para> - Add data and documentation installation location control to <link - linkend="xfunc-c-pgxs"><acronym>PGXS</></link> Makefiles - (Mark Cave-Ayland) + Add data and documentation installation location control to + <acronym>PGXS</> Makefiles (Mark Cave-Ayland) </para> </listitem> diff --git a/doc/src/sgml/syntax.sgml b/doc/src/sgml/syntax.sgml index 004205f126..00f665ae54 100644 --- a/doc/src/sgml/syntax.sgml +++ b/doc/src/sgml/syntax.sgml @@ -1899,6 +1899,54 @@ CAST ( <replaceable>expression</replaceable> AS <replaceable>type</replaceable> </note> </sect2> + <sect2 id="sql-syntax-collate-clause"> + <title>COLLATE Clause</title> + + <indexterm> + <primary>COLLATE</primary> + </indexterm> + + <para> + The <literal>COLLATE</literal> clause overrides the collation of + an expression. It is appended to the expression it applies to: +<synopsis> +<replaceable>expr</replaceable> COLLATE <replaceable>collation</replaceable> +</synopsis> + where <replaceable>collation</replaceable> is a possibly + schema-qualified identifier. The <literal>COLLATE</literal> + clause binds tighter than operators; parentheses can be used when + necessary. + </para> + + <para> + If no collation is explicitly specified, the database system + either derives a collation from the columns involved in the + expression, or it defaults to the default collation of the + database if no column is involved in the expression. + </para> + + <para> + The two typical uses of the <literal>COLLATE</literal> clause are + overriding the sort order in an <literal>ORDER BY</> clause, for + example: +<programlisting> +SELECT a, b, c FROM tbl WHERE ... ORDER BY a COLLATE "C"; +</programlisting> + and overriding the collation of a function or operator call that + has locale-sensitive results, for example: +<programlisting> +SELECT * FROM tbl WHERE a > 'foo' COLLATE "C"; +</programlisting> + In the latter case it doesn't matter which argument of the + operator of function call the <literal>COLLATE</> clause is + attached to, because the collation that is applied by the operator + or function is derived from all arguments, and + the <literal>COLLATE</> clause will override the collations of all + other arguments. Attaching nonmatching <literal>COLLATE</> + clauses to more than one argument, however, is an error. + </para> + </sect2> + <sect2 id="sql-syntax-scalar-subqueries"> <title>Scalar Subqueries</title> diff --git a/doc/src/sgml/tablefunc.sgml b/doc/src/sgml/tablefunc.sgml index 65917f7d30..dfb932f501 100644 --- a/doc/src/sgml/tablefunc.sgml +++ b/doc/src/sgml/tablefunc.sgml @@ -345,7 +345,9 @@ FROM crosstab3( <listitem> <para> Create a composite type describing the desired output columns, - similar to the examples in the installation script. Then define a + similar to the examples in + <filename>contrib/tablefunc/tablefunc--1.0.sql</>. + Then define a unique function name accepting one <type>text</> parameter and returning <type>setof your_type_name</>, but linking to the same underlying <function>crosstab</> C function. For example, if your source data diff --git a/doc/src/sgml/test-parser.sgml b/doc/src/sgml/test-parser.sgml index 0c53a3a413..0f2008cbe6 100644 --- a/doc/src/sgml/test-parser.sgml +++ b/doc/src/sgml/test-parser.sgml @@ -35,8 +35,8 @@ mydb=# SELECT * FROM ts_token_type('testparser'); <title>Usage</title> <para> - Running the installation script creates a text search parser - <literal>testparser</>. It has no user-configurable parameters. + Installing the <literal>test_parser</> extension creates a text search + parser <literal>testparser</>. It has no user-configurable parameters. </para> <para> diff --git a/doc/src/sgml/tsearch2.sgml b/doc/src/sgml/tsearch2.sgml index 1933e2b966..8321c1efb4 100644 --- a/doc/src/sgml/tsearch2.sgml +++ b/doc/src/sgml/tsearch2.sgml @@ -152,7 +152,7 @@ <emphasis>before</> loading the dump data! If your old installation had the <application>tsearch2</> objects in a schema other than <literal>public</>, be sure to adjust the - <literal>tsearch2</literal> installation script so that the replacement + <command>CREATE EXTENSION</> command so that the replacement objects are created in that same schema. </para> </step> diff --git a/doc/src/sgml/unaccent.sgml b/doc/src/sgml/unaccent.sgml index 2ad66f7ee1..9c34c0bbd2 100644 --- a/doc/src/sgml/unaccent.sgml +++ b/doc/src/sgml/unaccent.sgml @@ -73,7 +73,7 @@ <title>Usage</title> <para> - Running the installation script <filename>unaccent.sql</> creates a text + Installing the <literal>unaccent</> extension creates a text search template <literal>unaccent</> and a dictionary <literal>unaccent</> based on it. The <literal>unaccent</> dictionary has the default parameter setting <literal>RULES='unaccent'</>, which makes it immediately diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml index 4ad50ec0cb..4f2c23fab7 100644 --- a/doc/src/sgml/xfunc.sgml +++ b/doc/src/sgml/xfunc.sgml @@ -2392,273 +2392,6 @@ concat_text(PG_FUNCTION_ARGS) &dfunc; - <sect2 id="xfunc-c-pgxs"> - <title>Extension Building Infrastructure</title> - - <indexterm zone="xfunc-c-pgxs"> - <primary>pgxs</primary> - </indexterm> - - <para> - If you are thinking about distributing your - <productname>PostgreSQL</> extension modules, setting up a - portable build system for them can be fairly difficult. Therefore - the <productname>PostgreSQL</> installation provides a build - infrastructure for extensions, called <acronym>PGXS</acronym>, so - that simple extension modules can be built simply against an - already installed server. Note that this infrastructure is not - intended to be a universal build system framework that can be used - to build all software interfacing to <productname>PostgreSQL</>; - it simply automates common build rules for simple server extension - modules. For more complicated packages, you need to write your - own build system. - </para> - - <para> - To use the infrastructure for your extension, you must write a - simple makefile. In that makefile, you need to set some variables - and finally include the global <acronym>PGXS</acronym> makefile. - Here is an example that builds an extension module named - <literal>isbn_issn</literal> consisting of a shared library, an - SQL script, and a documentation text file: -<programlisting> -MODULES = isbn_issn -DATA_built = isbn_issn.sql -DOCS = README.isbn_issn - -PG_CONFIG = pg_config -PGXS := $(shell $(PG_CONFIG) --pgxs) -include $(PGXS) -</programlisting> - The last three lines should always be the same. Earlier in the - file, you assign variables or add custom - <application>make</application> rules. - </para> - - <para> - Set one of these three variables to specify what is built: - - <variablelist> - <varlistentry> - <term><varname>MODULES</varname></term> - <listitem> - <para> - list of shared objects to be built from source files with same - stem (do not include suffix in this list) - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>MODULE_big</varname></term> - <listitem> - <para> - a shared object to build from multiple source files - (list object files in <varname>OBJS</varname>) - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>PROGRAM</varname></term> - <listitem> - <para> - a binary program to build - (list object files in <varname>OBJS</varname>) - </para> - </listitem> - </varlistentry> - </variablelist> - - The following variables can also be set: - - <variablelist> - <varlistentry> - <term><varname>MODULEDIR</varname></term> - <listitem> - <para> - subdirectory into which DATA and DOCS files should be - installed (if not set, default is <literal>contrib</literal>) - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>DATA</varname></term> - <listitem> - <para> - random files to install into <literal><replaceable>prefix</replaceable>/share/$MODULEDIR</literal> - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>DATA_built</varname></term> - <listitem> - <para> - random files to install into - <literal><replaceable>prefix</replaceable>/share/$MODULEDIR</literal>, - which need to be built first - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>DATA_TSEARCH</varname></term> - <listitem> - <para> - random files to install under - <literal><replaceable>prefix</replaceable>/share/tsearch_data</literal> - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>DOCS</varname></term> - <listitem> - <para> - random files to install under - <literal><replaceable>prefix</replaceable>/doc/$MODULEDIR</literal> - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>SCRIPTS</varname></term> - <listitem> - <para> - script files (not binaries) to install into - <literal><replaceable>prefix</replaceable>/bin</literal> - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>SCRIPTS_built</varname></term> - <listitem> - <para> - script files (not binaries) to install into - <literal><replaceable>prefix</replaceable>/bin</literal>, - which need to be built first - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>REGRESS</varname></term> - <listitem> - <para> - list of regression test cases (without suffix), see below - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>EXTRA_CLEAN</varname></term> - <listitem> - <para> - extra files to remove in <literal>make clean</literal> - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>PG_CPPFLAGS</varname></term> - <listitem> - <para> - will be added to <varname>CPPFLAGS</varname> - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>PG_LIBS</varname></term> - <listitem> - <para> - will be added to <varname>PROGRAM</varname> link line - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>SHLIB_LINK</varname></term> - <listitem> - <para> - will be added to <varname>MODULE_big</varname> link line - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term><varname>PG_CONFIG</varname></term> - <listitem> - <para> - path to <application>pg_config</> program for the - <productname>PostgreSQL</productname> installation to build against - (typically just <literal>pg_config</> to use the first one in your - <varname>PATH</>) - </para> - </listitem> - </varlistentry> - </variablelist> - </para> - - <para> - Put this makefile as <literal>Makefile</literal> in the directory - which holds your extension. Then you can do - <literal>make</literal> to compile, and later <literal>make - install</literal> to install your module. By default, the extension is - compiled and installed for the - <productname>PostgreSQL</productname> installation that - corresponds to the first <command>pg_config</command> program - found in your path. You can use a different installation by - setting <varname>PG_CONFIG</varname> to point to its - <command>pg_config</command> program, either within the makefile - or on the <literal>make</literal> command line. - </para> - - <caution> - <para> - Changing <varname>PG_CONFIG</varname> only works when building - against <productname>PostgreSQL</productname> 8.3 or later. - With older releases it does not work to set it to anything except - <literal>pg_config</>; you must alter your <varname>PATH</> - to select the installation to build against. - </para> - </caution> - - <para> - The scripts listed in the <varname>REGRESS</> variable are used for - regression testing of your module, just like <literal>make - installcheck</literal> is used for the main - <productname>PostgreSQL</productname> server. For this to work you need - to have a subdirectory named <literal>sql/</literal> in your extension's - directory, within which you put one file for each group of tests you want - to run. The files should have extension <literal>.sql</literal>, which - should not be included in the <varname>REGRESS</varname> list in the - makefile. For each test there should be a file containing the expected - result in a subdirectory named <literal>expected/</literal>, with extension - <literal>.out</literal>. The tests are run by executing <literal>make - installcheck</literal>, and the resulting output will be compared to the - expected files. The differences will be written to the file - <literal>regression.diffs</literal> in <command>diff -c</command> format. - Note that trying to run a test which is missing the expected file will be - reported as <quote>trouble</quote>, so make sure you have all expected - files. - </para> - - <tip> - <para> - The easiest way of creating the expected files is creating empty files, - then carefully inspecting the result files after a test run (to be found - in the <literal>results/</literal> directory), and copying them to - <literal>expected/</literal> if they match what you want from the test. - </para> - - </tip> - </sect2> - - <sect2> <title>Composite-type Arguments</title> @@ -3385,4 +3118,68 @@ if (!ptr) </programlisting> </para> </sect2> + + <sect2 id="extend-Cpp"> + <title>Using C++ for Extensibility</title> + + <indexterm zone="extend-Cpp"> + <primary>C++</primary> + </indexterm> + + <para> + Although the <productname>PostgreSQL</productname> backend is written in + C, it is possible to write extensions in C++ if these guidelines are + followed: + + <itemizedlist> + <listitem> + <para> + All functions accessed by the backend must present a C interface + to the backend; these C functions can then call C++ functions. + For example, <literal>extern C</> linkage is required for + backend-accessed functions. This is also necessary for any + functions that are passed as pointers between the backend and + C++ code. + </para> + </listitem> + <listitem> + <para> + Free memory using the appropriate deallocation method. For example, + most backend memory is allocated using <function>palloc()</>, so use + <function>pfree()</> to free it. Using C++ + <function>delete</> in such cases will fail. + </para> + </listitem> + <listitem> + <para> + Prevent exceptions from propagating into the C code (use a catch-all + block at the top level of all <literal>extern C</> functions). This + is necessary even if the C++ code does not explicitly throw any + exceptions, because events like out-of-memory can still throw + exceptions. Any exceptions must be caught and appropriate errors + passed back to the C interface. If possible, compile C++ with + <option>-fno-exceptions</> to eliminate exceptions entirely; in such + cases, you must check for failures in your C++ code, e.g. check for + NULL returned by <function>new()</>. + </para> + </listitem> + <listitem> + <para> + If calling backend functions from C++ code, be sure that the + C++ call stack contains only plain old data structures + (<acronym>POD</>). This is necessary because backend errors + generate a distant <function>longjmp()</> that does not properly + unroll a C++ call stack with non-POD objects. + </para> + </listitem> + </itemizedlist> + </para> + + <para> + In summary, it is best to place C++ code behind a wall of + <literal>extern C</> functions that interface to the backend, + and avoid exception, memory, and call stack leakage. + </para> + </sect2> + </sect1> diff --git a/src/Makefile.global.in b/src/Makefile.global.in index d6b7b4769d..2eff4d4067 100644 --- a/src/Makefile.global.in +++ b/src/Makefile.global.in @@ -285,9 +285,6 @@ XGETTEXT = @XGETTEXT@ GZIP = gzip BZIP2 = bzip2 -PL_TESTDB = pl_regression -CONTRIB_TESTDB = contrib_regression - # Installation. INSTALL = $(SHELL) $(top_srcdir)/config/install-sh -c @@ -428,6 +425,19 @@ submake-libpgport: ########################################################################## # +# Testing support + +PL_TESTDB = pl_regression +CONTRIB_TESTDB = contrib_regression + +pg_regress_check = $(top_builddir)/src/test/regress/pg_regress --inputdir=$(srcdir) --temp-install=./tmp_check --top-builddir=$(top_builddir) +pg_regress_installcheck = $(top_builddir)/src/test/regress/pg_regress --inputdir=$(srcdir) --psqldir=$(PSQLDIR) + +pg_regress_clean_files = results/ regression.diffs regression.out tmp_check/ log/ + + +########################################################################## +# # Customization # # This includes your local customizations if Makefile.custom exists diff --git a/src/backend/access/common/scankey.c b/src/backend/access/common/scankey.c index 232322a91e..41cd36fce9 100644 --- a/src/backend/access/common/scankey.c +++ b/src/backend/access/common/scankey.c @@ -103,3 +103,16 @@ ScanKeyEntryInitializeWithInfo(ScanKey entry, entry->sk_argument = argument; fmgr_info_copy(&entry->sk_func, finfo, CurrentMemoryContext); } + +/* + * ScanKeyEntryInitializeCollation + * + * Initialize the collation of a scan key. This is just a notational + * convenience and small abstraction. + */ +void +ScanKeyEntryInitializeCollation(ScanKey entry, + Oid collation) +{ + entry->sk_func.fn_collation = collation; +} diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c index cfc95b9312..0cd1f941e3 100644 --- a/src/backend/access/common/tupdesc.c +++ b/src/backend/access/common/tupdesc.c @@ -488,10 +488,32 @@ TupleDescInitEntry(TupleDesc desc, att->attbyval = typeForm->typbyval; att->attalign = typeForm->typalign; att->attstorage = typeForm->typstorage; + att->attcollation = typeForm->typcollation; ReleaseSysCache(tuple); } +/* + * TupleDescInitEntryCollation + * + * Fill in the collation for an attribute in a previously initialized + * tuple descriptor. + */ +void +TupleDescInitEntryCollation(TupleDesc desc, + AttrNumber attributeNumber, + Oid collationid) +{ + /* + * sanity checks + */ + AssertArg(PointerIsValid(desc)); + AssertArg(attributeNumber >= 1); + AssertArg(attributeNumber <= desc->natts); + + desc->attrs[attributeNumber - 1]->attcollation = collationid; +} + /* * BuildDescForRelation @@ -513,6 +535,7 @@ BuildDescForRelation(List *schema) char *attname; Oid atttypid; int32 atttypmod; + Oid attcollation; int attdim; /* @@ -536,7 +559,7 @@ BuildDescForRelation(List *schema) attnum++; attname = entry->colname; - typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod); + typenameTypeIdModColl(NULL, entry->typeName, &atttypid, &atttypmod, &attcollation); attdim = list_length(entry->typeName->arrayBounds); if (entry->typeName->setof) @@ -547,6 +570,7 @@ BuildDescForRelation(List *schema) TupleDescInitEntry(desc, attnum, attname, atttypid, atttypmod, attdim); + TupleDescInitEntryCollation(desc, attnum, attcollation); /* Override TupleDescInitEntry's settings as requested */ if (entry->storage) @@ -588,18 +612,20 @@ BuildDescForRelation(List *schema) * with functions returning RECORD. */ TupleDesc -BuildDescFromLists(List *names, List *types, List *typmods) +BuildDescFromLists(List *names, List *types, List *typmods, List *collations) { int natts; AttrNumber attnum; ListCell *l1; ListCell *l2; ListCell *l3; + ListCell *l4; TupleDesc desc; natts = list_length(names); Assert(natts == list_length(types)); Assert(natts == list_length(typmods)); + Assert(natts == list_length(collations)); /* * allocate a new tuple descriptor @@ -610,20 +636,25 @@ BuildDescFromLists(List *names, List *types, List *typmods) l2 = list_head(types); l3 = list_head(typmods); + l4 = list_head(collations); foreach(l1, names) { char *attname = strVal(lfirst(l1)); Oid atttypid; int32 atttypmod; + Oid attcollation; atttypid = lfirst_oid(l2); l2 = lnext(l2); atttypmod = lfirst_int(l3); l3 = lnext(l3); + attcollation = lfirst_oid(l4); + l4 = lnext(l4); attnum++; TupleDescInitEntry(desc, attnum, attname, atttypid, atttypmod, 0); + TupleDescInitEntryCollation(desc, attnum, attcollation); } return desc; diff --git a/src/backend/access/gin/ginarrayproc.c b/src/backend/access/gin/ginarrayproc.c index 2100c5fd0e..ce9abae6aa 100644 --- a/src/backend/access/gin/ginarrayproc.c +++ b/src/backend/access/gin/ginarrayproc.c @@ -59,6 +59,20 @@ ginarrayextract(PG_FUNCTION_ARGS) } /* + * Formerly, ginarrayextract had only two arguments. Now it has three, + * but we still need a pg_proc entry with two args to support reloading + * pre-9.1 contrib/intarray opclass declarations. This compatibility + * function should go away eventually. + */ +Datum +ginarrayextract_2args(PG_FUNCTION_ARGS) +{ + if (PG_NARGS() < 3) /* should not happen */ + elog(ERROR, "ginarrayextract requires three arguments"); + return ginarrayextract(fcinfo); +} + +/* * extractQuery support function */ Datum diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c index 0a7c1c521f..35f71c843e 100644 --- a/src/backend/access/gin/ginutil.c +++ b/src/backend/access/gin/ginutil.c @@ -22,6 +22,7 @@ #include "storage/freespace.h" #include "storage/indexfsm.h" #include "storage/lmgr.h" +#include "utils/lsyscache.h" /* @@ -60,6 +61,8 @@ initGinState(GinState *state, Relation index) fmgr_info_copy(&(state->compareFn[i]), index_getprocinfo(index, i + 1, GIN_COMPARE_PROC), CurrentMemoryContext); + fmgr_info_collation(get_typcollation(index->rd_att->attrs[i]->atttypid), + &(state->compareFn[i])); fmgr_info_copy(&(state->extractValueFn[i]), index_getprocinfo(index, i + 1, GIN_EXTRACTVALUE_PROC), CurrentMemoryContext); diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c index 6e0db79517..6e6af18d47 100644 --- a/src/backend/access/index/indexam.c +++ b/src/backend/access/index/indexam.c @@ -872,6 +872,8 @@ index_getprocinfo(Relation irel, procnum, attnum, RelationGetRelationName(irel)); fmgr_info_cxt(procId, locinfo, irel->rd_indexcxt); + fmgr_info_collation(irel->rd_index->indcollation.values[attnum-1], + locinfo); } return locinfo; diff --git a/src/backend/access/nbtree/nbtsearch.c b/src/backend/access/nbtree/nbtsearch.c index cf74f7776c..be8d958352 100644 --- a/src/backend/access/nbtree/nbtsearch.c +++ b/src/backend/access/nbtree/nbtsearch.c @@ -723,6 +723,8 @@ _bt_first(IndexScanDesc scan, ScanDirection dir) cur->sk_subtype, procinfo, cur->sk_argument); + ScanKeyEntryInitializeCollation(scankeys + i, + cur->sk_func.fn_collation); } else { @@ -743,6 +745,8 @@ _bt_first(IndexScanDesc scan, ScanDirection dir) cur->sk_subtype, cmp_proc, cur->sk_argument); + ScanKeyEntryInitializeCollation(scankeys + i, + cur->sk_func.fn_collation); } } } diff --git a/src/backend/access/transam/recovery.conf.sample b/src/backend/access/transam/recovery.conf.sample index f8f6f9b108..0243b51fdf 100644 --- a/src/backend/access/transam/recovery.conf.sample +++ b/src/backend/access/transam/recovery.conf.sample @@ -66,11 +66,14 @@ # If you want to stop rollforward at a specific point, you # must set a recovery target. # -# You may set a recovery target either by transactionId, or -# by timestamp. Recovery may either include or exclude the +# You may set a recovery target either by transactionId, by name, +# or by timestamp. Recovery may either include or exclude the # transaction(s) with the recovery target value (ie, stop either # just after or just before the given target, respectively). # +# +#recovery_target_name = '' # e.g. 'daily backup 2011-01-26' +# #recovery_target_time = '' # e.g. '2004-07-14 22:39:00 EST' # #recovery_target_xid = '' diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 25c7e06234..3ba1f29197 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -62,6 +62,7 @@ /* File path names (all relative to $PGDATA) */ #define RECOVERY_COMMAND_FILE "recovery.conf" #define RECOVERY_COMMAND_DONE "recovery.done" +#define PROMOTE_SIGNAL_FILE "promote" /* User-settable parameters */ @@ -157,6 +158,11 @@ static XLogRecPtr LastRec; * known, need to check the shared state". */ static bool LocalRecoveryInProgress = true; +/* + * Local copy of SharedHotStandbyActive variable. False actually means "not + * known, need to check the shared state". + */ +static bool LocalHotStandbyActive = false; /* * Local state for XLogInsertAllowed(): @@ -182,17 +188,20 @@ static char *recoveryEndCommand = NULL; static char *archiveCleanupCommand = NULL; static RecoveryTargetType recoveryTarget = RECOVERY_TARGET_UNSET; static bool recoveryTargetInclusive = true; +static bool recoveryPauseAtTarget = true; static TransactionId recoveryTargetXid; static TimestampTz recoveryTargetTime; +static char *recoveryTargetName; /* options taken from recovery.conf for XLOG streaming */ static bool StandbyMode = false; static char *PrimaryConnInfo = NULL; static char *TriggerFile = NULL; -/* if recoveryStopsHere returns true, it saves actual stop xid/time here */ +/* if recoveryStopsHere returns true, it saves actual stop xid/time/name here */ static TransactionId recoveryStopXid; static TimestampTz recoveryStopTime; +static char recoveryStopName[MAXFNAMELEN]; static bool recoveryStopAfter; /* @@ -402,6 +411,12 @@ typedef struct XLogCtlData bool SharedRecoveryInProgress; /* + * SharedHotStandbyActive indicates if we're still in crash or archive + * recovery. Protected by info_lck. + */ + bool SharedHotStandbyActive; + + /* * recoveryWakeupLatch is used to wake up the startup process to * continue WAL replay, if it is waiting for WAL to arrive or failover * trigger file to appear. @@ -423,6 +438,8 @@ typedef struct XLogCtlData XLogRecPtr recoveryLastRecPtr; /* timestamp of last COMMIT/ABORT record replayed (or being replayed) */ TimestampTz recoveryLastXTime; + /* Are we requested to pause recovery? */ + bool recoveryPause; slock_t info_lck; /* locks shared variables shown above */ } XLogCtlData; @@ -548,11 +565,19 @@ typedef struct xl_parameter_change int wal_level; } xl_parameter_change; +/* logs restore point */ +typedef struct xl_restore_point +{ + TimestampTz rp_time; + char rp_name[MAXFNAMELEN]; +} xl_restore_point; + /* * Flags set by interrupt handlers for later service in the redo loop. */ static volatile sig_atomic_t got_SIGHUP = false; static volatile sig_atomic_t shutdown_requested = false; +static volatile sig_atomic_t promote_triggered = false; /* * Flag set when executing a restore command, to tell SIGTERM signal handler @@ -570,6 +595,9 @@ static void readRecoveryCommandFile(void); static void exitArchiveRecovery(TimeLineID endTLI, uint32 endLogId, uint32 endLogSeg); static bool recoveryStopsHere(XLogRecord *record, bool *includeThis); +static void recoveryPausesHere(void); +static bool RecoveryIsPaused(void); +static void SetRecoveryPause(bool recoveryPause); static void SetLatestXTime(TimestampTz xtime); static TimestampTz GetLatestXTime(void); static void CheckRequiredParameterValues(void); @@ -4385,6 +4413,13 @@ writeTimeLineHistory(TimeLineID newTLI, TimeLineID parentTLI, xlogfname, recoveryStopAfter ? "after" : "before", timestamptz_to_str(recoveryStopTime)); + else if (recoveryTarget == RECOVERY_TARGET_NAME) + snprintf(buffer, sizeof(buffer), + "%s%u\t%s\tat restore point \"%s\"\n", + (srcfd < 0) ? "" : "\n", + parentTLI, + xlogfname, + recoveryStopName); else snprintf(buffer, sizeof(buffer), "%s%u\t%s\tno recovery target specified\n", @@ -4893,6 +4928,7 @@ XLOGShmemInit(void) */ XLogCtl->XLogCacheBlck = XLOGbuffers - 1; XLogCtl->SharedRecoveryInProgress = true; + XLogCtl->SharedHotStandbyActive = false; XLogCtl->Insert.currpage = (XLogPageHeader) (XLogCtl->pages); SpinLockInit(&XLogCtl->info_lck); InitSharedLatch(&XLogCtl->recoveryWakeupLatch); @@ -5126,6 +5162,15 @@ readRecoveryCommandFile(void) (errmsg("archive_cleanup_command = '%s'", archiveCleanupCommand))); } + else if (strcmp(item->name, "pause_at_recovery_target") == 0) + { + if (!parse_bool(item->value, &recoveryPauseAtTarget)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("parameter \"%s\" requires a Boolean value", "pause_at_recovery_target"))); + ereport(DEBUG2, + (errmsg("pause_at_recovery_target = '%s'", item->value))); + } else if (strcmp(item->name, "recovery_target_timeline") == 0) { rtliGiven = true; @@ -5163,10 +5208,11 @@ readRecoveryCommandFile(void) else if (strcmp(item->name, "recovery_target_time") == 0) { /* - * if recovery_target_xid specified, then this overrides - * recovery_target_time + * if recovery_target_xid or recovery_target_name specified, then + * this overrides recovery_target_time */ - if (recoveryTarget == RECOVERY_TARGET_XID) + if (recoveryTarget == RECOVERY_TARGET_XID || + recoveryTarget == RECOVERY_TARGET_NAME) continue; recoveryTarget = RECOVERY_TARGET_TIME; @@ -5182,6 +5228,26 @@ readRecoveryCommandFile(void) (errmsg("recovery_target_time = '%s'", timestamptz_to_str(recoveryTargetTime)))); } + else if (strcmp(item->name, "recovery_target_name") == 0) + { + /* + * if recovery_target_xid specified, then this overrides + * recovery_target_name + */ + if (recoveryTarget == RECOVERY_TARGET_XID) + continue; + recoveryTarget = RECOVERY_TARGET_NAME; + + recoveryTargetName = pstrdup(item->value); + if (strlen(recoveryTargetName) >= MAXFNAMELEN) + ereport(FATAL, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("recovery_target_name is too long (maximum %d characters)", MAXFNAMELEN - 1))); + + ereport(DEBUG2, + (errmsg("recovery_target_name = '%s'", + recoveryTargetName))); + } else if (strcmp(item->name, "recovery_target_inclusive") == 0) { /* @@ -5396,8 +5462,8 @@ exitArchiveRecovery(TimeLineID endTLI, uint32 endLogId, uint32 endLogSeg) * Returns TRUE if we are stopping, FALSE otherwise. On TRUE return, * *includeThis is set TRUE if we should apply this record before stopping. * - * We also track the timestamp of the latest applied COMMIT/ABORT record - * in XLogCtl->recoveryLastXTime, for logging purposes. + * We also track the timestamp of the latest applied COMMIT/ABORT + * record in XLogCtl->recoveryLastXTime, for logging purposes. * Also, some information is saved in recoveryStopXid et al for use in * annotating the new timeline's history file. */ @@ -5407,9 +5473,10 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis) bool stopsHere; uint8 record_info; TimestampTz recordXtime; + char recordRPName[MAXFNAMELEN]; - /* We only consider stopping at COMMIT or ABORT records */ - if (record->xl_rmid != RM_XACT_ID) + /* We only consider stopping at COMMIT, ABORT or RESTORE POINT records */ + if (record->xl_rmid != RM_XACT_ID && record->xl_rmid != RM_XLOG_ID) return false; record_info = record->xl_info & ~XLR_INFO_MASK; if (record_info == XLOG_XACT_COMMIT) @@ -5426,20 +5493,33 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis) recordXactAbortData = (xl_xact_abort *) XLogRecGetData(record); recordXtime = recordXactAbortData->xact_time; } + else if (record_info == XLOG_RESTORE_POINT) + { + xl_restore_point *recordRestorePointData; + + recordRestorePointData = (xl_restore_point *) XLogRecGetData(record); + recordXtime = recordRestorePointData->rp_time; + strncpy(recordRPName, recordRestorePointData->rp_name, MAXFNAMELEN); + } else return false; /* Do we have a PITR target at all? */ if (recoveryTarget == RECOVERY_TARGET_UNSET) { - SetLatestXTime(recordXtime); + /* + * Save timestamp of latest transaction commit/abort if this is + * a transaction record + */ + if (record->xl_rmid == RM_XACT_ID) + SetLatestXTime(recordXtime); return false; } if (recoveryTarget == RECOVERY_TARGET_XID) { /* - * there can be only one transaction end record with this exact + * There can be only one transaction end record with this exact * transactionid * * when testing for an xid, we MUST test for equality only, since @@ -5451,10 +5531,24 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis) if (stopsHere) *includeThis = recoveryTargetInclusive; } + else if (recoveryTarget == RECOVERY_TARGET_NAME) + { + /* + * There can be many restore points that share the same name, so we stop + * at the first one + */ + stopsHere = (strcmp(recordRPName, recoveryTargetName) == 0); + + /* + * Ignore recoveryTargetInclusive because this is not a transaction + * record + */ + *includeThis = false; + } else { /* - * there can be many transactions that share the same commit time, so + * There can be many transactions that share the same commit time, so * we stop after the last one, if we are inclusive, or stop at the * first one if we are exclusive */ @@ -5485,7 +5579,7 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis) recoveryStopXid, timestamptz_to_str(recoveryStopTime)))); } - else + else if (record_info == XLOG_XACT_ABORT) { if (recoveryStopAfter) ereport(LOG, @@ -5498,17 +5592,135 @@ recoveryStopsHere(XLogRecord *record, bool *includeThis) recoveryStopXid, timestamptz_to_str(recoveryStopTime)))); } + else + { + strncpy(recoveryStopName, recordRPName, MAXFNAMELEN); - if (recoveryStopAfter) + ereport(LOG, + (errmsg("recovery stopping at restore point \"%s\", time %s", + recoveryStopName, + timestamptz_to_str(recoveryStopTime)))); + } + + /* + * Note that if we use a RECOVERY_TARGET_TIME then we can stop + * at a restore point since they are timestamped, though the latest + * transaction time is not updated. + */ + if (record->xl_rmid == RM_XACT_ID && recoveryStopAfter) SetLatestXTime(recordXtime); } - else + else if (record->xl_rmid == RM_XACT_ID) SetLatestXTime(recordXtime); return stopsHere; } /* + * Recheck shared recoveryPause by polling. + * + * XXX Can also be done with shared latch. + */ +static void +recoveryPausesHere(void) +{ + while (RecoveryIsPaused()); + { + pg_usleep(1000000L); /* 1000 ms */ + HandleStartupProcInterrupts(); + }; +} + +static bool +RecoveryIsPaused(void) +{ + /* use volatile pointer to prevent code rearrangement */ + volatile XLogCtlData *xlogctl = XLogCtl; + bool recoveryPause; + + SpinLockAcquire(&xlogctl->info_lck); + recoveryPause = xlogctl->recoveryPause; + SpinLockRelease(&xlogctl->info_lck); + + return recoveryPause; +} + +static void +SetRecoveryPause(bool recoveryPause) +{ + /* use volatile pointer to prevent code rearrangement */ + volatile XLogCtlData *xlogctl = XLogCtl; + + SpinLockAcquire(&xlogctl->info_lck); + xlogctl->recoveryPause = recoveryPause; + SpinLockRelease(&xlogctl->info_lck); +} + +/* + * pg_xlog_replay_pause - pause recovery now + */ +Datum +pg_xlog_replay_pause(PG_FUNCTION_ARGS) +{ + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to control recovery")))); + + if (!RecoveryInProgress()) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("recovery is not in progress"), + errhint("Recovery control functions can only be executed during recovery."))); + + SetRecoveryPause(true); + + PG_RETURN_VOID(); +} + +/* + * pg_xlog_replay_resume - resume recovery now + */ +Datum +pg_xlog_replay_resume(PG_FUNCTION_ARGS) +{ + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to control recovery")))); + + if (!RecoveryInProgress()) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("recovery is not in progress"), + errhint("Recovery control functions can only be executed during recovery."))); + + SetRecoveryPause(false); + + PG_RETURN_VOID(); +} + +/* + * pg_is_xlog_replay_paused + */ +Datum +pg_is_xlog_replay_paused(PG_FUNCTION_ARGS) +{ + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to control recovery")))); + + if (!RecoveryInProgress()) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("recovery is not in progress"), + errhint("Recovery control functions can only be executed during recovery."))); + + PG_RETURN_BOOL(RecoveryIsPaused()); +} + +/* * Save timestamp of latest processed commit/abort record. * * We keep this in XLogCtl, not a simple static variable, so that it can be @@ -5781,6 +5993,10 @@ StartupXLOG(void) ereport(LOG, (errmsg("starting point-in-time recovery to %s", timestamptz_to_str(recoveryTargetTime)))); + else if (recoveryTarget == RECOVERY_TARGET_NAME) + ereport(LOG, + (errmsg("starting point-in-time recovery to \"%s\"", + recoveryTargetName))); else ereport(LOG, (errmsg("starting archive recovery"))); @@ -6074,6 +6290,13 @@ StartupXLOG(void) StandbyRecoverPreparedTransactions(false); } } + else + { + /* + * Must not pause unless we are going to enter Hot Standby. + */ + recoveryPauseAtTarget = false; + } /* Initialize resource managers */ for (rmid = 0; rmid <= RM_MAX_ID; rmid++) @@ -6098,6 +6321,7 @@ StartupXLOG(void) xlogctl->replayEndRecPtr = ReadRecPtr; xlogctl->recoveryLastRecPtr = ReadRecPtr; xlogctl->recoveryLastXTime = 0; + xlogctl->recoveryPause = false; SpinLockRelease(&xlogctl->info_lck); /* Also ensure XLogReceiptTime has a sane value */ @@ -6146,6 +6370,7 @@ StartupXLOG(void) { bool recoveryContinue = true; bool recoveryApply = true; + bool recoveryPause = false; ErrorContextCallback errcontext; TimestampTz xtime; @@ -6192,6 +6417,11 @@ StartupXLOG(void) */ if (recoveryStopsHere(record, &recoveryApply)) { + if (recoveryPauseAtTarget) + { + SetRecoveryPause(true); + recoveryPausesHere(); + } reachedStopPoint = true; /* see below */ recoveryContinue = false; if (!recoveryApply) @@ -6218,8 +6448,12 @@ StartupXLOG(void) */ SpinLockAcquire(&xlogctl->info_lck); xlogctl->replayEndRecPtr = EndRecPtr; + recoveryPause = xlogctl->recoveryPause; SpinLockRelease(&xlogctl->info_lck); + if (recoveryPause) + recoveryPausesHere(); + /* * If we are attempting to enter Hot Standby mode, process * XIDs we see @@ -6568,8 +6802,6 @@ StartupXLOG(void) static void CheckRecoveryConsistency(void) { - static bool backendsAllowed = false; - /* * Have we passed our safe starting point? */ @@ -6589,11 +6821,19 @@ CheckRecoveryConsistency(void) * enabling connections. */ if (standbyState == STANDBY_SNAPSHOT_READY && - !backendsAllowed && + !LocalHotStandbyActive && reachedMinRecoveryPoint && IsUnderPostmaster) { - backendsAllowed = true; + /* use volatile pointer to prevent code rearrangement */ + volatile XLogCtlData *xlogctl = XLogCtl; + + SpinLockAcquire(&xlogctl->info_lck); + xlogctl->SharedHotStandbyActive = true; + SpinLockRelease(&xlogctl->info_lck); + + LocalHotStandbyActive = true; + SendPostmasterSignal(PMSIGNAL_BEGIN_HOT_STANDBY); } } @@ -6641,6 +6881,38 @@ RecoveryInProgress(void) } /* + * Is HotStandby active yet? This is only important in special backends + * since normal backends won't ever be able to connect until this returns + * true. Postmaster knows this by way of signal, not via shared memory. + * + * Unlike testing standbyState, this works in any process that's connected to + * shared memory. + */ +bool +HotStandbyActive(void) +{ + /* + * We check shared state each time only until Hot Standby is active. We + * can't de-activate Hot Standby, so there's no need to keep checking after + * the shared variable has once been seen true. + */ + if (LocalHotStandbyActive) + return true; + else + { + /* use volatile pointer to prevent code rearrangement */ + volatile XLogCtlData *xlogctl = XLogCtl; + + /* spinlock is essential on machines with weak memory ordering! */ + SpinLockAcquire(&xlogctl->info_lck); + LocalHotStandbyActive = xlogctl->SharedHotStandbyActive; + SpinLockRelease(&xlogctl->info_lck); + + return LocalHotStandbyActive; + } +} + +/* * Is this process allowed to insert new WAL records? * * Ordinarily this is essentially equivalent to !RecoveryInProgress(). @@ -7853,6 +8125,29 @@ RequestXLogSwitch(void) } /* + * Write a RESTORE POINT record + */ +XLogRecPtr +XLogRestorePoint(const char *rpName) +{ + XLogRecPtr RecPtr; + XLogRecData rdata; + xl_restore_point xlrec; + + xlrec.rp_time = GetCurrentTimestamp(); + strncpy(xlrec.rp_name, rpName, MAXFNAMELEN); + + rdata.buffer = InvalidBuffer; + rdata.data = (char *) &xlrec; + rdata.len = sizeof(xl_restore_point); + rdata.next = NULL; + + RecPtr = XLogInsert(RM_XLOG_ID, XLOG_RESTORE_POINT, &rdata); + + return RecPtr; +} + +/* * Check if any of the GUC parameters that are critical for hot standby * have changed, and update the value in pg_control file if necessary. */ @@ -8044,6 +8339,10 @@ xlog_redo(XLogRecPtr lsn, XLogRecord *record) { /* nothing to do here */ } + else if (info == XLOG_RESTORE_POINT) + { + /* nothing to do here */ + } else if (info == XLOG_BACKUP_END) { XLogRecPtr startpoint; @@ -8146,6 +8445,13 @@ xlog_desc(StringInfo buf, uint8 xl_info, char *rec) { appendStringInfo(buf, "xlog switch"); } + else if (info == XLOG_RESTORE_POINT) + { + xl_restore_point *xlrec = (xl_restore_point *) rec; + + appendStringInfo(buf, "restore point: %s", xlrec->rp_name); + + } else if (info == XLOG_BACKUP_END) { XLogRecPtr startpoint; @@ -8621,7 +8927,7 @@ pg_stop_backup(PG_FUNCTION_ARGS) XLogRecPtr stoppoint; char stopxlogstr[MAXFNAMELEN]; - stoppoint = do_pg_stop_backup(NULL); + stoppoint = do_pg_stop_backup(NULL, true); snprintf(stopxlogstr, sizeof(stopxlogstr), "%X/%X", stoppoint.xlogid, stoppoint.xrecoff); @@ -8636,7 +8942,7 @@ pg_stop_backup(PG_FUNCTION_ARGS) * the non-exclusive backup specified by 'labelfile'. */ XLogRecPtr -do_pg_stop_backup(char *labelfile) +do_pg_stop_backup(char *labelfile, bool waitforarchive) { bool exclusive = (labelfile == NULL); XLogRecPtr startpoint; @@ -8835,7 +9141,7 @@ do_pg_stop_backup(char *labelfile) * wish to wait, you can set statement_timeout. Also, some notices are * issued to clue in anyone who might be doing this interactively. */ - if (XLogArchivingActive()) + if (waitforarchive && XLogArchivingActive()) { XLByteToPrevSeg(stoppoint, _logId, _logSeg); XLogFileName(lastxlogfilename, ThisTimeLineID, _logId, _logSeg); @@ -8876,7 +9182,7 @@ do_pg_stop_backup(char *labelfile) ereport(NOTICE, (errmsg("pg_stop_backup complete, all required WAL segments have been archived"))); } - else + else if (waitforarchive) ereport(NOTICE, (errmsg("WAL archiving is not enabled; you must ensure that all required WAL segments are copied through other means to complete the backup"))); @@ -8944,6 +9250,51 @@ pg_switch_xlog(PG_FUNCTION_ARGS) } /* + * pg_create_restore_point: a named point for restore + */ +Datum +pg_create_restore_point(PG_FUNCTION_ARGS) +{ + text *restore_name = PG_GETARG_TEXT_P(0); + char *restore_name_str; + XLogRecPtr restorepoint; + char location[MAXFNAMELEN]; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to create a restore point")))); + + if (RecoveryInProgress()) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + (errmsg("recovery is in progress"), + errhint("WAL control functions cannot be executed during recovery.")))); + + if (!XLogIsNeeded()) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("WAL level not sufficient for creating a restore point"), + errhint("wal_level must be set to \"archive\" or \"hot_standby\" at server start."))); + + restore_name_str = text_to_cstring(restore_name); + + if (strlen(restore_name_str) >= MAXFNAMELEN) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("value too long for restore point (maximum %d characters)", MAXFNAMELEN - 1))); + + restorepoint = XLogRestorePoint(restore_name_str); + + /* + * As a convenience, return the WAL location of the restore point record + */ + snprintf(location, sizeof(location), "%X/%X", + restorepoint.xlogid, restorepoint.xrecoff); + PG_RETURN_TEXT_P(cstring_to_text(location)); +} + +/* * Report the current WAL write location (same format as pg_start_backup etc) * * This is useful for determining how much of WAL is visible to an external @@ -9029,6 +9380,25 @@ pg_last_xlog_receive_location(PG_FUNCTION_ARGS) } /* + * Get latest redo apply position. + * + * Exported to allow WALReceiver to read the pointer directly. + */ +XLogRecPtr +GetXLogReplayRecPtr(void) +{ + /* use volatile pointer to prevent code rearrangement */ + volatile XLogCtlData *xlogctl = XLogCtl; + XLogRecPtr recptr; + + SpinLockAcquire(&xlogctl->info_lck); + recptr = xlogctl->recoveryLastRecPtr; + SpinLockRelease(&xlogctl->info_lck); + + return recptr; +} + +/* * Report the last WAL replay location (same format as pg_start_backup etc) * * This is useful for determining how much of WAL is visible to read-only @@ -9037,14 +9407,10 @@ pg_last_xlog_receive_location(PG_FUNCTION_ARGS) Datum pg_last_xlog_replay_location(PG_FUNCTION_ARGS) { - /* use volatile pointer to prevent code rearrangement */ - volatile XLogCtlData *xlogctl = XLogCtl; XLogRecPtr recptr; char location[MAXFNAMELEN]; - SpinLockAcquire(&xlogctl->info_lck); - recptr = xlogctl->recoveryLastRecPtr; - SpinLockRelease(&xlogctl->info_lck); + recptr = GetXLogReplayRecPtr(); if (recptr.xlogid == 0 && recptr.xrecoff == 0) PG_RETURN_NULL(); @@ -9355,6 +9721,14 @@ StartupProcSigUsr1Handler(SIGNAL_ARGS) latch_sigusr1_handler(); } +/* SIGUSR2: set flag to finish recovery */ +static void +StartupProcTriggerHandler(SIGNAL_ARGS) +{ + promote_triggered = true; + WakeupRecovery(); +} + /* SIGHUP: set flag to re-read config file at next convenient time */ static void StartupProcSigHupHandler(SIGNAL_ARGS) @@ -9432,7 +9806,7 @@ StartupProcessMain(void) pqsignal(SIGALRM, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN); pqsignal(SIGUSR1, StartupProcSigUsr1Handler); - pqsignal(SIGUSR2, SIG_IGN); + pqsignal(SIGUSR2, StartupProcTriggerHandler); /* * Reset some signals that are accepted by postmaster but not here @@ -9619,7 +9993,7 @@ retry: * five seconds like in the WAL file polling case below. */ if (CheckForStandbyTrigger()) - goto triggered; + goto retry; /* * Wait for more WAL to arrive, or timeout to be reached @@ -9878,14 +10252,28 @@ emode_for_corrupt_record(int emode, XLogRecPtr RecPtr) } /* - * Check to see if the trigger file exists. If it does, request postmaster - * to shut down walreceiver, wait for it to exit, remove the trigger - * file, and return true. + * Check to see whether the user-specified trigger file exists and whether a + * promote request has arrived. If either condition holds, request postmaster + * to shut down walreceiver, wait for it to exit, and return true. */ static bool CheckForStandbyTrigger(void) { struct stat stat_buf; + static bool triggered = false; + + if (triggered) + return true; + + if (promote_triggered) + { + ereport(LOG, + (errmsg("received promote request"))); + ShutdownWalRcv(); + promote_triggered = false; + triggered = true; + return true; + } if (TriggerFile == NULL) return false; @@ -9896,6 +10284,28 @@ CheckForStandbyTrigger(void) (errmsg("trigger file found: %s", TriggerFile))); ShutdownWalRcv(); unlink(TriggerFile); + triggered = true; + return true; + } + return false; +} + +/* + * Check to see if a promote request has arrived. Should be + * called by postmaster after receiving SIGUSR1. + */ +bool +CheckPromoteSignal(void) +{ + struct stat stat_buf; + + if (stat(PROMOTE_SIGNAL_FILE, &stat_buf) == 0) + { + /* + * Since we are in a signal handler, it's not safe + * to elog. We silently ignore any error from unlink. + */ + unlink(PROMOTE_SIGNAL_FILE); return true; } return false; diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index dbcc55c822..528ea23d4c 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -26,6 +26,7 @@ #include "access/xact.h" #include "bootstrap/bootstrap.h" #include "catalog/index.h" +#include "catalog/pg_collation.h" #include "catalog/pg_type.h" #include "libpq/pqsignal.h" #include "miscadmin.h" @@ -88,56 +89,57 @@ struct typinfo bool byval; char align; char storage; + Oid collation; Oid inproc; Oid outproc; }; static const struct typinfo TypInfo[] = { - {"bool", BOOLOID, 0, 1, true, 'c', 'p', + {"bool", BOOLOID, 0, 1, true, 'c', 'p', InvalidOid, F_BOOLIN, F_BOOLOUT}, - {"bytea", BYTEAOID, 0, -1, false, 'i', 'x', + {"bytea", BYTEAOID, 0, -1, false, 'i', 'x', InvalidOid, F_BYTEAIN, F_BYTEAOUT}, - {"char", CHAROID, 0, 1, true, 'c', 'p', + {"char", CHAROID, 0, 1, true, 'c', 'p', InvalidOid, F_CHARIN, F_CHAROUT}, - {"int2", INT2OID, 0, 2, true, 's', 'p', + {"int2", INT2OID, 0, 2, true, 's', 'p', InvalidOid, F_INT2IN, F_INT2OUT}, - {"int4", INT4OID, 0, 4, true, 'i', 'p', + {"int4", INT4OID, 0, 4, true, 'i', 'p', InvalidOid, F_INT4IN, F_INT4OUT}, - {"float4", FLOAT4OID, 0, 4, FLOAT4PASSBYVAL, 'i', 'p', + {"float4", FLOAT4OID, 0, 4, FLOAT4PASSBYVAL, 'i', 'p', InvalidOid, F_FLOAT4IN, F_FLOAT4OUT}, - {"name", NAMEOID, CHAROID, NAMEDATALEN, false, 'c', 'p', + {"name", NAMEOID, CHAROID, NAMEDATALEN, false, 'c', 'p', InvalidOid, F_NAMEIN, F_NAMEOUT}, - {"regclass", REGCLASSOID, 0, 4, true, 'i', 'p', + {"regclass", REGCLASSOID, 0, 4, true, 'i', 'p', InvalidOid, F_REGCLASSIN, F_REGCLASSOUT}, - {"regproc", REGPROCOID, 0, 4, true, 'i', 'p', + {"regproc", REGPROCOID, 0, 4, true, 'i', 'p', InvalidOid, F_REGPROCIN, F_REGPROCOUT}, - {"regtype", REGTYPEOID, 0, 4, true, 'i', 'p', + {"regtype", REGTYPEOID, 0, 4, true, 'i', 'p', InvalidOid, F_REGTYPEIN, F_REGTYPEOUT}, - {"text", TEXTOID, 0, -1, false, 'i', 'x', + {"text", TEXTOID, 0, -1, false, 'i', 'x', DEFAULT_COLLATION_OID, F_TEXTIN, F_TEXTOUT}, - {"oid", OIDOID, 0, 4, true, 'i', 'p', + {"oid", OIDOID, 0, 4, true, 'i', 'p', InvalidOid, F_OIDIN, F_OIDOUT}, - {"tid", TIDOID, 0, 6, false, 's', 'p', + {"tid", TIDOID, 0, 6, false, 's', 'p', InvalidOid, F_TIDIN, F_TIDOUT}, - {"xid", XIDOID, 0, 4, true, 'i', 'p', + {"xid", XIDOID, 0, 4, true, 'i', 'p', InvalidOid, F_XIDIN, F_XIDOUT}, - {"cid", CIDOID, 0, 4, true, 'i', 'p', + {"cid", CIDOID, 0, 4, true, 'i', 'p', InvalidOid, F_CIDIN, F_CIDOUT}, - {"pg_node_tree", PGNODETREEOID, 0, -1, false, 'i', 'x', + {"pg_node_tree", PGNODETREEOID, 0, -1, false, 'i', 'x', DEFAULT_COLLATION_OID, F_PG_NODE_TREE_IN, F_PG_NODE_TREE_OUT}, - {"int2vector", INT2VECTOROID, INT2OID, -1, false, 'i', 'p', + {"int2vector", INT2VECTOROID, INT2OID, -1, false, 'i', 'p', InvalidOid, F_INT2VECTORIN, F_INT2VECTOROUT}, - {"oidvector", OIDVECTOROID, OIDOID, -1, false, 'i', 'p', + {"oidvector", OIDVECTOROID, OIDOID, -1, false, 'i', 'p', InvalidOid, F_OIDVECTORIN, F_OIDVECTOROUT}, - {"_int4", INT4ARRAYOID, INT4OID, -1, false, 'i', 'x', + {"_int4", INT4ARRAYOID, INT4OID, -1, false, 'i', 'x', InvalidOid, F_ARRAY_IN, F_ARRAY_OUT}, - {"_text", 1009, TEXTOID, -1, false, 'i', 'x', + {"_text", 1009, TEXTOID, -1, false, 'i', 'x', DEFAULT_COLLATION_OID, F_ARRAY_IN, F_ARRAY_OUT}, - {"_oid", 1028, OIDOID, -1, false, 'i', 'x', + {"_oid", 1028, OIDOID, -1, false, 'i', 'x', InvalidOid, F_ARRAY_IN, F_ARRAY_OUT}, - {"_char", 1002, CHAROID, -1, false, 'i', 'x', + {"_char", 1002, CHAROID, -1, false, 'i', 'x', InvalidOid, F_ARRAY_IN, F_ARRAY_OUT}, - {"_aclitem", 1034, ACLITEMOID, -1, false, 'i', 'x', + {"_aclitem", 1034, ACLITEMOID, -1, false, 'i', 'x', InvalidOid, F_ARRAY_IN, F_ARRAY_OUT} }; @@ -710,6 +712,7 @@ DefineAttr(char *name, char *type, int attnum) attrtypes[attnum]->attbyval = Ap->am_typ.typbyval; attrtypes[attnum]->attstorage = Ap->am_typ.typstorage; attrtypes[attnum]->attalign = Ap->am_typ.typalign; + attrtypes[attnum]->attcollation = Ap->am_typ.typcollation; /* if an array type, assume 1-dimensional attribute */ if (Ap->am_typ.typelem != InvalidOid && Ap->am_typ.typlen < 0) attrtypes[attnum]->attndims = 1; @@ -723,6 +726,7 @@ DefineAttr(char *name, char *type, int attnum) attrtypes[attnum]->attbyval = TypInfo[typeoid].byval; attrtypes[attnum]->attstorage = TypInfo[typeoid].storage; attrtypes[attnum]->attalign = TypInfo[typeoid].align; + attrtypes[attnum]->attcollation = TypInfo[typeoid].collation; /* if an array type, assume 1-dimensional attribute */ if (TypInfo[typeoid].elem != InvalidOid && attrtypes[attnum]->attlen < 0) diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile index 17f4cc6cfc..3a834618d2 100644 --- a/src/backend/catalog/Makefile +++ b/src/backend/catalog/Makefile @@ -11,7 +11,7 @@ top_builddir = ../../.. include $(top_builddir)/src/Makefile.global OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \ - objectaddress.o pg_aggregate.o pg_constraint.o pg_conversion.o \ + objectaddress.o pg_aggregate.o pg_collation.o pg_constraint.o pg_conversion.o \ pg_depend.o pg_enum.o pg_inherits.o pg_largeobject.o pg_namespace.o \ pg_operator.o pg_proc.o pg_db_role_setting.o pg_shdepend.o pg_type.o \ storage.o toasting.o @@ -36,10 +36,10 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\ pg_database.h pg_db_role_setting.h pg_tablespace.h pg_pltemplate.h \ pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \ pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \ - pg_ts_parser.h pg_ts_template.h \ + pg_ts_parser.h pg_ts_template.h pg_extension.h \ pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \ pg_foreign_table.h \ - pg_default_acl.h pg_seclabel.h \ + pg_default_acl.h pg_seclabel.h pg_collation.h \ toasting.h indexing.h \ ) diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c index e07db507c0..db1d092796 100644 --- a/src/backend/catalog/aclchk.c +++ b/src/backend/catalog/aclchk.c @@ -25,6 +25,7 @@ #include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/pg_authid.h" +#include "catalog/pg_collation.h" #include "catalog/pg_conversion.h" #include "catalog/pg_database.h" #include "catalog/pg_default_acl.h" @@ -3131,6 +3132,8 @@ static const char *const no_priv_msg[MAX_ACL_KIND] = gettext_noop("permission denied for operator class %s"), /* ACL_KIND_OPFAMILY */ gettext_noop("permission denied for operator family %s"), + /* ACL_KIND_COLLATION */ + gettext_noop("permission denied for collation %s"), /* ACL_KIND_CONVERSION */ gettext_noop("permission denied for conversion %s"), /* ACL_KIND_TABLESPACE */ @@ -3173,6 +3176,8 @@ static const char *const not_owner_msg[MAX_ACL_KIND] = gettext_noop("must be owner of operator class %s"), /* ACL_KIND_OPFAMILY */ gettext_noop("must be owner of operator family %s"), + /* ACL_KIND_COLLATION */ + gettext_noop("must be owner of collation %s"), /* ACL_KIND_CONVERSION */ gettext_noop("must be owner of conversion %s"), /* ACL_KIND_TABLESPACE */ @@ -4632,6 +4637,32 @@ pg_database_ownercheck(Oid db_oid, Oid roleid) } /* + * Ownership check for a collation (specified by OID). + */ +bool +pg_collation_ownercheck(Oid coll_oid, Oid roleid) +{ + HeapTuple tuple; + Oid ownerId; + + /* Superusers bypass all permission checking. */ + if (superuser_arg(roleid)) + return true; + + tuple = SearchSysCache1(COLLOID, ObjectIdGetDatum(coll_oid)); + if (!HeapTupleIsValid(tuple)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("collation with OID %u does not exist", coll_oid))); + + ownerId = ((Form_pg_collation) GETSTRUCT(tuple))->collowner; + + ReleaseSysCache(tuple); + + return has_privs_of_role(roleid, ownerId); +} + +/* * Ownership check for a conversion (specified by OID). */ bool diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index ba0ea178ac..1679776f01 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -28,12 +28,15 @@ #include "catalog/pg_attrdef.h" #include "catalog/pg_authid.h" #include "catalog/pg_cast.h" +#include "catalog/pg_collation.h" +#include "catalog/pg_collation_fn.h" #include "catalog/pg_constraint.h" #include "catalog/pg_conversion.h" #include "catalog/pg_conversion_fn.h" #include "catalog/pg_database.h" #include "catalog/pg_default_acl.h" #include "catalog/pg_depend.h" +#include "catalog/pg_extension.h" #include "catalog/pg_foreign_data_wrapper.h" #include "catalog/pg_foreign_server.h" #include "catalog/pg_foreign_table.h" @@ -56,6 +59,7 @@ #include "commands/comment.h" #include "commands/dbcommands.h" #include "commands/defrem.h" +#include "commands/extension.h" #include "commands/proclang.h" #include "commands/schemacmds.h" #include "commands/seclabel.h" @@ -93,6 +97,7 @@ typedef struct #define DEPFLAG_NORMAL 0x0002 /* reached via normal dependency */ #define DEPFLAG_AUTO 0x0004 /* reached via auto dependency */ #define DEPFLAG_INTERNAL 0x0008 /* reached via internal dependency */ +#define DEPFLAG_EXTENSION 0x0010 /* reached via extension dependency */ /* expansible list of ObjectAddresses */ @@ -130,6 +135,7 @@ static const Oid object_classes[MAX_OCLASS] = { ProcedureRelationId, /* OCLASS_PROC */ TypeRelationId, /* OCLASS_TYPE */ CastRelationId, /* OCLASS_CAST */ + CollationRelationId, /* OCLASS_COLLATION */ ConstraintRelationId, /* OCLASS_CONSTRAINT */ ConversionRelationId, /* OCLASS_CONVERSION */ AttrDefaultRelationId, /* OCLASS_DEFAULT */ @@ -153,8 +159,8 @@ static const Oid object_classes[MAX_OCLASS] = { ForeignDataWrapperRelationId, /* OCLASS_FDW */ ForeignServerRelationId, /* OCLASS_FOREIGN_SERVER */ UserMappingRelationId, /* OCLASS_USER_MAPPING */ - ForeignTableRelationId, /* OCLASS_FOREIGN_TABLE */ - DefaultAclRelationId /* OCLASS_DEFACL */ + DefaultAclRelationId, /* OCLASS_DEFACL */ + ExtensionRelationId /* OCLASS_EXTENSION */ }; @@ -551,10 +557,12 @@ findDependentObjects(const ObjectAddress *object, /* no problem */ break; case DEPENDENCY_INTERNAL: + case DEPENDENCY_EXTENSION: /* * This object is part of the internal implementation of - * another object. We have three cases: + * another object, or is part of the extension that is the + * other object. We have three cases: * * 1. At the outermost recursion level, disallow the DROP. (We * just ereport here, rather than proceeding, since no other @@ -726,6 +734,9 @@ findDependentObjects(const ObjectAddress *object, case DEPENDENCY_INTERNAL: subflags = DEPFLAG_INTERNAL; break; + case DEPENDENCY_EXTENSION: + subflags = DEPFLAG_EXTENSION; + break; case DEPENDENCY_PIN: /* @@ -836,10 +847,12 @@ reportDependentObjects(const ObjectAddresses *targetObjects, /* * If, at any stage of the recursive search, we reached the object via - * an AUTO or INTERNAL dependency, then it's okay to delete it even in - * RESTRICT mode. + * an AUTO, INTERNAL, or EXTENSION dependency, then it's okay to + * delete it even in RESTRICT mode. */ - if (extra->flags & (DEPFLAG_AUTO | DEPFLAG_INTERNAL)) + if (extra->flags & (DEPFLAG_AUTO | + DEPFLAG_INTERNAL | + DEPFLAG_EXTENSION)) { /* * auto-cascades are reported at DEBUG2, not msglevel. We don't @@ -1065,6 +1078,10 @@ doDeletion(const ObjectAddress *object) DropCastById(object->objectId); break; + case OCLASS_COLLATION: + RemoveCollationById(object->objectId); + break; + case OCLASS_CONSTRAINT: RemoveConstraintById(object->objectId); break; @@ -1154,6 +1171,10 @@ doDeletion(const ObjectAddress *object) RemoveDefaultACLById(object->objectId); break; + case OCLASS_EXTENSION: + RemoveExtensionById(object->objectId); + break; + default: elog(ERROR, "unrecognized object class: %u", object->classId); @@ -1403,6 +1424,9 @@ find_expr_references_walker(Node *node, /* A constant must depend on the constant's datatype */ add_object_address(OCLASS_TYPE, con->consttype, 0, context->addrs); + if (OidIsValid(con->constcollid)) + add_object_address(OCLASS_COLLATION, con->constcollid, 0, + context->addrs); /* * If it's a regclass or similar literal referring to an existing @@ -1469,6 +1493,9 @@ find_expr_references_walker(Node *node, /* A parameter must depend on the parameter's datatype */ add_object_address(OCLASS_TYPE, param->paramtype, 0, context->addrs); + if (OidIsValid(param->paramcollation)) + add_object_address(OCLASS_COLLATION, param->paramcollation, 0, + context->addrs); } else if (IsA(node, FuncExpr)) { @@ -1539,6 +1566,13 @@ find_expr_references_walker(Node *node, add_object_address(OCLASS_TYPE, relab->resulttype, 0, context->addrs); } + else if (IsA(node, CollateClause)) + { + CollateClause *coll = (CollateClause *) node; + + add_object_address(OCLASS_COLLATION, coll->collOid, 0, + context->addrs); + } else if (IsA(node, CoerceViaIO)) { CoerceViaIO *iocoerce = (CoerceViaIO *) node; @@ -1639,6 +1673,14 @@ find_expr_references_walker(Node *node, add_object_address(OCLASS_TYPE, lfirst_oid(ct), 0, context->addrs); } + foreach(ct, rte->funccolcollations) + { + Oid collid = lfirst_oid(ct); + + if (OidIsValid(collid)) + add_object_address(OCLASS_COLLATION, collid, 0, + context->addrs); + } break; default: break; @@ -2005,6 +2047,9 @@ getObjectClass(const ObjectAddress *object) case CastRelationId: return OCLASS_CAST; + case CollationRelationId: + return OCLASS_COLLATION; + case ConstraintRelationId: return OCLASS_CONSTRAINT; @@ -2074,12 +2119,11 @@ getObjectClass(const ObjectAddress *object) case UserMappingRelationId: return OCLASS_USER_MAPPING; - case ForeignTableRelationId: - Assert(object->objectSubId == 0); - return OCLASS_FOREIGN_TABLE; - case DefaultAclRelationId: return OCLASS_DEFACL; + + case ExtensionRelationId: + return OCLASS_EXTENSION; } /* shouldn't get here */ @@ -2154,6 +2198,21 @@ getObjectDescription(const ObjectAddress *object) break; } + case OCLASS_COLLATION: + { + HeapTuple collTup; + + collTup = SearchSysCache1(COLLOID, + ObjectIdGetDatum(object->objectId)); + if (!HeapTupleIsValid(collTup)) + elog(ERROR, "cache lookup failed for collation %u", + object->objectId); + appendStringInfo(&buffer, _("collation %s"), + NameStr(((Form_pg_collation) GETSTRUCT(collTup))->collname)); + ReleaseSysCache(collTup); + break; + } + case OCLASS_CONSTRAINT: { HeapTuple conTup; @@ -2687,6 +2746,18 @@ getObjectDescription(const ObjectAddress *object) break; } + case OCLASS_EXTENSION: + { + char *extname; + + extname = get_extension_name(object->objectId); + if (!extname) + elog(ERROR, "cache lookup failed for extension %u", + object->objectId); + appendStringInfo(&buffer, _("extension %s"), extname); + break; + } + default: appendStringInfo(&buffer, "unrecognized object %u %u %d", object->classId, diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl index d07e892525..0aeaf5bfd7 100644 --- a/src/backend/catalog/genbki.pl +++ b/src/backend/catalog/genbki.pl @@ -340,6 +340,7 @@ sub emit_pgattr_row $row{attalign} = $type->{typalign}; # set attndims if it's an array type $row{attndims} = $type->{typcategory} eq 'A' ? '1' : '0'; + $row{attcollation} = $type->{typcollation}; # attnotnull must be set true if the type is fixed-width and # prior columns are too --- compare DefineAttr in bootstrap.c. # oidvector and int2vector are also treated as not-nullable. diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 35e588ffd9..bfffd6ef4c 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -42,6 +42,7 @@ #include "catalog/namespace.h" #include "catalog/objectaccess.h" #include "catalog/pg_attrdef.h" +#include "catalog/pg_collation.h" #include "catalog/pg_constraint.h" #include "catalog/pg_foreign_table.h" #include "catalog/pg_inherits.h" @@ -542,6 +543,7 @@ InsertPgAttributeTuple(Relation pg_attribute_rel, values[Anum_pg_attribute_attisdropped - 1] = BoolGetDatum(new_attribute->attisdropped); values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(new_attribute->attislocal); values[Anum_pg_attribute_attinhcount - 1] = Int32GetDatum(new_attribute->attinhcount); + values[Anum_pg_attribute_attcollation - 1] = ObjectIdGetDatum(new_attribute->attcollation); /* start out with empty permissions and empty options */ nulls[Anum_pg_attribute_attacl - 1] = true; @@ -612,6 +614,14 @@ AddNewAttributeTuples(Oid new_rel_oid, referenced.objectId = attr->atttypid; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + + if (OidIsValid(attr->attcollation)) + { + referenced.classId = CollationRelationId; + referenced.objectId = attr->attcollation; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } } /* @@ -859,7 +869,8 @@ AddNewRelationType(const char *typeName, 'x', /* fully TOASTable */ -1, /* typmod */ 0, /* array dimensions for typBaseType */ - false); /* Type NOT NULL */ + false, /* Type NOT NULL */ + InvalidOid); /* typcollation */ } /* -------------------------------- @@ -1120,7 +1131,8 @@ heap_create_with_catalog(const char *relname, 'x', /* fully TOASTable */ -1, /* typmod */ 0, /* array dimensions for typBaseType */ - false); /* Type NOT NULL */ + false, /* Type NOT NULL */ + InvalidOid); /* typcollation */ pfree(relarrayname); } @@ -1157,7 +1169,8 @@ heap_create_with_catalog(const char *relname, * entry, so we needn't record them here. Likewise, TOAST tables don't * need a namespace dependency (they live in a pinned namespace) nor an * owner dependency (they depend indirectly through the parent table), nor - * should they have any ACL entries. + * should they have any ACL entries. The same applies for extension + * dependencies. * * Also, skip this in bootstrap mode, since we don't make dependencies * while bootstrapping. @@ -1179,6 +1192,8 @@ heap_create_with_catalog(const char *relname, recordDependencyOnOwner(RelationRelationId, relid, ownerid); + recordDependencyOnCurrentExtension(&myself); + if (reloftypeid) { referenced.classId = TypeRelationId; @@ -1588,14 +1603,10 @@ heap_drop_with_catalog(Oid relid) /* * There can no longer be anyone *else* touching the relation, but we - * might still have open queries or cursors in our own session. + * might still have open queries or cursors, or pending trigger events, + * in our own session. */ - if (rel->rd_refcnt != 1) - ereport(ERROR, - (errcode(ERRCODE_OBJECT_IN_USE), - errmsg("cannot drop \"%s\" because " - "it is being used by active queries in this session", - RelationGetRelationName(rel)))); + CheckTableNotInUse(rel, "DROP TABLE"); /* * Delete pg_foreign_table tuple first. @@ -1837,6 +1848,7 @@ StoreRelCheck(Relation rel, char *ccname, Node *expr, CONSTRAINT_CHECK, /* Constraint Type */ false, /* Is Deferrable */ false, /* Is Deferred */ + true, /* Is Validated */ RelationGetRelid(rel), /* relation */ attNos, /* attrs in the constraint */ keycount, /* # attrs in the constraint */ diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 5254b65f39..58265129e6 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -36,6 +36,7 @@ #include "catalog/index.h" #include "catalog/indexing.h" #include "catalog/namespace.h" +#include "catalog/pg_collation.h" #include "catalog/pg_constraint.h" #include "catalog/pg_operator.h" #include "catalog/pg_opclass.h" @@ -87,12 +88,14 @@ static TupleDesc ConstructTupleDescriptor(Relation heapRelation, IndexInfo *indexInfo, List *indexColNames, Oid accessMethodObjectId, + Oid *collationObjectId, Oid *classObjectId); static void InitializeAttributeOids(Relation indexRelation, int numatts, Oid indexoid); static void AppendAttributeTuples(Relation indexRelation, int numatts); static void UpdateIndexRelation(Oid indexoid, Oid heapoid, IndexInfo *indexInfo, + Oid *collationOids, Oid *classOids, int16 *coloptions, bool primary, @@ -264,6 +267,7 @@ ConstructTupleDescriptor(Relation heapRelation, IndexInfo *indexInfo, List *indexColNames, Oid accessMethodObjectId, + Oid *collationObjectId, Oid *classObjectId) { int numatts = indexInfo->ii_NumIndexAttrs; @@ -398,6 +402,8 @@ ConstructTupleDescriptor(Relation heapRelation, CheckAttributeType(NameStr(to->attname), to->atttypid, false); } + to->attcollation = collationObjectId[i]; + /* * We do not yet have the correct relation OID for the index, so just * set it invalid for now. InitializeAttributeOids() will fix it @@ -521,6 +527,7 @@ static void UpdateIndexRelation(Oid indexoid, Oid heapoid, IndexInfo *indexInfo, + Oid *collationOids, Oid *classOids, int16 *coloptions, bool primary, @@ -529,6 +536,7 @@ UpdateIndexRelation(Oid indexoid, bool isvalid) { int2vector *indkey; + oidvector *indcollation; oidvector *indclass; int2vector *indoption; Datum exprsDatum; @@ -546,6 +554,7 @@ UpdateIndexRelation(Oid indexoid, indkey = buildint2vector(NULL, indexInfo->ii_NumIndexAttrs); for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++) indkey->values[i] = indexInfo->ii_KeyAttrNumbers[i]; + indcollation = buildoidvector(collationOids, indexInfo->ii_NumIndexAttrs); indclass = buildoidvector(classOids, indexInfo->ii_NumIndexAttrs); indoption = buildint2vector(coloptions, indexInfo->ii_NumIndexAttrs); @@ -601,6 +610,7 @@ UpdateIndexRelation(Oid indexoid, /* we set isvalid and isready the same way */ values[Anum_pg_index_indisready - 1] = BoolGetDatum(isvalid); values[Anum_pg_index_indkey - 1] = PointerGetDatum(indkey); + values[Anum_pg_index_indcollation - 1] = PointerGetDatum(indcollation); values[Anum_pg_index_indclass - 1] = PointerGetDatum(indclass); values[Anum_pg_index_indoption - 1] = PointerGetDatum(indoption); values[Anum_pg_index_indexprs - 1] = exprsDatum; @@ -664,6 +674,7 @@ index_create(Relation heapRelation, List *indexColNames, Oid accessMethodObjectId, Oid tableSpaceId, + Oid *collationObjectId, Oid *classObjectId, int16 *coloptions, Datum reloptions, @@ -761,6 +772,7 @@ index_create(Relation heapRelation, indexInfo, indexColNames, accessMethodObjectId, + collationObjectId, classObjectId); /* @@ -856,7 +868,7 @@ index_create(Relation heapRelation, * ---------------- */ UpdateIndexRelation(indexRelationId, heapRelationId, indexInfo, - classObjectId, coloptions, isprimary, is_exclusion, + collationObjectId, classObjectId, coloptions, isprimary, is_exclusion, !deferrable, !concurrent); @@ -949,6 +961,19 @@ index_create(Relation heapRelation, Assert(!initdeferred); } + /* Store dependency on collations */ + for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++) + { + if (OidIsValid(collationObjectId[i])) + { + referenced.classId = CollationRelationId; + referenced.objectId = collationObjectId[i]; + referenced.objectSubId = 0; + + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } + } + /* Store dependency on operator classes */ for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++) { @@ -1103,6 +1128,7 @@ index_constraint_create(Relation heapRelation, constraintType, deferrable, initdeferred, + true, RelationGetRelid(heapRelation), indexInfo->ii_KeyAttrNumbers, indexInfo->ii_NumIndexAttrs, @@ -1273,6 +1299,12 @@ index_drop(Oid indexId) userIndexRelation = index_open(indexId, AccessExclusiveLock); /* + * There can no longer be anyone *else* touching the index, but we + * might still have open queries using it in our own session. + */ + CheckTableNotInUse(userIndexRelation, "DROP INDEX"); + + /* * Schedule physical removal of the files */ RelationDropStorage(userIndexRelation); @@ -1673,6 +1705,11 @@ index_build(Relation heapRelation, procedure = indexRelation->rd_am->ambuild; Assert(RegProcedureIsValid(procedure)); + ereport(DEBUG1, + (errmsg("building index \"%s\" on table \"%s\"", + RelationGetRelationName(indexRelation), + RelationGetRelationName(heapRelation)))); + /* * Switch to the table owner's userid, so that any index functions are run * as that user. Also lock down security-restricted operations and @@ -2369,7 +2406,7 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot) ivinfo.strategy = NULL; state.tuplesort = tuplesort_begin_datum(TIDOID, - TIDLessOperator, false, + TIDLessOperator, InvalidOid, false, maintenance_work_mem, false); state.htups = state.itups = state.tups_inserted = 0; diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index 5b8b941770..e81a3bb40d 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -354,7 +354,23 @@ GRANT SELECT ON attributes TO PUBLIC; * CHARACTER_SETS view */ --- feature not supported +CREATE VIEW character_sets AS + SELECT CAST(null AS sql_identifier) AS character_set_catalog, + CAST(null AS sql_identifier) AS character_set_schema, + CAST(getdatabaseencoding() AS sql_identifier) AS character_set_name, + CAST(CASE WHEN getdatabaseencoding() = 'UTF8' THEN 'UCS' ELSE getdatabaseencoding() END AS sql_identifier) AS character_repertoire, + CAST(getdatabaseencoding() AS sql_identifier) AS form_of_use, + CAST(current_database() AS sql_identifier) AS default_collate_catalog, + CAST(nc.nspname AS sql_identifier) AS default_collate_schema, + CAST(c.collname AS sql_identifier) AS default_collate_name + FROM pg_database d + LEFT JOIN (pg_collation c JOIN pg_namespace nc ON (c.collnamespace = nc.oid)) + ON (datcollate = collcollate AND datctype = collctype) + WHERE d.datname = current_database() + ORDER BY char_length(c.collname) DESC, c.collname ASC -- prefer full/canonical name + LIMIT 1; + +GRANT SELECT ON character_sets TO PUBLIC; /* @@ -425,14 +441,35 @@ GRANT SELECT ON check_constraints TO PUBLIC; * COLLATIONS view */ --- feature not supported +CREATE VIEW collations AS + SELECT CAST(current_database() AS sql_identifier) AS collation_catalog, + CAST(nc.nspname AS sql_identifier) AS collation_schema, + CAST(c.collname AS sql_identifier) AS collation_name, + CAST('NO PAD' AS character_data) AS pad_attribute + FROM pg_collation c, pg_namespace nc + WHERE c.collnamespace = nc.oid + AND collencoding = (SELECT encoding FROM pg_catalog.pg_database WHERE datname = pg_catalog.current_database()); + +GRANT SELECT ON collations TO PUBLIC; + /* * 5.16 * COLLATION_CHARACTER_SET_APPLICABILITY view */ --- feature not supported +CREATE VIEW collation_character_set_applicability AS + SELECT CAST(current_database() AS sql_identifier) AS collation_catalog, + CAST(nc.nspname AS sql_identifier) AS collation_schema, + CAST(c.collname AS sql_identifier) AS collation_name, + CAST(null AS sql_identifier) AS character_set_catalog, + CAST(null AS sql_identifier) AS character_set_schema, + CAST(getdatabaseencoding() AS sql_identifier) AS character_set_name + FROM pg_collation c, pg_namespace nc + WHERE c.collnamespace = nc.oid + AND collencoding = (SELECT encoding FROM pg_catalog.pg_database WHERE datname = pg_catalog.current_database()); + +GRANT SELECT ON collation_character_set_applicability TO PUBLIC; /* diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c index d8bb4e39a8..8b04b9fd9b 100644 --- a/src/backend/catalog/namespace.c +++ b/src/backend/catalog/namespace.c @@ -23,6 +23,7 @@ #include "catalog/dependency.h" #include "catalog/namespace.h" #include "catalog/pg_authid.h" +#include "catalog/pg_collation.h" #include "catalog/pg_conversion.h" #include "catalog/pg_conversion_fn.h" #include "catalog/pg_namespace.h" @@ -37,6 +38,7 @@ #include "catalog/pg_type.h" #include "commands/dbcommands.h" #include "funcapi.h" +#include "mb/pg_wchar.h" #include "miscadmin.h" #include "nodes/makefuncs.h" #include "parser/parse_func.h" @@ -198,6 +200,7 @@ Datum pg_type_is_visible(PG_FUNCTION_ARGS); Datum pg_function_is_visible(PG_FUNCTION_ARGS); Datum pg_operator_is_visible(PG_FUNCTION_ARGS); Datum pg_opclass_is_visible(PG_FUNCTION_ARGS); +Datum pg_collation_is_visible(PG_FUNCTION_ARGS); Datum pg_conversion_is_visible(PG_FUNCTION_ARGS); Datum pg_ts_parser_is_visible(PG_FUNCTION_ARGS); Datum pg_ts_dict_is_visible(PG_FUNCTION_ARGS); @@ -1611,6 +1614,89 @@ OpfamilyIsVisible(Oid opfid) } /* + * CollationGetCollid + * Try to resolve an unqualified collation name. + * Returns OID if collation found in search path, else InvalidOid. + * + * This is essentially the same as RelnameGetRelid. + */ +Oid +CollationGetCollid(const char *collname) +{ + Oid collid; + ListCell *l; + + recomputeNamespacePath(); + + foreach(l, activeSearchPath) + { + Oid namespaceId = lfirst_oid(l); + + if (namespaceId == myTempNamespace) + continue; /* do not look in temp namespace */ + + collid = GetSysCacheOid3(COLLNAMEENCNSP, + PointerGetDatum(collname), + Int32GetDatum(GetDatabaseEncoding()), + ObjectIdGetDatum(namespaceId)); + if (OidIsValid(collid)) + return collid; + } + + /* Not found in path */ + return InvalidOid; +} + +/* + * CollationIsVisible + * Determine whether a collation (identified by OID) is visible in the + * current search path. Visible means "would be found by searching + * for the unqualified collation name". + */ +bool +CollationIsVisible(Oid collid) +{ + HeapTuple colltup; + Form_pg_collation collform; + Oid collnamespace; + bool visible; + + colltup = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid)); + if (!HeapTupleIsValid(colltup)) + elog(ERROR, "cache lookup failed for collation %u", collid); + collform = (Form_pg_collation) GETSTRUCT(colltup); + + recomputeNamespacePath(); + + /* + * Quick check: if it ain't in the path at all, it ain't visible. Items in + * the system namespace are surely in the path and so we needn't even do + * list_member_oid() for them. + */ + collnamespace = collform->collnamespace; + if (collnamespace != PG_CATALOG_NAMESPACE && + !list_member_oid(activeSearchPath, collnamespace)) + visible = false; + else + { + /* + * If it is in the path, it might still not be visible; it could be + * hidden by another conversion of the same name earlier in the path. + * So we must do a slow check to see if this conversion would be found + * by CollationGetCollid. + */ + char *collname = NameStr(collform->collname); + + visible = (CollationGetCollid(collname) == collid); + } + + ReleaseSysCache(colltup); + + return visible; +} + + +/* * ConversionGetConid * Try to resolve an unqualified conversion name. * Returns OID if conversion found in search path, else InvalidOid. @@ -2808,6 +2894,63 @@ PopOverrideSearchPath(void) /* + * get_collation_oid - find a collation by possibly qualified name + */ +Oid +get_collation_oid(List *name, bool missing_ok) +{ + char *schemaname; + char *collation_name; + Oid namespaceId; + Oid colloid = InvalidOid; + ListCell *l; + int encoding; + + encoding = GetDatabaseEncoding(); + + /* deconstruct the name list */ + DeconstructQualifiedName(name, &schemaname, &collation_name); + + if (schemaname) + { + /* use exact schema given */ + namespaceId = LookupExplicitNamespace(schemaname); + colloid = GetSysCacheOid3(COLLNAMEENCNSP, + PointerGetDatum(collation_name), + Int32GetDatum(encoding), + ObjectIdGetDatum(namespaceId)); + } + else + { + /* search for it in search path */ + recomputeNamespacePath(); + + foreach(l, activeSearchPath) + { + namespaceId = lfirst_oid(l); + + if (namespaceId == myTempNamespace) + continue; /* do not look in temp namespace */ + + colloid = GetSysCacheOid3(COLLNAMEENCNSP, + PointerGetDatum(collation_name), + Int32GetDatum(encoding), + ObjectIdGetDatum(namespaceId)); + if (OidIsValid(colloid)) + return colloid; + } + } + + /* Not found in path */ + if (!OidIsValid(colloid) && !missing_ok) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("collation \"%s\" for current database encoding \"%s\" does not exist", + NameListToString(name), GetDatabaseEncodingName()))); + return colloid; +} + +/* * get_conversion_oid - find a conversion by possibly qualified name */ Oid @@ -3567,6 +3710,17 @@ pg_opclass_is_visible(PG_FUNCTION_ARGS) } Datum +pg_collation_is_visible(PG_FUNCTION_ARGS) +{ + Oid oid = PG_GETARG_OID(0); + + if (!SearchSysCacheExists1(COLLOID, ObjectIdGetDatum(oid))) + PG_RETURN_NULL(); + + PG_RETURN_BOOL(CollationIsVisible(oid)); +} + +Datum pg_conversion_is_visible(PG_FUNCTION_ARGS) { Oid oid = PG_GETARG_OID(0); diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c index c4608f7f17..aeb07710e8 100644 --- a/src/backend/catalog/objectaddress.c +++ b/src/backend/catalog/objectaddress.c @@ -25,9 +25,11 @@ #include "catalog/pg_authid.h" #include "catalog/pg_cast.h" #include "catalog/pg_class.h" +#include "catalog/pg_collation.h" #include "catalog/pg_constraint.h" #include "catalog/pg_conversion.h" #include "catalog/pg_database.h" +#include "catalog/pg_extension.h" #include "catalog/pg_language.h" #include "catalog/pg_largeobject.h" #include "catalog/pg_largeobject_metadata.h" @@ -46,6 +48,7 @@ #include "catalog/pg_type.h" #include "commands/dbcommands.h" #include "commands/defrem.h" +#include "commands/extension.h" #include "commands/proclang.h" #include "commands/tablespace.h" #include "commands/trigger.h" @@ -129,6 +132,7 @@ get_object_address(ObjectType objtype, List *objname, List *objargs, address = get_object_address_relobject(objtype, objname, &relation); break; case OBJECT_DATABASE: + case OBJECT_EXTENSION: case OBJECT_TABLESPACE: case OBJECT_ROLE: case OBJECT_SCHEMA: @@ -136,6 +140,7 @@ get_object_address(ObjectType objtype, List *objname, List *objargs, address = get_object_address_unqualified(objtype, objname); break; case OBJECT_TYPE: + case OBJECT_DOMAIN: address.classId = TypeRelationId; address.objectId = typenameTypeId(NULL, makeTypeNameFromNameList(objname)); @@ -161,6 +166,11 @@ get_object_address(ObjectType objtype, List *objname, List *objargs, false, -1); address.objectSubId = 0; break; + case OBJECT_COLLATION: + address.classId = CollationRelationId; + address.objectId = get_collation_oid(objname, false); + address.objectSubId = 0; + break; case OBJECT_CONVERSION: address.classId = ConversionRelationId; address.objectId = get_conversion_oid(objname, false); @@ -267,6 +277,9 @@ get_object_address_unqualified(ObjectType objtype, List *qualname) case OBJECT_DATABASE: msg = gettext_noop("database name cannot be qualified"); break; + case OBJECT_EXTENSION: + msg = gettext_noop("extension name cannot be qualified"); + break; case OBJECT_TABLESPACE: msg = gettext_noop("tablespace name cannot be qualified"); break; @@ -299,6 +312,11 @@ get_object_address_unqualified(ObjectType objtype, List *qualname) address.objectId = get_database_oid(name, false); address.objectSubId = 0; break; + case OBJECT_EXTENSION: + address.classId = ExtensionRelationId; + address.objectId = get_extension_oid(name, false); + address.objectSubId = 0; + break; case OBJECT_TABLESPACE: address.classId = TableSpaceRelationId; address.objectId = get_tablespace_oid(name, false); @@ -609,6 +627,9 @@ object_exists(ObjectAddress address) case OperatorRelationId: cache = OPEROID; break; + case CollationRelationId: + cache = COLLOID; + break; case ConversionRelationId: cache = CONVOID; break; @@ -643,6 +664,9 @@ object_exists(ObjectAddress address) case TSConfigRelationId: cache = TSCONFIGOID; break; + case ExtensionRelationId: + indexoid = ExtensionOidIndexId; + break; default: elog(ERROR, "unrecognized classid: %u", address.classId); } diff --git a/src/backend/catalog/pg_collation.c b/src/backend/catalog/pg_collation.c new file mode 100644 index 0000000000..54a75a6f62 --- /dev/null +++ b/src/backend/catalog/pg_collation.c @@ -0,0 +1,163 @@ +/*------------------------------------------------------------------------- + * + * pg_collation.c + * routines to support manipulation of the pg_collation relation + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/catalog/pg_collation.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/heapam.h" +#include "access/sysattr.h" +#include "catalog/dependency.h" +#include "catalog/indexing.h" +#include "catalog/objectaccess.h" +#include "catalog/pg_collation.h" +#include "catalog/pg_collation_fn.h" +#include "catalog/pg_namespace.h" +#include "catalog/pg_proc.h" +#include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/fmgroids.h" +#include "utils/rel.h" +#include "utils/syscache.h" +#include "utils/tqual.h" + +/* + * CollationCreate + * + * Add a new tuple to pg_collation. + */ +Oid +CollationCreate(const char *collname, Oid collnamespace, + Oid collowner, + int32 collencoding, + const char *collcollate, const char *collctype) +{ + int i; + Relation rel; + TupleDesc tupDesc; + HeapTuple tup; + bool nulls[Natts_pg_collation]; + Datum values[Natts_pg_collation]; + NameData name_name, name_collate, name_ctype; + Oid oid; + ObjectAddress myself, + referenced; + + AssertArg(collname); + AssertArg(collnamespace); + AssertArg(collowner); + AssertArg(collcollate); + AssertArg(collctype); + + /* make sure there is no existing collation of same name */ + if (SearchSysCacheExists3(COLLNAMEENCNSP, + PointerGetDatum(collname), + Int32GetDatum(collencoding), + ObjectIdGetDatum(collnamespace))) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("collation \"%s\" for encoding \"%s\" already exists", + collname, pg_encoding_to_char(collencoding)))); + + /* open pg_collation */ + rel = heap_open(CollationRelationId, RowExclusiveLock); + tupDesc = rel->rd_att; + + /* initialize nulls and values */ + for (i = 0; i < Natts_pg_collation; i++) + { + nulls[i] = false; + values[i] = (Datum) NULL; + } + + /* form a tuple */ + namestrcpy(&name_name, collname); + values[Anum_pg_collation_collname - 1] = NameGetDatum(&name_name); + values[Anum_pg_collation_collnamespace - 1] = ObjectIdGetDatum(collnamespace); + values[Anum_pg_collation_collowner - 1] = ObjectIdGetDatum(collowner); + values[Anum_pg_collation_collencoding - 1] = Int32GetDatum(collencoding); + namestrcpy(&name_collate, collcollate); + values[Anum_pg_collation_collcollate - 1] = NameGetDatum(&name_collate); + namestrcpy(&name_ctype, collctype); + values[Anum_pg_collation_collctype - 1] = NameGetDatum(&name_ctype); + + tup = heap_form_tuple(tupDesc, values, nulls); + + /* insert a new tuple */ + oid = simple_heap_insert(rel, tup); + Assert(OidIsValid(oid)); + + /* update the index if any */ + CatalogUpdateIndexes(rel, tup); + + myself.classId = CollationRelationId; + myself.objectId = HeapTupleGetOid(tup); + myself.objectSubId = 0; + + /* create dependency on namespace */ + referenced.classId = NamespaceRelationId; + referenced.objectId = collnamespace; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + + /* create dependency on owner */ + recordDependencyOnOwner(CollationRelationId, HeapTupleGetOid(tup), + collowner); + + /* dependency on extension */ + recordDependencyOnCurrentExtension(&myself); + + /* Post creation hook for new collation */ + InvokeObjectAccessHook(OAT_POST_CREATE, + CollationRelationId, HeapTupleGetOid(tup), 0); + + heap_freetuple(tup); + heap_close(rel, RowExclusiveLock); + + return oid; +} + +/* + * RemoveCollationById + * + * Remove a tuple from pg_collation by Oid. This function is solely + * called inside catalog/dependency.c + */ +void +RemoveCollationById(Oid collationOid) +{ + Relation rel; + HeapTuple tuple; + HeapScanDesc scan; + ScanKeyData scanKeyData; + + ScanKeyInit(&scanKeyData, + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(collationOid)); + + /* open pg_collation */ + rel = heap_open(CollationRelationId, RowExclusiveLock); + + scan = heap_beginscan(rel, SnapshotNow, + 1, &scanKeyData); + + /* search for the target tuple */ + if (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection))) + simple_heap_delete(rel, &tuple->t_self); + else + elog(ERROR, "could not find tuple for collation %u", collationOid); + heap_endscan(scan); + heap_close(rel, RowExclusiveLock); +} diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c index 3a38518b6c..6619eed431 100644 --- a/src/backend/catalog/pg_constraint.c +++ b/src/backend/catalog/pg_constraint.c @@ -46,6 +46,7 @@ CreateConstraintEntry(const char *constraintName, char constraintType, bool isDeferrable, bool isDeferred, + bool isValidated, Oid relId, const int16 *constraintKey, int constraintNKeys, @@ -158,6 +159,7 @@ CreateConstraintEntry(const char *constraintName, values[Anum_pg_constraint_contype - 1] = CharGetDatum(constraintType); values[Anum_pg_constraint_condeferrable - 1] = BoolGetDatum(isDeferrable); values[Anum_pg_constraint_condeferred - 1] = BoolGetDatum(isDeferred); + values[Anum_pg_constraint_convalidated - 1] = BoolGetDatum(isValidated); values[Anum_pg_constraint_conrelid - 1] = ObjectIdGetDatum(relId); values[Anum_pg_constraint_contypid - 1] = ObjectIdGetDatum(domainId); values[Anum_pg_constraint_conindid - 1] = ObjectIdGetDatum(indexRelId); diff --git a/src/backend/catalog/pg_conversion.c b/src/backend/catalog/pg_conversion.c index b2f66d75ac..1ef6a9d24e 100644 --- a/src/backend/catalog/pg_conversion.c +++ b/src/backend/catalog/pg_conversion.c @@ -132,6 +132,9 @@ ConversionCreate(const char *conname, Oid connamespace, recordDependencyOnOwner(ConversionRelationId, HeapTupleGetOid(tup), conowner); + /* dependency on extension */ + recordDependencyOnCurrentExtension(&myself); + /* Post creation hook for new conversion */ InvokeObjectAccessHook(OAT_POST_CREATE, ConversionRelationId, HeapTupleGetOid(tup), 0); diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c index 370051d91e..2bb7bb3d5f 100644 --- a/src/backend/catalog/pg_depend.c +++ b/src/backend/catalog/pg_depend.c @@ -20,6 +20,8 @@ #include "catalog/indexing.h" #include "catalog/pg_constraint.h" #include "catalog/pg_depend.h" +#include "catalog/pg_extension.h" +#include "commands/extension.h" #include "miscadmin.h" #include "utils/fmgroids.h" #include "utils/lsyscache.h" @@ -123,15 +125,42 @@ recordMultipleDependencies(const ObjectAddress *depender, } /* + * If we are executing a CREATE EXTENSION operation, mark the given object + * as being a member of the extension. Otherwise, do nothing. + * + * This must be called during creation of any user-definable object type + * that could be a member of an extension. + */ +void +recordDependencyOnCurrentExtension(const ObjectAddress *object) +{ + if (creating_extension) + { + ObjectAddress extension; + + extension.classId = ExtensionRelationId; + extension.objectId = CurrentExtensionObject; + extension.objectSubId = 0; + + recordDependencyOn(object, &extension, DEPENDENCY_EXTENSION); + } +} + +/* * deleteDependencyRecordsFor -- delete all records with given depender * classId/objectId. Returns the number of records deleted. * * This is used when redefining an existing object. Links leading to the * object do not change, and links leading from it will be recreated * (possibly with some differences from before). + * + * If skipExtensionDeps is true, we do not delete any dependencies that + * show that the given object is a member of an extension. This avoids + * needing a lot of extra logic to fetch and recreate that dependency. */ long -deleteDependencyRecordsFor(Oid classId, Oid objectId) +deleteDependencyRecordsFor(Oid classId, Oid objectId, + bool skipExtensionDeps) { long count = 0; Relation depRel; @@ -155,6 +184,10 @@ deleteDependencyRecordsFor(Oid classId, Oid objectId) while (HeapTupleIsValid(tup = systable_getnext(scan))) { + if (skipExtensionDeps && + ((Form_pg_depend) GETSTRUCT(tup))->deptype == DEPENDENCY_EXTENSION) + continue; + simple_heap_delete(depRel, &tup->t_self); count++; } @@ -167,6 +200,57 @@ deleteDependencyRecordsFor(Oid classId, Oid objectId) } /* + * deleteDependencyRecordsForClass -- delete all records with given depender + * classId/objectId, dependee classId, and deptype. + * Returns the number of records deleted. + * + * This is a variant of deleteDependencyRecordsFor, useful when revoking + * an object property that is expressed by a dependency record (such as + * extension membership). + */ +long +deleteDependencyRecordsForClass(Oid classId, Oid objectId, + Oid refclassId, char deptype) +{ + long count = 0; + Relation depRel; + ScanKeyData key[2]; + SysScanDesc scan; + HeapTuple tup; + + depRel = heap_open(DependRelationId, RowExclusiveLock); + + ScanKeyInit(&key[0], + Anum_pg_depend_classid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(classId)); + ScanKeyInit(&key[1], + Anum_pg_depend_objid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(objectId)); + + scan = systable_beginscan(depRel, DependDependerIndexId, true, + SnapshotNow, 2, key); + + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup); + + if (depform->refclassid == refclassId && depform->deptype == deptype) + { + simple_heap_delete(depRel, &tup->t_self); + count++; + } + } + + systable_endscan(scan); + + heap_close(depRel, RowExclusiveLock); + + return count; +} + +/* * Adjust dependency record(s) to point to a different object of the same type * * classId/objectId specify the referencing object. @@ -321,20 +405,20 @@ isObjectPinned(const ObjectAddress *object, Relation rel) /* - * Detect whether a sequence is marked as "owned" by a column + * Find the extension containing the specified object, if any * - * An ownership marker is an AUTO dependency from the sequence to the - * column. If we find one, store the identity of the owning column - * into *tableId and *colId and return TRUE; else return FALSE. + * Returns the OID of the extension, or InvalidOid if the object does not + * belong to any extension. * - * Note: if there's more than one such pg_depend entry then you get - * a random one of them returned into the out parameters. This should - * not happen, though. + * Extension membership is marked by an EXTENSION dependency from the object + * to the extension. Note that the result will be indeterminate if pg_depend + * contains links from this object to more than one extension ... but that + * should never happen. */ -bool -sequenceIsOwned(Oid seqId, Oid *tableId, int32 *colId) +Oid +getExtensionOfObject(Oid classId, Oid objectId) { - bool ret = false; + Oid result = InvalidOid; Relation depRel; ScanKeyData key[2]; SysScanDesc scan; @@ -345,11 +429,11 @@ sequenceIsOwned(Oid seqId, Oid *tableId, int32 *colId) ScanKeyInit(&key[0], Anum_pg_depend_classid, BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(RelationRelationId)); + ObjectIdGetDatum(classId)); ScanKeyInit(&key[1], Anum_pg_depend_objid, BTEqualStrategyNumber, F_OIDEQ, - ObjectIdGetDatum(seqId)); + ObjectIdGetDatum(objectId)); scan = systable_beginscan(depRel, DependDependerIndexId, true, SnapshotNow, 2, key); @@ -358,12 +442,10 @@ sequenceIsOwned(Oid seqId, Oid *tableId, int32 *colId) { Form_pg_depend depform = (Form_pg_depend) GETSTRUCT(tup); - if (depform->refclassid == RelationRelationId && - depform->deptype == DEPENDENCY_AUTO) + if (depform->refclassid == ExtensionRelationId && + depform->deptype == DEPENDENCY_EXTENSION) { - *tableId = depform->refobjid; - *colId = depform->refobjsubid; - ret = true; + result = depform->refobjid; break; /* no need to keep scanning */ } } @@ -372,24 +454,30 @@ sequenceIsOwned(Oid seqId, Oid *tableId, int32 *colId) heap_close(depRel, AccessShareLock); - return ret; + return result; } /* - * Remove any existing "owned" markers for the specified sequence. + * Detect whether a sequence is marked as "owned" by a column * - * Note: we don't provide a special function to install an "owned" - * marker; just use recordDependencyOn(). + * An ownership marker is an AUTO dependency from the sequence to the + * column. If we find one, store the identity of the owning column + * into *tableId and *colId and return TRUE; else return FALSE. + * + * Note: if there's more than one such pg_depend entry then you get + * a random one of them returned into the out parameters. This should + * not happen, though. */ -void -markSequenceUnowned(Oid seqId) +bool +sequenceIsOwned(Oid seqId, Oid *tableId, int32 *colId) { + bool ret = false; Relation depRel; ScanKeyData key[2]; SysScanDesc scan; HeapTuple tup; - depRel = heap_open(DependRelationId, RowExclusiveLock); + depRel = heap_open(DependRelationId, AccessShareLock); ScanKeyInit(&key[0], Anum_pg_depend_classid, @@ -410,13 +498,31 @@ markSequenceUnowned(Oid seqId) if (depform->refclassid == RelationRelationId && depform->deptype == DEPENDENCY_AUTO) { - simple_heap_delete(depRel, &tup->t_self); + *tableId = depform->refobjid; + *colId = depform->refobjsubid; + ret = true; + break; /* no need to keep scanning */ } } systable_endscan(scan); - heap_close(depRel, RowExclusiveLock); + heap_close(depRel, AccessShareLock); + + return ret; +} + +/* + * Remove any existing "owned" markers for the specified sequence. + * + * Note: we don't provide a special function to install an "owned" + * marker; just use recordDependencyOn(). + */ +void +markSequenceUnowned(Oid seqId) +{ + deleteDependencyRecordsForClass(RelationRelationId, seqId, + RelationRelationId, DEPENDENCY_AUTO); } /* diff --git a/src/backend/catalog/pg_namespace.c b/src/backend/catalog/pg_namespace.c index c78aa019bf..172f99196c 100644 --- a/src/backend/catalog/pg_namespace.c +++ b/src/backend/catalog/pg_namespace.c @@ -38,6 +38,7 @@ NamespaceCreate(const char *nspName, Oid ownerId) Datum values[Natts_pg_namespace]; NameData nname; TupleDesc tupDesc; + ObjectAddress myself; int i; /* sanity checks */ @@ -73,9 +74,17 @@ NamespaceCreate(const char *nspName, Oid ownerId) heap_close(nspdesc, RowExclusiveLock); - /* Record dependency on owner */ + /* Record dependencies */ + myself.classId = NamespaceRelationId; + myself.objectId = nspoid; + myself.objectSubId = 0; + + /* dependency on owner */ recordDependencyOnOwner(NamespaceRelationId, nspoid, ownerId); + /* dependency on extension */ + recordDependencyOnCurrentExtension(&myself); + /* Post creation hook for new schema */ InvokeObjectAccessHook(OAT_POST_CREATE, NamespaceRelationId, nspoid, 0); diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c index c70483a739..ccd0fe1997 100644 --- a/src/backend/catalog/pg_operator.c +++ b/src/backend/catalog/pg_operator.c @@ -777,7 +777,7 @@ makeOperatorDependencies(HeapTuple tuple) myself.objectSubId = 0; /* In case we are updating a shell, delete any existing entries */ - deleteDependencyRecordsFor(myself.classId, myself.objectId); + deleteDependencyRecordsFor(myself.classId, myself.objectId, false); deleteSharedDependencyRecordsFor(myself.classId, myself.objectId, 0); /* Dependency on namespace */ @@ -855,4 +855,7 @@ makeOperatorDependencies(HeapTuple tuple) /* Dependency on owner */ recordDependencyOnOwner(OperatorRelationId, HeapTupleGetOid(tuple), oper->oprowner); + + /* Dependency on extension */ + recordDependencyOnCurrentExtension(&myself); } diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index 2ab87d2df5..3f3877da28 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -562,10 +562,11 @@ ProcedureCreate(const char *procedureName, * Create dependencies for the new function. If we are updating an * existing function, first delete any existing pg_depend entries. * (However, since we are not changing ownership or permissions, the - * shared dependencies do *not* need to change, and we leave them alone.) + * shared dependencies do *not* need to change, and we leave them alone. + * We also don't change any pre-existing extension-membership dependency.) */ if (is_update) - deleteDependencyRecordsFor(ProcedureRelationId, retval); + deleteDependencyRecordsFor(ProcedureRelationId, retval, true); myself.classId = ProcedureRelationId; myself.objectId = retval; @@ -615,6 +616,10 @@ ProcedureCreate(const char *procedureName, nnewmembers, newmembers); } + /* dependency on extension */ + if (!is_update) + recordDependencyOnCurrentExtension(&myself); + heap_freetuple(tup); /* Post creation hook for new function */ diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c index 040f777b02..8c8e7b276d 100644 --- a/src/backend/catalog/pg_shdepend.c +++ b/src/backend/catalog/pg_shdepend.c @@ -21,6 +21,7 @@ #include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/pg_authid.h" +#include "catalog/pg_collation.h" #include "catalog/pg_conversion.h" #include "catalog/pg_database.h" #include "catalog/pg_default_acl.h" @@ -35,6 +36,7 @@ #include "catalog/pg_tablespace.h" #include "catalog/pg_type.h" #include "commands/dbcommands.h" +#include "commands/collationcmds.h" #include "commands/conversioncmds.h" #include "commands/defrem.h" #include "commands/proclang.h" @@ -1323,6 +1325,10 @@ shdepReassignOwned(List *roleids, Oid newrole) /* Issue the appropriate ALTER OWNER call */ switch (sdepForm->classid) { + case CollationRelationId: + AlterCollationOwner_oid(sdepForm->objid, newrole); + break; + case ConversionRelationId: AlterConversionOwner_oid(sdepForm->objid, newrole); break; diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c index 9c249a7ff7..06301c075b 100644 --- a/src/backend/catalog/pg_type.c +++ b/src/backend/catalog/pg_type.c @@ -19,6 +19,7 @@ #include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/objectaccess.h" +#include "catalog/pg_collation.h" #include "catalog/pg_namespace.h" #include "catalog/pg_proc.h" #include "catalog/pg_type.h" @@ -114,6 +115,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId) values[i++] = ObjectIdGetDatum(InvalidOid); /* typbasetype */ values[i++] = Int32GetDatum(-1); /* typtypmod */ values[i++] = Int32GetDatum(0); /* typndims */ + values[i++] = ObjectIdGetDatum(InvalidOid); /* typcollation */ nulls[i++] = true; /* typdefaultbin */ nulls[i++] = true; /* typdefault */ @@ -155,6 +157,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId) InvalidOid, false, InvalidOid, + InvalidOid, NULL, false); @@ -210,7 +213,8 @@ TypeCreate(Oid newTypeOid, char storage, int32 typeMod, int32 typNDims, /* Array dimensions for baseType */ - bool typeNotNull) + bool typeNotNull, + Oid typeCollation) { Relation pg_type_desc; Oid typeObjectId; @@ -348,6 +352,7 @@ TypeCreate(Oid newTypeOid, values[i++] = ObjectIdGetDatum(baseType); /* typbasetype */ values[i++] = Int32GetDatum(typeMod); /* typtypmod */ values[i++] = Int32GetDatum(typNDims); /* typndims */ + values[i++] = ObjectIdGetDatum(typeCollation); /* typcollation */ /* * initialize the default binary value for this type. Check for nulls of @@ -457,6 +462,7 @@ TypeCreate(Oid newTypeOid, elementType, isImplicitArray, baseType, + typeCollation, (defaultTypeBin ? stringToNode(defaultTypeBin) : NULL), @@ -478,7 +484,7 @@ TypeCreate(Oid newTypeOid, * * If rebuild is true, we remove existing dependencies and rebuild them * from scratch. This is needed for ALTER TYPE, and also when replacing - * a shell type. + * a shell type. We don't remove/rebuild extension dependencies, though. */ void GenerateTypeDependencies(Oid typeNamespace, @@ -496,6 +502,7 @@ GenerateTypeDependencies(Oid typeNamespace, Oid elementType, bool isImplicitArray, Oid baseType, + Oid typeCollation, Node *defaultExpr, bool rebuild) { @@ -504,7 +511,7 @@ GenerateTypeDependencies(Oid typeNamespace, if (rebuild) { - deleteDependencyRecordsFor(TypeRelationId, typeObjectId); + deleteDependencyRecordsFor(TypeRelationId, typeObjectId, true); deleteSharedDependencyRecordsFor(TypeRelationId, typeObjectId, 0); } @@ -518,7 +525,7 @@ GenerateTypeDependencies(Oid typeNamespace, * For a relation rowtype (that's not a composite type), we should skip * these because we'll depend on them indirectly through the pg_class * entry. Likewise, skip for implicit arrays since we'll depend on them - * through the element type. + * through the element type. The same goes for extension membership. */ if ((!OidIsValid(relationOid) || relationKind == RELKIND_COMPOSITE_TYPE) && !isImplicitArray) @@ -529,6 +536,10 @@ GenerateTypeDependencies(Oid typeNamespace, recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); recordDependencyOnOwner(TypeRelationId, typeObjectId, owner); + + /* dependency on extension */ + if (!rebuild) + recordDependencyOnCurrentExtension(&myself); } /* Normal dependencies on the I/O functions */ @@ -632,6 +643,15 @@ GenerateTypeDependencies(Oid typeNamespace, recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } + /* Normal dependency from a domain to its base type's collation. */ + if (OidIsValid(typeCollation)) + { + referenced.classId = CollationRelationId; + referenced.objectId = typeCollation; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } + /* Normal dependency on the default expression. */ if (defaultExpr) recordDependencyOnExpr(&myself, defaultExpr, NIL, DEPENDENCY_NORMAL); diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 718e996e6b..b259303ce6 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -153,6 +153,19 @@ CREATE VIEW pg_locks AS CREATE VIEW pg_cursors AS SELECT * FROM pg_cursor() AS C; +CREATE VIEW pg_available_extensions AS + SELECT E.name, E.default_version, X.extversion AS installed_version, + E.comment + FROM pg_available_extensions() AS E + LEFT JOIN pg_extension AS X ON E.name = X.extname; + +CREATE VIEW pg_available_extension_versions AS + SELECT E.name, E.version, (X.extname IS NOT NULL) AS installed, + E.relocatable, E.schema, E.requires, E.comment + FROM pg_available_extension_versions() AS E + LEFT JOIN pg_extension AS X + ON E.name = X.extname AND E.version = X.extversion; + CREATE VIEW pg_prepared_xacts AS SELECT P.transaction, P.gid, P.prepared, U.rolname AS owner, D.datname AS database @@ -502,7 +515,10 @@ CREATE VIEW pg_stat_replication AS S.client_port, S.backend_start, W.state, - W.sent_location + W.sent_location, + W.write_location, + W.flush_location, + W.apply_location FROM pg_stat_get_activity(NULL) AS S, pg_authid U, pg_stat_get_wal_senders() AS W WHERE S.usesysid = U.oid AND @@ -523,7 +539,8 @@ CREATE VIEW pg_stat_database AS pg_stat_get_db_tuples_inserted(D.oid) AS tup_inserted, pg_stat_get_db_tuples_updated(D.oid) AS tup_updated, pg_stat_get_db_tuples_deleted(D.oid) AS tup_deleted, - pg_stat_get_db_conflict_all(D.oid) AS conflicts + pg_stat_get_db_conflict_all(D.oid) AS conflicts, + pg_stat_get_db_stat_reset_time(D.oid) AS stats_reset FROM pg_database D; CREATE VIEW pg_stat_database_conflicts AS @@ -570,7 +587,8 @@ CREATE VIEW pg_stat_bgwriter AS pg_stat_get_bgwriter_maxwritten_clean() AS maxwritten_clean, pg_stat_get_buf_written_backend() AS buffers_backend, pg_stat_get_buf_fsync_backend() AS buffers_backend_fsync, - pg_stat_get_buf_alloc() AS buffers_alloc; + pg_stat_get_buf_alloc() AS buffers_alloc, + pg_stat_get_bgwriter_stat_reset_time() AS stats_reset; CREATE VIEW pg_user_mappings AS SELECT @@ -672,5 +690,9 @@ COMMENT ON FUNCTION ts_debug(text) IS -- CREATE OR REPLACE FUNCTION + format_type(oid, int DEFAULT NULL, oid DEFAULT NULL) + RETURNS text STABLE LANGUAGE internal AS 'format_type'; + +CREATE OR REPLACE FUNCTION pg_start_backup(label text, fast boolean DEFAULT false) RETURNS text STRICT VOLATILE LANGUAGE internal AS 'pg_start_backup'; diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index c4be3a9ae3..5d5496df98 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -124,6 +124,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio char toast_relname[NAMEDATALEN]; char toast_idxname[NAMEDATALEN]; IndexInfo *indexInfo; + Oid collationObjectId[2]; Oid classObjectId[2]; int16 coloptions[2]; ObjectAddress baseobject, @@ -264,6 +265,9 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio indexInfo->ii_Concurrent = false; indexInfo->ii_BrokenHotChain = false; + collationObjectId[0] = InvalidOid; + collationObjectId[1] = InvalidOid; + classObjectId[0] = OID_BTREE_OPS_OID; classObjectId[1] = INT4_BTREE_OPS_OID; @@ -275,7 +279,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio list_make2("chunk_id", "chunk_seq"), BTREE_AM_OID, rel->rd_rel->reltablespace, - classObjectId, coloptions, (Datum) 0, + collationObjectId, classObjectId, coloptions, (Datum) 0, true, false, false, false, true, false, false); diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile index 9d2a732245..81fd6581f3 100644 --- a/src/backend/commands/Makefile +++ b/src/backend/commands/Makefile @@ -13,8 +13,9 @@ top_builddir = ../../.. include $(top_builddir)/src/Makefile.global OBJS = aggregatecmds.o alter.o analyze.o async.o cluster.o comment.o \ - constraint.o conversioncmds.o copy.o \ - dbcommands.o define.o discard.o explain.o foreigncmds.o functioncmds.o \ + collationcmds.o constraint.o conversioncmds.o copy.o \ + dbcommands.o define.o discard.o explain.o extension.o \ + foreigncmds.o functioncmds.o \ indexcmds.o lockcmds.o operatorcmds.o opclasscmds.o \ portalcmds.o prepare.o proclang.o \ schemacmds.o seclabel.o sequence.o tablecmds.o tablespace.o trigger.o \ diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c index 1c6ae0243e..99fdd7dba3 100644 --- a/src/backend/commands/alter.c +++ b/src/backend/commands/alter.c @@ -20,9 +20,11 @@ #include "catalog/pg_largeobject.h" #include "catalog/pg_namespace.h" #include "commands/alter.h" +#include "commands/collationcmds.h" #include "commands/conversioncmds.h" #include "commands/dbcommands.h" #include "commands/defrem.h" +#include "commands/extension.h" #include "commands/proclang.h" #include "commands/schemacmds.h" #include "commands/tablecmds.h" @@ -52,6 +54,10 @@ ExecRenameStmt(RenameStmt *stmt) RenameAggregate(stmt->object, stmt->objarg, stmt->newname); break; + case OBJECT_COLLATION: + RenameCollation(stmt->object, stmt->newname); + break; + case OBJECT_CONVERSION: RenameConversion(stmt->object, stmt->newname); break; @@ -184,10 +190,18 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt) stmt->newschema); break; + case OBJECT_COLLATION: + AlterCollationNamespace(stmt->object, stmt->newschema); + break; + case OBJECT_CONVERSION: AlterConversionNamespace(stmt->object, stmt->newschema); break; + case OBJECT_EXTENSION: + AlterExtensionNamespace(stmt->object, stmt->newschema); + break; + case OBJECT_FUNCTION: AlterFunctionNamespace(stmt->object, stmt->objarg, false, stmt->newschema); @@ -242,87 +256,208 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt) } /* + * Change an object's namespace given its classOid and object Oid. + * + * Objects that don't have a namespace should be ignored. + * + * This function is currently used only by ALTER EXTENSION SET SCHEMA, + * so it only needs to cover object types that can be members of an + * extension, and it doesn't have to deal with certain special cases + * such as not wanting to process array types --- those should never + * be direct members of an extension anyway. + * + * Returns the OID of the object's previous namespace, or InvalidOid if + * object doesn't have a schema. + */ +Oid +AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid) +{ + Oid oldNspOid = InvalidOid; + ObjectAddress dep; + + dep.classId = classId; + dep.objectId = objid; + dep.objectSubId = 0; + + switch (getObjectClass(&dep)) + { + case OCLASS_CLASS: + { + Relation rel; + Relation classRel; + + rel = relation_open(objid, AccessExclusiveLock); + oldNspOid = RelationGetNamespace(rel); + + classRel = heap_open(RelationRelationId, RowExclusiveLock); + + AlterRelationNamespaceInternal(classRel, + objid, + oldNspOid, + nspOid, + true); + + heap_close(classRel, RowExclusiveLock); + + relation_close(rel, NoLock); + break; + } + + case OCLASS_PROC: + oldNspOid = AlterFunctionNamespace_oid(objid, nspOid); + break; + + case OCLASS_TYPE: + oldNspOid = AlterTypeNamespace_oid(objid, nspOid); + break; + + case OCLASS_COLLATION: + oldNspOid = AlterCollationNamespace_oid(objid, nspOid); + break; + + case OCLASS_CONVERSION: + oldNspOid = AlterConversionNamespace_oid(objid, nspOid); + break; + + case OCLASS_OPERATOR: + oldNspOid = AlterOperatorNamespace_oid(objid, nspOid); + break; + + case OCLASS_OPCLASS: + oldNspOid = AlterOpClassNamespace_oid(objid, nspOid); + break; + + case OCLASS_OPFAMILY: + oldNspOid = AlterOpFamilyNamespace_oid(objid, nspOid); + break; + + case OCLASS_TSPARSER: + oldNspOid = AlterTSParserNamespace_oid(objid, nspOid); + break; + + case OCLASS_TSDICT: + oldNspOid = AlterTSDictionaryNamespace_oid(objid, nspOid); + break; + + case OCLASS_TSTEMPLATE: + oldNspOid = AlterTSTemplateNamespace_oid(objid, nspOid); + break; + + case OCLASS_TSCONFIG: + oldNspOid = AlterTSConfigurationNamespace_oid(objid, nspOid); + break; + + default: + break; + } + + return oldNspOid; +} + +/* * Generic function to change the namespace of a given object, for simple - * cases (won't work for tables or functions, objects which have more than 2 - * key-attributes to use when searching for their syscache entries --- we - * don't want nor need to get this generic here). + * cases (won't work for tables, nor other cases where we need to do more + * than change the namespace column of a single catalog entry). * * The AlterFooNamespace() calls just above will call a function whose job * is to lookup the arguments for the generic function here. * - * Relation must already by open, it's the responsibility of the caller to - * close it. + * rel: catalog relation containing object (RowExclusiveLock'd by caller) + * oidCacheId: syscache that indexes this catalog by OID + * nameCacheId: syscache that indexes this catalog by name and namespace + * (pass -1 if there is none) + * objid: OID of object to change the namespace of + * nspOid: OID of new namespace + * Anum_name: column number of catalog's name column + * Anum_namespace: column number of catalog's namespace column + * Anum_owner: column number of catalog's owner column, or -1 if none + * acl_kind: ACL type for object, or -1 if none assigned + * + * If the object does not have an owner or permissions, pass -1 for + * Anum_owner and acl_kind. In this case the calling user must be superuser. + * + * Returns the OID of the object's previous namespace. */ -void -AlterObjectNamespace(Relation rel, int cacheId, - Oid classId, Oid objid, Oid nspOid, +Oid +AlterObjectNamespace(Relation rel, int oidCacheId, int nameCacheId, + Oid objid, Oid nspOid, int Anum_name, int Anum_namespace, int Anum_owner, - AclObjectKind acl_kind, - bool superuser_only) + AclObjectKind acl_kind) { + Oid classId = RelationGetRelid(rel); Oid oldNspOid; Datum name, namespace; bool isnull; - HeapTuple tup, newtup = NULL; + HeapTuple tup, newtup; Datum *values; bool *nulls; bool *replaces; - tup = SearchSysCacheCopy1(cacheId, ObjectIdGetDatum(objid)); + tup = SearchSysCacheCopy1(oidCacheId, ObjectIdGetDatum(objid)); if (!HeapTupleIsValid(tup)) /* should not happen */ - elog(ERROR, "cache lookup failed for object %u: %s", - objid, getObjectDescriptionOids(classId, objid)); + elog(ERROR, "cache lookup failed for object %u of catalog \"%s\"", + objid, RelationGetRelationName(rel)); - name = heap_getattr(tup, Anum_name, rel->rd_att, &isnull); - namespace = heap_getattr(tup, Anum_namespace, rel->rd_att, &isnull); + name = heap_getattr(tup, Anum_name, RelationGetDescr(rel), &isnull); + Assert(!isnull); + namespace = heap_getattr(tup, Anum_namespace, RelationGetDescr(rel), &isnull); + Assert(!isnull); oldNspOid = DatumGetObjectId(namespace); /* Check basic namespace related issues */ CheckSetNamespace(oldNspOid, nspOid, classId, objid); - /* check for duplicate name (more friendly than unique-index failure) */ - if (SearchSysCacheExists2(cacheId, name, ObjectIdGetDatum(nspOid))) - ereport(ERROR, - (errcode(ERRCODE_DUPLICATE_OBJECT), - errmsg("%s already exists in schema \"%s\"", - getObjectDescriptionOids(classId, objid), - get_namespace_name(nspOid)))); - - /* Superusers can always do it */ + /* Permission checks ... superusers can always do it */ if (!superuser()) { Datum owner; Oid ownerId; AclResult aclresult; - if (superuser_only) + /* Fail if object does not have an explicit owner */ + if (Anum_owner <= 0) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("must be superuser to SET SCHEMA of %s", getObjectDescriptionOids(classId, objid))))); /* Otherwise, must be owner of the existing object */ - owner = heap_getattr(tup, Anum_owner, rel->rd_att, &isnull); + owner = heap_getattr(tup, Anum_owner, RelationGetDescr(rel), &isnull); + Assert(!isnull); ownerId = DatumGetObjectId(owner); if (!has_privs_of_role(GetUserId(), ownerId)) aclcheck_error(ACLCHECK_NOT_OWNER, acl_kind, NameStr(*(DatumGetName(name)))); - /* owner must have CREATE privilege on namespace */ - aclresult = pg_namespace_aclcheck(oldNspOid, GetUserId(), ACL_CREATE); + /* User must have CREATE privilege on new namespace */ + aclresult = pg_namespace_aclcheck(nspOid, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, - get_namespace_name(oldNspOid)); + get_namespace_name(nspOid)); } - /* Prepare to update tuple */ - values = palloc0(rel->rd_att->natts * sizeof(Datum)); - nulls = palloc0(rel->rd_att->natts * sizeof(bool)); - replaces = palloc0(rel->rd_att->natts * sizeof(bool)); - values[Anum_namespace - 1] = nspOid; + /* + * Check for duplicate name (more friendly than unique-index failure). + * Since this is just a friendliness check, we can just skip it in cases + * where there isn't a suitable syscache available. + */ + if (nameCacheId >= 0 && + SearchSysCacheExists2(nameCacheId, name, ObjectIdGetDatum(nspOid))) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("%s already exists in schema \"%s\"", + getObjectDescriptionOids(classId, objid), + get_namespace_name(nspOid)))); + + /* Build modified tuple */ + values = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(Datum)); + nulls = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool)); + replaces = palloc0(RelationGetNumberOfAttributes(rel) * sizeof(bool)); + values[Anum_namespace - 1] = ObjectIdGetDatum(nspOid); replaces[Anum_namespace - 1] = true; - newtup = heap_modify_tuple(tup, rel->rd_att, values, nulls, replaces); + newtup = heap_modify_tuple(tup, RelationGetDescr(rel), + values, nulls, replaces); /* Perform actual update */ simple_heap_update(rel, &tup->t_self, newtup); @@ -336,6 +471,8 @@ AlterObjectNamespace(Relation rel, int cacheId, /* update dependencies to point to the new schema */ changeDependencyFor(classId, objid, NamespaceRelationId, oldNspOid, nspOid); + + return oldNspOid; } @@ -354,6 +491,10 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt) AlterAggregateOwner(stmt->object, stmt->objarg, newowner); break; + case OBJECT_COLLATION: + AlterCollationOwner(stmt->object, newowner); + break; + case OBJECT_CONVERSION: AlterConversionOwner(stmt->object, newowner); break; diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 4c106dd8c5..bafdc80d58 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -862,11 +862,13 @@ examine_attribute(Relation onerel, int attnum, Node *index_expr) { stats->attrtypid = exprType(index_expr); stats->attrtypmod = exprTypmod(index_expr); + stats->attrcollation = exprCollation(index_expr); } else { stats->attrtypid = attr->atttypid; stats->attrtypmod = attr->atttypmod; + stats->attrcollation = attr->attcollation; } typtuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(stats->attrtypid)); @@ -1929,6 +1931,7 @@ compute_minimal_stats(VacAttrStatsP stats, track_cnt = 0; fmgr_info(mystats->eqfunc, &f_cmpeq); + fmgr_info_collation(stats->attrcollation, &f_cmpeq); for (i = 0; i < samplerows; i++) { @@ -2250,6 +2253,7 @@ compute_scalar_stats(VacAttrStatsP stats, SelectSortFunction(mystats->ltopr, false, &cmpFn, &cmpFlags); fmgr_info(cmpFn, &f_cmpfn); + fmgr_info_collation(stats->attrcollation, &f_cmpfn); /* Initial scan to find sortable values */ for (i = 0; i < samplerows; i++) diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index 59a439413e..4c4f356e79 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -1277,7 +1277,8 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class, if (relform1->reltoastrelid) { count = deleteDependencyRecordsFor(RelationRelationId, - relform1->reltoastrelid); + relform1->reltoastrelid, + false); if (count != 1) elog(ERROR, "expected one dependency record for TOAST table, found %ld", count); @@ -1285,7 +1286,8 @@ swap_relation_files(Oid r1, Oid r2, bool target_is_pg_class, if (relform2->reltoastrelid) { count = deleteDependencyRecordsFor(RelationRelationId, - relform2->reltoastrelid); + relform2->reltoastrelid, + false); if (count != 1) elog(ERROR, "expected one dependency record for TOAST table, found %ld", count); diff --git a/src/backend/commands/collationcmds.c b/src/backend/commands/collationcmds.c new file mode 100644 index 0000000000..6db72d919c --- /dev/null +++ b/src/backend/commands/collationcmds.c @@ -0,0 +1,401 @@ +/*------------------------------------------------------------------------- + * + * collationcmds.c + * collation creation command support code + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/commands/collationcmds.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/heapam.h" +#include "catalog/dependency.h" +#include "catalog/indexing.h" +#include "catalog/namespace.h" +#include "catalog/pg_collation.h" +#include "catalog/pg_collation_fn.h" +#include "commands/alter.h" +#include "commands/collationcmds.h" +#include "commands/dbcommands.h" +#include "commands/defrem.h" +#include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "parser/parse_type.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/syscache.h" + +static void AlterCollationOwner_internal(Relation rel, Oid collationOid, + Oid newOwnerId); + +/* + * CREATE COLLATION + */ +void +DefineCollation(List *names, List *parameters) +{ + char *collName; + Oid collNamespace; + AclResult aclresult; + ListCell *pl; + DefElem *fromEl = NULL; + DefElem *localeEl = NULL; + DefElem *lccollateEl = NULL; + DefElem *lcctypeEl = NULL; + char *collcollate = NULL; + char *collctype = NULL; + + collNamespace = QualifiedNameGetCreationNamespace(names, &collName); + + aclresult = pg_namespace_aclcheck(collNamespace, GetUserId(), ACL_CREATE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(collNamespace)); + + foreach(pl, parameters) + { + DefElem *defel = (DefElem *) lfirst(pl); + DefElem **defelp; + + if (pg_strcasecmp(defel->defname, "from") == 0) + defelp = &fromEl; + else if (pg_strcasecmp(defel->defname, "locale") == 0) + defelp = &localeEl; + else if (pg_strcasecmp(defel->defname, "lc_collate") == 0) + defelp = &lccollateEl; + else if (pg_strcasecmp(defel->defname, "lc_ctype") == 0) + defelp = &lcctypeEl; + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("collation attribute \"%s\" not recognized", + defel->defname))); + break; + } + + *defelp = defel; + } + + if ((localeEl && (lccollateEl || lcctypeEl)) + || (fromEl && list_length(parameters) != 1)) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + + if (fromEl) + { + Oid collid; + HeapTuple tp; + + collid = LookupCollation(NULL, defGetQualifiedName(fromEl), -1); + tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid)); + if (!HeapTupleIsValid(tp)) + elog(ERROR, "cache lookup failed for collation %u", collid); + + collcollate = pstrdup(NameStr(((Form_pg_collation) GETSTRUCT(tp))->collcollate)); + collctype = pstrdup(NameStr(((Form_pg_collation) GETSTRUCT(tp))->collctype)); + + ReleaseSysCache(tp); + } + + if (localeEl) + { + collcollate = defGetString(localeEl); + collctype = defGetString(localeEl); + } + + if (lccollateEl) + collcollate = defGetString(lccollateEl); + + if (lcctypeEl) + collctype = defGetString(lcctypeEl); + + if (!collcollate) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("parameter \"lc_collate\" parameter must be specified"))); + + if (!collctype) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("parameter \"lc_ctype\" must be specified"))); + + check_encoding_locale_matches(GetDatabaseEncoding(), collcollate, collctype); + + CollationCreate(collName, + collNamespace, + GetUserId(), + GetDatabaseEncoding(), + collcollate, + collctype); +} + +/* + * DROP COLLATION + */ +void +DropCollationsCommand(DropStmt *drop) +{ + ObjectAddresses *objects; + ListCell *cell; + + /* + * First we identify all the collations, then we delete them in a single + * performMultipleDeletions() call. This is to avoid unwanted DROP + * RESTRICT errors if one of the collations depends on another. (Not that + * that is very likely, but we may as well do this consistently.) + */ + objects = new_object_addresses(); + + foreach(cell, drop->objects) + { + List *name = (List *) lfirst(cell); + Oid collationOid; + HeapTuple tuple; + Form_pg_collation coll; + ObjectAddress object; + + collationOid = get_collation_oid(name, drop->missing_ok); + + if (!OidIsValid(collationOid)) + { + ereport(NOTICE, + (errmsg("collation \"%s\" does not exist, skipping", + NameListToString(name)))); + continue; + } + + tuple = SearchSysCache1(COLLOID, ObjectIdGetDatum(collationOid)); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "cache lookup failed for collation %u", + collationOid); + coll = (Form_pg_collation) GETSTRUCT(tuple); + + /* Permission check: must own collation or its namespace */ + if (!pg_collation_ownercheck(collationOid, GetUserId()) && + !pg_namespace_ownercheck(coll->collnamespace, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_COLLATION, + NameStr(coll->collname)); + + object.classId = CollationRelationId; + object.objectId = collationOid; + object.objectSubId = 0; + + add_exact_object_address(&object, objects); + + ReleaseSysCache(tuple); + } + + performMultipleDeletions(objects, drop->behavior); + + free_object_addresses(objects); +} + +/* + * Rename collation + */ +void +RenameCollation(List *name, const char *newname) +{ + Oid collationOid; + Oid namespaceOid; + HeapTuple tup; + Relation rel; + AclResult aclresult; + + rel = heap_open(CollationRelationId, RowExclusiveLock); + + collationOid = get_collation_oid(name, false); + + tup = SearchSysCacheCopy1(COLLOID, ObjectIdGetDatum(collationOid)); + if (!HeapTupleIsValid(tup)) /* should not happen */ + elog(ERROR, "cache lookup failed for collation %u", collationOid); + + namespaceOid = ((Form_pg_collation) GETSTRUCT(tup))->collnamespace; + + /* make sure the new name doesn't exist */ + if (SearchSysCacheExists3(COLLNAMEENCNSP, + CStringGetDatum(newname), + Int32GetDatum(GetDatabaseEncoding()), + ObjectIdGetDatum(namespaceOid))) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("collation \"%s\" for current database encoding \"%s\" already exists in schema \"%s\"", + newname, + GetDatabaseEncodingName(), + get_namespace_name(namespaceOid)))); + + /* must be owner */ + if (!pg_collation_ownercheck(collationOid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_COLLATION, + NameListToString(name)); + + /* must have CREATE privilege on namespace */ + aclresult = pg_namespace_aclcheck(namespaceOid, GetUserId(), ACL_CREATE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(namespaceOid)); + + /* rename */ + namestrcpy(&(((Form_pg_collation) GETSTRUCT(tup))->collname), newname); + simple_heap_update(rel, &tup->t_self, tup); + CatalogUpdateIndexes(rel, tup); + + heap_close(rel, NoLock); + heap_freetuple(tup); +} + +/* + * Change collation owner, by name + */ +void +AlterCollationOwner(List *name, Oid newOwnerId) +{ + Oid collationOid; + Relation rel; + + rel = heap_open(CollationRelationId, RowExclusiveLock); + + collationOid = get_collation_oid(name, false); + + AlterCollationOwner_internal(rel, collationOid, newOwnerId); + + heap_close(rel, NoLock); +} + +/* + * Change collation owner, by oid + */ +void +AlterCollationOwner_oid(Oid collationOid, Oid newOwnerId) +{ + Relation rel; + + rel = heap_open(CollationRelationId, RowExclusiveLock); + + AlterCollationOwner_internal(rel, collationOid, newOwnerId); + + heap_close(rel, NoLock); +} + +/* + * AlterCollationOwner_internal + * + * Internal routine for changing the owner. rel must be pg_collation, already + * open and suitably locked; it will not be closed. + */ +static void +AlterCollationOwner_internal(Relation rel, Oid collationOid, Oid newOwnerId) +{ + Form_pg_collation collForm; + HeapTuple tup; + + Assert(RelationGetRelid(rel) == CollationRelationId); + + tup = SearchSysCacheCopy1(COLLOID, ObjectIdGetDatum(collationOid)); + if (!HeapTupleIsValid(tup)) /* should not happen */ + elog(ERROR, "cache lookup failed for collation %u", collationOid); + + collForm = (Form_pg_collation) GETSTRUCT(tup); + + /* + * If the new owner is the same as the existing owner, consider the + * command to have succeeded. This is for dump restoration purposes. + */ + if (collForm->collowner != newOwnerId) + { + AclResult aclresult; + + /* Superusers can always do it */ + if (!superuser()) + { + /* Otherwise, must be owner of the existing object */ + if (!pg_collation_ownercheck(HeapTupleGetOid(tup), GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_COLLATION, + NameStr(collForm->collname)); + + /* Must be able to become new owner */ + check_is_member_of_role(GetUserId(), newOwnerId); + + /* New owner must have CREATE privilege on namespace */ + aclresult = pg_namespace_aclcheck(collForm->collnamespace, + newOwnerId, + ACL_CREATE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(collForm->collnamespace)); + } + + /* + * Modify the owner --- okay to scribble on tup because it's a copy + */ + collForm->collowner = newOwnerId; + + simple_heap_update(rel, &tup->t_self, tup); + + CatalogUpdateIndexes(rel, tup); + + /* Update owner dependency reference */ + changeDependencyOnOwner(CollationRelationId, collationOid, + newOwnerId); + } + + heap_freetuple(tup); +} + +/* + * Execute ALTER COLLATION SET SCHEMA + */ +void +AlterCollationNamespace(List *name, const char *newschema) +{ + Oid collOid, nspOid; + Relation rel; + + rel = heap_open(CollationRelationId, RowExclusiveLock); + + collOid = get_collation_oid(name, false); + + /* get schema OID */ + nspOid = LookupCreationNamespace(newschema); + + AlterObjectNamespace(rel, COLLOID, -1, + collOid, nspOid, + Anum_pg_collation_collname, + Anum_pg_collation_collnamespace, + Anum_pg_collation_collowner, + ACL_KIND_COLLATION); + + heap_close(rel, NoLock); +} + +/* + * Change collation schema, by oid + */ +Oid +AlterCollationNamespace_oid(Oid collOid, Oid newNspOid) +{ + Oid oldNspOid; + Relation rel; + + rel = heap_open(CollationRelationId, RowExclusiveLock); + + oldNspOid = AlterObjectNamespace(rel, COLLOID, -1, + collOid, newNspOid, + Anum_pg_collation_collname, + Anum_pg_collation_collnamespace, + Anum_pg_collation_collowner, + ACL_KIND_COLLATION); + + heap_close(rel, RowExclusiveLock); + + return oldNspOid; +} diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c index 2e8c4df927..a0a561c144 100644 --- a/src/backend/commands/comment.c +++ b/src/backend/commands/comment.c @@ -105,6 +105,7 @@ CommentObject(CommentStmt *stmt) strVal(linitial(stmt->objname))); break; case OBJECT_TYPE: + case OBJECT_DOMAIN: if (!pg_type_ownercheck(address.objectId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, format_type_be(address.objectId)); @@ -132,6 +133,11 @@ CommentObject(CommentStmt *stmt) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_NAMESPACE, strVal(linitial(stmt->objname))); break; + case OBJECT_COLLATION: + if (!pg_collation_ownercheck(address.objectId, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_COLLATION, + NameListToString(stmt->objname)); + break; case OBJECT_CONVERSION: if (!pg_conversion_ownercheck(address.objectId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CONVERSION, @@ -143,6 +149,12 @@ CommentObject(CommentStmt *stmt) (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to comment on procedural language"))); break; + case OBJECT_EXTENSION: + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to comment on extension"))); + break; case OBJECT_OPCLASS: if (!pg_opclass_ownercheck(address.objectId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_OPCLASS, diff --git a/src/backend/commands/conversioncmds.c b/src/backend/commands/conversioncmds.c index da024df5f0..b5e4420ca8 100644 --- a/src/backend/commands/conversioncmds.c +++ b/src/backend/commands/conversioncmds.c @@ -345,12 +345,35 @@ AlterConversionNamespace(List *name, const char *newschema) /* get schema OID */ nspOid = LookupCreationNamespace(newschema); - AlterObjectNamespace(rel, CONVOID, ConversionRelationId, convOid, nspOid, + AlterObjectNamespace(rel, CONVOID, CONNAMENSP, + convOid, nspOid, Anum_pg_conversion_conname, Anum_pg_conversion_connamespace, Anum_pg_conversion_conowner, - ACL_KIND_CONVERSION, - false); + ACL_KIND_CONVERSION); - heap_close(rel, NoLock); + heap_close(rel, RowExclusiveLock); +} + +/* + * Change conversion schema, by oid + */ +Oid +AlterConversionNamespace_oid(Oid convOid, Oid newNspOid) +{ + Oid oldNspOid; + Relation rel; + + rel = heap_open(ConversionRelationId, RowExclusiveLock); + + oldNspOid = AlterObjectNamespace(rel, CONVOID, CONNAMENSP, + convOid, newNspOid, + Anum_pg_conversion_conname, + Anum_pg_conversion_connamespace, + Anum_pg_conversion_conowner, + ACL_KIND_CONVERSION); + + heap_close(rel, RowExclusiveLock); + + return oldNspOid; } diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 3350ca0b6e..9f7263d59a 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -93,13 +93,11 @@ typedef struct CopyStateData FILE *copy_file; /* used if copy_dest == COPY_FILE */ StringInfo fe_msgbuf; /* used for all dests during COPY TO, only for * dest == COPY_NEW_FE in COPY FROM */ - bool fe_copy; /* true for all FE copy dests */ bool fe_eof; /* true if detected end of copy data */ EolType eol_type; /* EOL type of input */ int client_encoding; /* remote side's character encoding */ bool need_transcoding; /* client encoding diff from server? */ bool encoding_embeds_ascii; /* ASCII can be non-first byte? */ - uint64 processed; /* # of tuples processed */ /* parameters from the COPY command */ Relation rel; /* relation to copy to or from */ @@ -119,19 +117,36 @@ typedef struct CopyStateData bool *force_quote_flags; /* per-column CSV FQ flags */ bool *force_notnull_flags; /* per-column CSV FNN flags */ - /* these are just for error messages, see copy_in_error_callback */ + /* these are just for error messages, see CopyFromErrorCallback */ const char *cur_relname; /* table name for error messages */ int cur_lineno; /* line number for error messages */ const char *cur_attname; /* current att for error messages */ const char *cur_attval; /* current att value for error messages */ /* + * Working state for COPY TO/FROM + */ + MemoryContext copycontext; /* per-copy execution context */ + + /* * Working state for COPY TO */ FmgrInfo *out_functions; /* lookup info for output functions */ MemoryContext rowcontext; /* per-row evaluation context */ /* + * Working state for COPY FROM + */ + AttrNumber num_defaults; + bool file_has_oids; + FmgrInfo oid_in_function; + Oid oid_typioparam; + FmgrInfo *in_functions; /* array of input functions for each attrs */ + Oid *typioparams; /* array of element types for in_functions */ + int *defmap; /* array of default att numbers */ + ExprState **defexprs; /* array of default att expressions */ + + /* * These variables are used to reduce overhead in textual COPY FROM. * * attribute_buf holds the separated, de-escaped text for each field of @@ -169,13 +184,12 @@ typedef struct CopyStateData int raw_buf_len; /* total # of bytes stored */ } CopyStateData; -typedef CopyStateData *CopyState; - /* DestReceiver for COPY (SELECT) TO */ typedef struct { DestReceiver pub; /* publicly-known function pointers */ CopyState cstate; /* CopyStateData for the command */ + uint64 processed; /* # of tuples processed */ } DR_copy; @@ -248,11 +262,17 @@ static const char BinarySignature[11] = "PGCOPY\n\377\r\n\0"; /* non-export function prototypes */ -static void DoCopyTo(CopyState cstate); -static void CopyTo(CopyState cstate); +static CopyState BeginCopy(bool is_from, Relation rel, Node *raw_query, + const char *queryString, List *attnamelist, List *options); +static void EndCopy(CopyState cstate); +static CopyState BeginCopyTo(Relation rel, Node *query, const char *queryString, + const char *filename, List *attnamelist, List *options); +static void EndCopyTo(CopyState cstate); +static uint64 DoCopyTo(CopyState cstate); +static uint64 CopyTo(CopyState cstate); static void CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls); -static void CopyFrom(CopyState cstate); +static uint64 CopyFrom(CopyState cstate); static bool CopyReadLine(CopyState cstate); static bool CopyReadLineText(CopyState cstate); static int CopyReadAttributesText(CopyState cstate); @@ -700,6 +720,102 @@ CopyLoadRawBuf(CopyState cstate) * input/output stream. The latter could be either stdin/stdout or a * socket, depending on whether we're running under Postmaster control. * + * Do not allow a Postgres user without superuser privilege to read from + * or write to a file. + * + * Do not allow the copy if user doesn't have proper permission to access + * the table or the specifically requested columns. + */ +uint64 +DoCopy(const CopyStmt *stmt, const char *queryString) +{ + CopyState cstate; + bool is_from = stmt->is_from; + bool pipe = (stmt->filename == NULL); + Relation rel; + uint64 processed; + + /* Disallow file COPY except to superusers. */ + if (!pipe && !superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to COPY to or from a file"), + errhint("Anyone can COPY to stdout or from stdin. " + "psql's \\copy command also works for anyone."))); + + if (stmt->relation) + { + TupleDesc tupDesc; + AclMode required_access = (is_from ? ACL_INSERT : ACL_SELECT); + RangeTblEntry *rte; + List *attnums; + ListCell *cur; + + Assert(!stmt->query); + + /* Open and lock the relation, using the appropriate lock type. */ + rel = heap_openrv(stmt->relation, + (is_from ? RowExclusiveLock : AccessShareLock)); + + rte = makeNode(RangeTblEntry); + rte->rtekind = RTE_RELATION; + rte->relid = RelationGetRelid(rel); + rte->requiredPerms = required_access; + + tupDesc = RelationGetDescr(rel); + attnums = CopyGetAttnums(tupDesc, rel, stmt->attlist); + foreach (cur, attnums) + { + int attno = lfirst_int(cur) - + FirstLowInvalidHeapAttributeNumber; + + if (is_from) + rte->modifiedCols = bms_add_member(rte->modifiedCols, attno); + else + rte->selectedCols = bms_add_member(rte->selectedCols, attno); + } + ExecCheckRTPerms(list_make1(rte), true); + } + else + { + Assert(stmt->query); + + rel = NULL; + } + + if (is_from) + { + /* check read-only transaction */ + if (XactReadOnly && rel->rd_backend != MyBackendId) + PreventCommandIfReadOnly("COPY FROM"); + + cstate = BeginCopyFrom(rel, stmt->filename, + stmt->attlist, stmt->options); + processed = CopyFrom(cstate); /* copy from file to database */ + EndCopyFrom(cstate); + } + else + { + cstate = BeginCopyTo(rel, stmt->query, queryString, stmt->filename, + stmt->attlist, stmt->options); + processed = DoCopyTo(cstate); /* copy from database to file */ + EndCopyTo(cstate); + } + + /* + * Close the relation. If reading, we can release the AccessShareLock we + * got; if writing, we should hold the lock until end of transaction to + * ensure that updates will be committed before lock is released. + */ + if (rel != NULL) + heap_close(rel, (is_from ? NoLock : AccessShareLock)); + + return processed; +} + +/* + * Common setup routines used by BeginCopyFrom and BeginCopyTo. + * * Iff <binary>, unload or reload in the binary format, as opposed to the * more wasteful but more robust and portable text format. * @@ -711,35 +827,42 @@ CopyLoadRawBuf(CopyState cstate) * * If in the text format, delimit columns with delimiter <delim> and print * NULL values as <null_print>. - * - * Do not allow a Postgres user without superuser privilege to read from - * or write to a file. - * - * Do not allow the copy if user doesn't have proper permission to access - * the table or the specifically requested columns. */ -uint64 -DoCopy(const CopyStmt *stmt, const char *queryString) +static CopyState +BeginCopy(bool is_from, + Relation rel, + Node *raw_query, + const char *queryString, + List *attnamelist, + List *options) { CopyState cstate; - bool is_from = stmt->is_from; - bool pipe = (stmt->filename == NULL); - List *attnamelist = stmt->attlist; List *force_quote = NIL; List *force_notnull = NIL; bool force_quote_all = false; bool format_specified = false; - AclMode required_access = (is_from ? ACL_INSERT : ACL_SELECT); ListCell *option; TupleDesc tupDesc; int num_phys_attrs; - uint64 processed; + MemoryContext oldcontext; /* Allocate workspace and zero all fields */ cstate = (CopyStateData *) palloc0(sizeof(CopyStateData)); + /* + * We allocate everything used by a cstate in a new memory context. + * This would avoid memory leaks repeated uses of COPY in a query. + */ + cstate->copycontext = AllocSetContextCreate(CurrentMemoryContext, + "COPY", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + + oldcontext = MemoryContextSwitchTo(cstate->copycontext); + /* Extract options from the statement node tree */ - foreach(option, stmt->options) + foreach(option, options) { DefElem *defel = (DefElem *) lfirst(option); @@ -980,51 +1103,14 @@ DoCopy(const CopyStmt *stmt, const char *queryString) (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("CSV quote character must not appear in the NULL specification"))); - /* Disallow file COPY except to superusers. */ - if (!pipe && !superuser()) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("must be superuser to COPY to or from a file"), - errhint("Anyone can COPY to stdout or from stdin. " - "psql's \\copy command also works for anyone."))); - - if (stmt->relation) + if (rel) { - RangeTblEntry *rte; - List *attnums; - ListCell *cur; + Assert(!raw_query); - Assert(!stmt->query); - cstate->queryDesc = NULL; - - /* Open and lock the relation, using the appropriate lock type. */ - cstate->rel = heap_openrv(stmt->relation, - (is_from ? RowExclusiveLock : AccessShareLock)); + cstate->rel = rel; tupDesc = RelationGetDescr(cstate->rel); - /* Check relation permissions. */ - rte = makeNode(RangeTblEntry); - rte->rtekind = RTE_RELATION; - rte->relid = RelationGetRelid(cstate->rel); - rte->requiredPerms = required_access; - - attnums = CopyGetAttnums(tupDesc, cstate->rel, attnamelist); - foreach (cur, attnums) - { - int attno = lfirst_int(cur) - FirstLowInvalidHeapAttributeNumber; - - if (is_from) - rte->modifiedCols = bms_add_member(rte->modifiedCols, attno); - else - rte->selectedCols = bms_add_member(rte->selectedCols, attno); - } - ExecCheckRTPerms(list_make1(rte), true); - - /* check read-only transaction */ - if (XactReadOnly && is_from && cstate->rel->rd_backend != MyBackendId) - PreventCommandIfReadOnly("COPY FROM"); - /* Don't allow COPY w/ OIDs to or from a table without them */ if (cstate->oids && !cstate->rel->rd_rel->relhasoids) ereport(ERROR, @@ -1058,7 +1144,7 @@ DoCopy(const CopyStmt *stmt, const char *queryString) * function and is executed repeatedly. (See also the same hack in * DECLARE CURSOR and PREPARE.) XXX FIXME someday. */ - rewritten = pg_analyze_and_rewrite((Node *) copyObject(stmt->query), + rewritten = pg_analyze_and_rewrite((Node *) copyObject(raw_query), queryString, NULL, 0); /* We don't expect more or less than one result query */ @@ -1160,14 +1246,6 @@ DoCopy(const CopyStmt *stmt, const char *queryString) } } - /* Set up variables to avoid per-attribute overhead. */ - initStringInfo(&cstate->attribute_buf); - initStringInfo(&cstate->line_buf); - cstate->line_buf_converted = false; - cstate->raw_buf = (char *) palloc(RAW_BUF_SIZE + 1); - cstate->raw_buf_index = cstate->raw_buf_len = 0; - cstate->processed = 0; - /* * Set up encoding conversion info. Even if the client and server * encodings are the same, we must apply pg_client_to_server() to validate @@ -1181,84 +1259,75 @@ DoCopy(const CopyStmt *stmt, const char *queryString) cstate->encoding_embeds_ascii = PG_ENCODING_IS_CLIENT_ONLY(cstate->client_encoding); cstate->copy_dest = COPY_FILE; /* default */ - cstate->filename = stmt->filename; - if (is_from) - CopyFrom(cstate); /* copy from file to database */ - else - DoCopyTo(cstate); /* copy from database to file */ + MemoryContextSwitchTo(oldcontext); - /* - * Close the relation or query. If reading, we can release the - * AccessShareLock we got; if writing, we should hold the lock until end - * of transaction to ensure that updates will be committed before lock is - * released. - */ - if (cstate->rel) - heap_close(cstate->rel, (is_from ? NoLock : AccessShareLock)); - else - { - /* Close down the query and free resources. */ - ExecutorEnd(cstate->queryDesc); - FreeQueryDesc(cstate->queryDesc); - PopActiveSnapshot(); - } + return cstate; +} - /* Clean up storage (probably not really necessary) */ - processed = cstate->processed; +/* + * Release resources allocated in a cstate for COPY TO/FROM. + */ +static void +EndCopy(CopyState cstate) +{ + if (cstate->filename != NULL && FreeFile(cstate->copy_file)) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not close file \"%s\": %m", + cstate->filename))); - pfree(cstate->attribute_buf.data); - pfree(cstate->line_buf.data); - pfree(cstate->raw_buf); + MemoryContextDelete(cstate->copycontext); pfree(cstate); - - return processed; } - /* - * This intermediate routine exists mainly to localize the effects of setjmp - * so we don't need to plaster a lot of variables with "volatile". + * Setup CopyState to read tuples from a table or a query for COPY TO. */ -static void -DoCopyTo(CopyState cstate) +static CopyState +BeginCopyTo(Relation rel, + Node *query, + const char *queryString, + const char *filename, + List *attnamelist, + List *options) { - bool pipe = (cstate->filename == NULL); + CopyState cstate; + bool pipe = (filename == NULL); + MemoryContext oldcontext; - if (cstate->rel) + if (rel != NULL && rel->rd_rel->relkind != RELKIND_RELATION) { - if (cstate->rel->rd_rel->relkind != RELKIND_RELATION) - { - if (cstate->rel->rd_rel->relkind == RELKIND_VIEW) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot copy from view \"%s\"", - RelationGetRelationName(cstate->rel)), - errhint("Try the COPY (SELECT ...) TO variant."))); - else if (cstate->rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot copy from foreign table \"%s\"", - RelationGetRelationName(cstate->rel)), - errhint("Try the COPY (SELECT ...) TO variant."))); - else if (cstate->rel->rd_rel->relkind == RELKIND_SEQUENCE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot copy from sequence \"%s\"", - RelationGetRelationName(cstate->rel)))); - else - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot copy from non-table relation \"%s\"", - RelationGetRelationName(cstate->rel)))); - } + if (rel->rd_rel->relkind == RELKIND_VIEW) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot copy from view \"%s\"", + RelationGetRelationName(rel)), + errhint("Try the COPY (SELECT ...) TO variant."))); + else if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot copy from foreign table \"%s\"", + RelationGetRelationName(rel)), + errhint("Try the COPY (SELECT ...) TO variant."))); + else if (rel->rd_rel->relkind == RELKIND_SEQUENCE) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot copy from sequence \"%s\"", + RelationGetRelationName(rel)))); + else + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot copy from non-table relation \"%s\"", + RelationGetRelationName(rel)))); } + cstate = BeginCopy(false, rel, query, queryString, attnamelist, options); + oldcontext = MemoryContextSwitchTo(cstate->copycontext); + if (pipe) { - if (whereToSendOutput == DestRemote) - cstate->fe_copy = true; - else + if (whereToSendOutput != DestRemote) cstate->copy_file = stdout; } else @@ -1270,11 +1339,12 @@ DoCopyTo(CopyState cstate) * Prevent write to relative path ... too easy to shoot oneself in the * foot by overwriting a database file ... */ - if (!is_absolute_path(cstate->filename)) + if (!is_absolute_path(filename)) ereport(ERROR, (errcode(ERRCODE_INVALID_NAME), errmsg("relative path not allowed for COPY to file"))); + cstate->filename = pstrdup(filename); oumask = umask(S_IWGRP | S_IWOTH); cstate->copy_file = AllocateFile(cstate->filename, PG_BINARY_W); umask(oumask); @@ -1292,14 +1362,30 @@ DoCopyTo(CopyState cstate) errmsg("\"%s\" is a directory", cstate->filename))); } + MemoryContextSwitchTo(oldcontext); + + return cstate; +} + +/* + * This intermediate routine exists mainly to localize the effects of setjmp + * so we don't need to plaster a lot of variables with "volatile". + */ +static uint64 +DoCopyTo(CopyState cstate) +{ + bool pipe = (cstate->filename == NULL); + bool fe_copy = (pipe && whereToSendOutput == DestRemote); + uint64 processed; + PG_TRY(); { - if (cstate->fe_copy) + if (fe_copy) SendCopyBegin(cstate); - CopyTo(cstate); + processed = CopyTo(cstate); - if (cstate->fe_copy) + if (fe_copy) SendCopyEnd(cstate); } PG_CATCH(); @@ -1314,26 +1400,38 @@ DoCopyTo(CopyState cstate) } PG_END_TRY(); - if (!pipe) + return processed; +} + +/* + * Clean up storage and release resources for COPY TO. + */ +static void +EndCopyTo(CopyState cstate) +{ + if (cstate->queryDesc != NULL) { - if (FreeFile(cstate->copy_file)) - ereport(ERROR, - (errcode_for_file_access(), - errmsg("could not close file \"%s\": %m", - cstate->filename))); + /* Close down the query and free resources. */ + ExecutorEnd(cstate->queryDesc); + FreeQueryDesc(cstate->queryDesc); + PopActiveSnapshot(); } + + /* Clean up storage */ + EndCopy(cstate); } /* * Copy from relation or query TO file. */ -static void +static uint64 CopyTo(CopyState cstate) { TupleDesc tupDesc; int num_phys_attrs; Form_pg_attribute *attr; ListCell *cur; + uint64 processed; if (cstate->rel) tupDesc = RelationGetDescr(cstate->rel); @@ -1439,6 +1537,7 @@ CopyTo(CopyState cstate) scandesc = heap_beginscan(cstate->rel, GetActiveSnapshot(), 0, NULL); + processed = 0; while ((tuple = heap_getnext(scandesc, ForwardScanDirection)) != NULL) { CHECK_FOR_INTERRUPTS(); @@ -1448,14 +1547,19 @@ CopyTo(CopyState cstate) /* Format and send the data */ CopyOneRowTo(cstate, HeapTupleGetOid(tuple), values, nulls); + processed++; } heap_endscan(scandesc); + + pfree(values); + pfree(nulls); } else { /* run the plan --- the dest receiver will send tuples */ ExecutorRun(cstate->queryDesc, ForwardScanDirection, 0L); + processed = ((DR_copy *) cstate->queryDesc->dest)->processed; } if (cstate->binary) @@ -1467,6 +1571,8 @@ CopyTo(CopyState cstate) } MemoryContextDelete(cstate->rowcontext); + + return processed; } /* @@ -1558,16 +1664,16 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) CopySendEndOfRow(cstate); MemoryContextSwitchTo(oldcontext); - - cstate->processed++; } /* * error context callback for COPY FROM + * + * The argument for the error context must be CopyState. */ -static void -copy_in_error_callback(void *arg) +void +CopyFromErrorCallback(void *arg) { CopyState cstate = (CopyState) arg; @@ -1669,41 +1775,23 @@ limit_printout_length(const char *str) /* * Copy FROM file to relation. */ -static void +static uint64 CopyFrom(CopyState cstate) { - bool pipe = (cstate->filename == NULL); HeapTuple tuple; TupleDesc tupDesc; - Form_pg_attribute *attr; - AttrNumber num_phys_attrs, - attr_count, - num_defaults; - FmgrInfo *in_functions; - FmgrInfo oid_in_function; - Oid *typioparams; - Oid oid_typioparam; - int attnum; - int i; - Oid in_func_oid; Datum *values; bool *nulls; - int nfields; - char **field_strings; - bool done = false; - bool isnull; ResultRelInfo *resultRelInfo; EState *estate = CreateExecutorState(); /* for ExecConstraints() */ + ExprContext *econtext; TupleTableSlot *slot; - bool file_has_oids; - int *defmap; - ExprState **defexprs; /* array of default att expressions */ - ExprContext *econtext; /* used for ExecEvalExpr for default atts */ MemoryContext oldcontext = CurrentMemoryContext; ErrorContextCallback errcontext; CommandId mycid = GetCurrentCommandId(true); int hi_options = 0; /* start with default heap_insert options */ BulkInsertState bistate; + uint64 processed = 0; Assert(cstate->rel); @@ -1731,6 +1819,8 @@ CopyFrom(CopyState cstate) RelationGetRelationName(cstate->rel)))); } + tupDesc = RelationGetDescr(cstate->rel); + /*---------- * Check to see if we can avoid writing WAL * @@ -1766,38 +1856,6 @@ CopyFrom(CopyState cstate) hi_options |= HEAP_INSERT_SKIP_WAL; } - if (pipe) - { - if (whereToSendOutput == DestRemote) - ReceiveCopyBegin(cstate); - else - cstate->copy_file = stdin; - } - else - { - struct stat st; - - cstate->copy_file = AllocateFile(cstate->filename, PG_BINARY_R); - - if (cstate->copy_file == NULL) - ereport(ERROR, - (errcode_for_file_access(), - errmsg("could not open file \"%s\" for reading: %m", - cstate->filename))); - - fstat(fileno(cstate->copy_file), &st); - if (S_ISDIR(st.st_mode)) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("\"%s\" is a directory", cstate->filename))); - } - - tupDesc = RelationGetDescr(cstate->rel); - attr = tupDesc->attrs; - num_phys_attrs = tupDesc->natts; - attr_count = list_length(cstate->attnumlist); - num_defaults = 0; - /* * We need a ResultRelInfo so we can use the regular executor's * index-entry-making machinery. (There used to be a huge amount of code @@ -1826,8 +1884,191 @@ CopyFrom(CopyState cstate) slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); + /* Prepare to catch AFTER triggers. */ + AfterTriggerBeginQuery(); + + /* + * Check BEFORE STATEMENT insertion triggers. It's debateable whether we + * should do this for COPY, since it's not really an "INSERT" statement as + * such. However, executing these triggers maintains consistency with the + * EACH ROW triggers that we already fire on COPY. + */ + ExecBSInsertTriggers(estate, resultRelInfo); + + values = (Datum *) palloc(tupDesc->natts * sizeof(Datum)); + nulls = (bool *) palloc(tupDesc->natts * sizeof(bool)); + + bistate = GetBulkInsertState(); econtext = GetPerTupleExprContext(estate); + /* Set up callback to identify error line number */ + errcontext.callback = CopyFromErrorCallback; + errcontext.arg = (void *) cstate; + errcontext.previous = error_context_stack; + error_context_stack = &errcontext; + + for (;;) + { + bool skip_tuple; + Oid loaded_oid = InvalidOid; + + CHECK_FOR_INTERRUPTS(); + + /* Reset the per-tuple exprcontext */ + ResetPerTupleExprContext(estate); + + /* Switch into its memory context */ + MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); + + if (!NextCopyFrom(cstate, econtext, values, nulls, &loaded_oid)) + break; + + /* And now we can form the input tuple. */ + tuple = heap_form_tuple(tupDesc, values, nulls); + + if (loaded_oid != InvalidOid) + HeapTupleSetOid(tuple, loaded_oid); + + /* Triggers and stuff need to be invoked in query context. */ + MemoryContextSwitchTo(oldcontext); + + skip_tuple = false; + + /* BEFORE ROW INSERT Triggers */ + if (resultRelInfo->ri_TrigDesc && + resultRelInfo->ri_TrigDesc->trig_insert_before_row) + { + HeapTuple newtuple; + + newtuple = ExecBRInsertTriggers(estate, resultRelInfo, tuple); + + if (newtuple == NULL) /* "do nothing" */ + skip_tuple = true; + else if (newtuple != tuple) /* modified by Trigger(s) */ + { + heap_freetuple(tuple); + tuple = newtuple; + } + } + + if (!skip_tuple) + { + List *recheckIndexes = NIL; + + /* Place tuple in tuple slot */ + ExecStoreTuple(tuple, slot, InvalidBuffer, false); + + /* Check the constraints of the tuple */ + if (cstate->rel->rd_att->constr) + ExecConstraints(resultRelInfo, slot, estate); + + /* OK, store the tuple and create index entries for it */ + heap_insert(cstate->rel, tuple, mycid, hi_options, bistate); + + if (resultRelInfo->ri_NumIndices > 0) + recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self), + estate); + + /* AFTER ROW INSERT Triggers */ + ExecARInsertTriggers(estate, resultRelInfo, tuple, + recheckIndexes); + + list_free(recheckIndexes); + + /* + * We count only tuples not suppressed by a BEFORE INSERT trigger; + * this is the same definition used by execMain.c for counting + * tuples inserted by an INSERT command. + */ + processed++; + } + } + + /* Done, clean up */ + error_context_stack = errcontext.previous; + + FreeBulkInsertState(bistate); + + MemoryContextSwitchTo(oldcontext); + + /* Execute AFTER STATEMENT insertion triggers */ + ExecASInsertTriggers(estate, resultRelInfo); + + /* Handle queued AFTER triggers */ + AfterTriggerEndQuery(estate); + + pfree(values); + pfree(nulls); + + ExecResetTupleTable(estate->es_tupleTable, false); + + ExecCloseIndices(resultRelInfo); + + FreeExecutorState(estate); + + /* + * If we skipped writing WAL, then we need to sync the heap (but not + * indexes since those use WAL anyway) + */ + if (hi_options & HEAP_INSERT_SKIP_WAL) + heap_sync(cstate->rel); + + return processed; +} + +/* + * Setup to read tuples from a file for COPY FROM. + * + * 'rel': Used as a template for the tuples + * 'filename': Name of server-local file to read + * 'attnamelist': List of char *, columns to include. NIL selects all cols. + * 'options': List of DefElem. See copy_opt_item in gram.y for selections. + * + * Returns a CopyState, to be passed to NextCopyFrom and related functions. + */ +CopyState +BeginCopyFrom(Relation rel, + const char *filename, + List *attnamelist, + List *options) +{ + CopyState cstate; + bool pipe = (filename == NULL); + TupleDesc tupDesc; + Form_pg_attribute *attr; + AttrNumber num_phys_attrs, + num_defaults; + FmgrInfo *in_functions; + Oid *typioparams; + int attnum; + Oid in_func_oid; + int *defmap; + ExprState **defexprs; + MemoryContext oldcontext; + + cstate = BeginCopy(true, rel, NULL, NULL, attnamelist, options); + oldcontext = MemoryContextSwitchTo(cstate->copycontext); + + /* Initialize state variables */ + cstate->fe_eof = false; + cstate->eol_type = EOL_UNKNOWN; + cstate->cur_relname = RelationGetRelationName(cstate->rel); + cstate->cur_lineno = 0; + cstate->cur_attname = NULL; + cstate->cur_attval = NULL; + + /* Set up variables to avoid per-attribute overhead. */ + initStringInfo(&cstate->attribute_buf); + initStringInfo(&cstate->line_buf); + cstate->line_buf_converted = false; + cstate->raw_buf = (char *) palloc(RAW_BUF_SIZE + 1); + cstate->raw_buf_index = cstate->raw_buf_len = 0; + + tupDesc = RelationGetDescr(cstate->rel); + attr = tupDesc->attrs; + num_phys_attrs = tupDesc->natts; + num_defaults = 0; + /* * Pick up the required catalog information for each attribute in the * relation, including the input function, the element type (to pass to @@ -1863,27 +2104,54 @@ CopyFrom(CopyState cstate) if (defexpr != NULL) { - defexprs[num_defaults] = ExecPrepareExpr((Expr *) defexpr, - estate); + /* Initialize expressions in copycontext. */ + defexprs[num_defaults] = ExecInitExpr( + expression_planner((Expr *) defexpr), NULL); defmap[num_defaults] = attnum - 1; num_defaults++; } } } - /* Prepare to catch AFTER triggers. */ - AfterTriggerBeginQuery(); + /* We keep those variables in cstate. */ + cstate->in_functions = in_functions; + cstate->typioparams = typioparams; + cstate->defmap = defmap; + cstate->defexprs = defexprs; + cstate->num_defaults = num_defaults; - /* - * Check BEFORE STATEMENT insertion triggers. It's debateable whether we - * should do this for COPY, since it's not really an "INSERT" statement as - * such. However, executing these triggers maintains consistency with the - * EACH ROW triggers that we already fire on COPY. - */ - ExecBSInsertTriggers(estate, resultRelInfo); + if (pipe) + { + if (whereToSendOutput == DestRemote) + ReceiveCopyBegin(cstate); + else + cstate->copy_file = stdin; + } + else + { + struct stat st; + + cstate->filename = pstrdup(filename); + cstate->copy_file = AllocateFile(cstate->filename, PG_BINARY_R); + + if (cstate->copy_file == NULL) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not open file \"%s\" for reading: %m", + cstate->filename))); + + fstat(fileno(cstate->copy_file), &st); + if (S_ISDIR(st.st_mode)) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("\"%s\" is a directory", cstate->filename))); + } if (!cstate->binary) - file_has_oids = cstate->oids; /* must rely on user to tell us... */ + { + /* must rely on user to tell us... */ + cstate->file_has_oids = cstate->oids; + } else { /* Read and verify binary header */ @@ -1901,7 +2169,7 @@ CopyFrom(CopyState cstate) ereport(ERROR, (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), errmsg("invalid COPY file header (missing flags)"))); - file_has_oids = (tmp & (1 << 16)) != 0; + cstate->file_has_oids = (tmp & (1 << 16)) != 0; tmp &= ~(1 << 16); if ((tmp >> 16) != 0) ereport(ERROR, @@ -1923,358 +2191,315 @@ CopyFrom(CopyState cstate) } } - if (file_has_oids && cstate->binary) + if (cstate->file_has_oids && cstate->binary) { getTypeBinaryInputInfo(OIDOID, - &in_func_oid, &oid_typioparam); - fmgr_info(in_func_oid, &oid_in_function); + &in_func_oid, &cstate->oid_typioparam); + fmgr_info(in_func_oid, &cstate->oid_in_function); } - values = (Datum *) palloc(num_phys_attrs * sizeof(Datum)); - nulls = (bool *) palloc(num_phys_attrs * sizeof(bool)); - /* create workspace for CopyReadAttributes results */ - nfields = file_has_oids ? (attr_count + 1) : attr_count; - if (! cstate->binary) + if (!cstate->binary) { + AttrNumber attr_count = list_length(cstate->attnumlist); + int nfields = cstate->file_has_oids ? (attr_count + 1) : attr_count; + cstate->max_fields = nfields; cstate->raw_fields = (char **) palloc(nfields * sizeof(char *)); } - /* Initialize state variables */ - cstate->fe_eof = false; - cstate->eol_type = EOL_UNKNOWN; - cstate->cur_relname = RelationGetRelationName(cstate->rel); - cstate->cur_lineno = 0; - cstate->cur_attname = NULL; - cstate->cur_attval = NULL; + MemoryContextSwitchTo(oldcontext); - bistate = GetBulkInsertState(); + return cstate; +} - /* Set up callback to identify error line number */ - errcontext.callback = copy_in_error_callback; - errcontext.arg = (void *) cstate; - errcontext.previous = error_context_stack; - error_context_stack = &errcontext; +/* + * Read raw fields in the next line for COPY FROM in text or csv mode. + * Return false if no more lines. + * + * An internal temporary buffer is returned via 'fields'. It is valid until + * the next call of the function. Since the function returns all raw fields + * in the input file, 'nfields' could be different from the number of columns + * in the relation. + * + * NOTE: force_not_null option are not applied to the returned fields. + */ +bool +NextCopyFromRawFields(CopyState cstate, char ***fields, int *nfields) +{ + int fldct; + bool done; + + /* only available for text or csv input */ + Assert(!cstate->binary); /* on input just throw the header line away */ - if (cstate->header_line) + if (cstate->cur_lineno == 0 && cstate->header_line) { cstate->cur_lineno++; - done = CopyReadLine(cstate); + if (CopyReadLine(cstate)) + return false; /* done */ } - while (!done) - { - bool skip_tuple; - Oid loaded_oid = InvalidOid; + cstate->cur_lineno++; - CHECK_FOR_INTERRUPTS(); + /* Actually read the line into memory here */ + done = CopyReadLine(cstate); - cstate->cur_lineno++; + /* + * EOF at start of line means we're done. If we see EOF after + * some characters, we act as though it was newline followed by + * EOF, ie, process the line and then exit loop on next iteration. + */ + if (done && cstate->line_buf.len == 0) + return false; - /* Reset the per-tuple exprcontext */ - ResetPerTupleExprContext(estate); + /* Parse the line into de-escaped field values */ + if (cstate->csv_mode) + fldct = CopyReadAttributesCSV(cstate); + else + fldct = CopyReadAttributesText(cstate); - /* Switch into its memory context */ - MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); + *fields = cstate->raw_fields; + *nfields = fldct; + return true; +} - /* Initialize all values for row to NULL */ - MemSet(values, 0, num_phys_attrs * sizeof(Datum)); - MemSet(nulls, true, num_phys_attrs * sizeof(bool)); +/* + * Read next tuple from file for COPY FROM. Return false if no more tuples. + * + * 'econtext' is used to evaluate default expression for each columns not + * read from the file. It can be NULL when no default values are used, i.e. + * when all columns are read from the file. + * + * 'values' and 'nulls' arrays must be the same length as columns of the + * relation passed to BeginCopyFrom. This function fills the arrays. + * Oid of the tuple is returned with 'tupleOid' separately. + */ +bool +NextCopyFrom(CopyState cstate, ExprContext *econtext, + Datum *values, bool *nulls, Oid *tupleOid) +{ + TupleDesc tupDesc; + Form_pg_attribute *attr; + AttrNumber num_phys_attrs, + attr_count, + num_defaults = cstate->num_defaults; + FmgrInfo *in_functions = cstate->in_functions; + Oid *typioparams = cstate->typioparams; + int i; + int nfields; + bool isnull; + bool file_has_oids = cstate->file_has_oids; + int *defmap = cstate->defmap; + ExprState **defexprs = cstate->defexprs; - if (!cstate->binary) - { - ListCell *cur; - int fldct; - int fieldno; - char *string; + tupDesc = RelationGetDescr(cstate->rel); + attr = tupDesc->attrs; + num_phys_attrs = tupDesc->natts; + attr_count = list_length(cstate->attnumlist); + nfields = file_has_oids ? (attr_count + 1) : attr_count; - /* Actually read the line into memory here */ - done = CopyReadLine(cstate); + /* Initialize all values for row to NULL */ + MemSet(values, 0, num_phys_attrs * sizeof(Datum)); + MemSet(nulls, true, num_phys_attrs * sizeof(bool)); - /* - * EOF at start of line means we're done. If we see EOF after - * some characters, we act as though it was newline followed by - * EOF, ie, process the line and then exit loop on next iteration. - */ - if (done && cstate->line_buf.len == 0) - break; + if (!cstate->binary) + { + char **field_strings; + ListCell *cur; + int fldct; + int fieldno; + char *string; - /* Parse the line into de-escaped field values */ - if (cstate->csv_mode) - fldct = CopyReadAttributesCSV(cstate); - else - fldct = CopyReadAttributesText(cstate); + /* read raw fields in the next line */ + if (!NextCopyFromRawFields(cstate, &field_strings, &fldct)) + return false; + + /* check for overflowing fields */ + if (nfields > 0 && fldct > nfields) + ereport(ERROR, + (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), + errmsg("extra data after last expected column"))); + + fieldno = 0; - /* check for overflowing fields */ - if (nfields > 0 && fldct > nfields) + /* Read the OID field if present */ + if (file_has_oids) + { + if (fieldno >= fldct) ereport(ERROR, (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), - errmsg("extra data after last expected column"))); - - fieldno = 0; - field_strings = cstate->raw_fields; + errmsg("missing data for OID column"))); + string = field_strings[fieldno++]; - /* Read the OID field if present */ - if (file_has_oids) + if (string == NULL) + ereport(ERROR, + (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), + errmsg("null OID in COPY data"))); + else if (cstate->oids && tupleOid != NULL) { - if (fieldno >= fldct) - ereport(ERROR, - (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), - errmsg("missing data for OID column"))); - string = field_strings[fieldno++]; - - if (string == NULL) - ereport(ERROR, - (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), - errmsg("null OID in COPY data"))); - else - { - cstate->cur_attname = "oid"; - cstate->cur_attval = string; - loaded_oid = DatumGetObjectId(DirectFunctionCall1(oidin, + cstate->cur_attname = "oid"; + cstate->cur_attval = string; + *tupleOid = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(string))); - if (loaded_oid == InvalidOid) - ereport(ERROR, - (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), - errmsg("invalid OID in COPY data"))); - cstate->cur_attname = NULL; - cstate->cur_attval = NULL; - } - } - - /* Loop to read the user attributes on the line. */ - foreach(cur, cstate->attnumlist) - { - int attnum = lfirst_int(cur); - int m = attnum - 1; - - if (fieldno >= fldct) + if (*tupleOid == InvalidOid) ereport(ERROR, (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), - errmsg("missing data for column \"%s\"", - NameStr(attr[m]->attname)))); - string = field_strings[fieldno++]; - - if (cstate->csv_mode && string == NULL && - cstate->force_notnull_flags[m]) - { - /* Go ahead and read the NULL string */ - string = cstate->null_print; - } - - cstate->cur_attname = NameStr(attr[m]->attname); - cstate->cur_attval = string; - values[m] = InputFunctionCall(&in_functions[m], - string, - typioparams[m], - attr[m]->atttypmod); - if (string != NULL) - nulls[m] = false; + errmsg("invalid OID in COPY data"))); cstate->cur_attname = NULL; cstate->cur_attval = NULL; } - - Assert(fieldno == nfields); } - else - { - /* binary */ - int16 fld_count; - ListCell *cur; - if (!CopyGetInt16(cstate, &fld_count)) - { - /* EOF detected (end of file, or protocol-level EOF) */ - done = true; - break; - } - - if (fld_count == -1) - { - /* - * Received EOF marker. In a V3-protocol copy, wait for - * the protocol-level EOF, and complain if it doesn't come - * immediately. This ensures that we correctly handle - * CopyFail, if client chooses to send that now. - * - * Note that we MUST NOT try to read more data in an - * old-protocol copy, since there is no protocol-level EOF - * marker then. We could go either way for copy from file, - * but choose to throw error if there's data after the EOF - * marker, for consistency with the new-protocol case. - */ - char dummy; - - if (cstate->copy_dest != COPY_OLD_FE && - CopyGetData(cstate, &dummy, 1, 1) > 0) - ereport(ERROR, - (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), - errmsg("received copy data after EOF marker"))); - done = true; - break; - } + /* Loop to read the user attributes on the line. */ + foreach(cur, cstate->attnumlist) + { + int attnum = lfirst_int(cur); + int m = attnum - 1; - if (fld_count != attr_count) + if (fieldno >= fldct) ereport(ERROR, (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), - errmsg("row field count is %d, expected %d", - (int) fld_count, attr_count))); - - if (file_has_oids) - { - cstate->cur_attname = "oid"; - loaded_oid = - DatumGetObjectId(CopyReadBinaryAttribute(cstate, - 0, - &oid_in_function, - oid_typioparam, - -1, - &isnull)); - if (isnull || loaded_oid == InvalidOid) - ereport(ERROR, - (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), - errmsg("invalid OID in COPY data"))); - cstate->cur_attname = NULL; - } + errmsg("missing data for column \"%s\"", + NameStr(attr[m]->attname)))); + string = field_strings[fieldno++]; - i = 0; - foreach(cur, cstate->attnumlist) + if (cstate->csv_mode && string == NULL && + cstate->force_notnull_flags[m]) { - int attnum = lfirst_int(cur); - int m = attnum - 1; - - cstate->cur_attname = NameStr(attr[m]->attname); - i++; - values[m] = CopyReadBinaryAttribute(cstate, - i, - &in_functions[m], - typioparams[m], - attr[m]->atttypmod, - &nulls[m]); - cstate->cur_attname = NULL; + /* Go ahead and read the NULL string */ + string = cstate->null_print; } - } - /* - * Now compute and insert any defaults available for the columns not - * provided by the input data. Anything not processed here or above - * will remain NULL. - */ - for (i = 0; i < num_defaults; i++) - { - values[defmap[i]] = ExecEvalExpr(defexprs[i], econtext, - &nulls[defmap[i]], NULL); + cstate->cur_attname = NameStr(attr[m]->attname); + cstate->cur_attval = string; + values[m] = InputFunctionCall(&in_functions[m], + string, + typioparams[m], + attr[m]->atttypmod); + if (string != NULL) + nulls[m] = false; + cstate->cur_attname = NULL; + cstate->cur_attval = NULL; } - /* And now we can form the input tuple. */ - tuple = heap_form_tuple(tupDesc, values, nulls); - - if (cstate->oids && file_has_oids) - HeapTupleSetOid(tuple, loaded_oid); - - /* Triggers and stuff need to be invoked in query context. */ - MemoryContextSwitchTo(oldcontext); + Assert(fieldno == nfields); + } + else + { + /* binary */ + int16 fld_count; + ListCell *cur; - skip_tuple = false; + cstate->cur_lineno++; - /* BEFORE ROW INSERT Triggers */ - if (resultRelInfo->ri_TrigDesc && - resultRelInfo->ri_TrigDesc->trig_insert_before_row) + if (!CopyGetInt16(cstate, &fld_count)) { - HeapTuple newtuple; - - newtuple = ExecBRInsertTriggers(estate, resultRelInfo, tuple); - - if (newtuple == NULL) /* "do nothing" */ - skip_tuple = true; - else if (newtuple != tuple) /* modified by Trigger(s) */ - { - heap_freetuple(tuple); - tuple = newtuple; - } + /* EOF detected (end of file, or protocol-level EOF) */ + return false; } - if (!skip_tuple) + if (fld_count == -1) { - List *recheckIndexes = NIL; - - /* Place tuple in tuple slot */ - ExecStoreTuple(tuple, slot, InvalidBuffer, false); - - /* Check the constraints of the tuple */ - if (cstate->rel->rd_att->constr) - ExecConstraints(resultRelInfo, slot, estate); - - /* OK, store the tuple and create index entries for it */ - heap_insert(cstate->rel, tuple, mycid, hi_options, bistate); - - if (resultRelInfo->ri_NumIndices > 0) - recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self), - estate); - - /* AFTER ROW INSERT Triggers */ - ExecARInsertTriggers(estate, resultRelInfo, tuple, - recheckIndexes); - - list_free(recheckIndexes); - /* - * We count only tuples not suppressed by a BEFORE INSERT trigger; - * this is the same definition used by execMain.c for counting - * tuples inserted by an INSERT command. + * Received EOF marker. In a V3-protocol copy, wait for + * the protocol-level EOF, and complain if it doesn't come + * immediately. This ensures that we correctly handle + * CopyFail, if client chooses to send that now. + * + * Note that we MUST NOT try to read more data in an + * old-protocol copy, since there is no protocol-level EOF + * marker then. We could go either way for copy from file, + * but choose to throw error if there's data after the EOF + * marker, for consistency with the new-protocol case. */ - cstate->processed++; - } - } - - /* Done, clean up */ - error_context_stack = errcontext.previous; + char dummy; - FreeBulkInsertState(bistate); - - MemoryContextSwitchTo(oldcontext); - - /* Execute AFTER STATEMENT insertion triggers */ - ExecASInsertTriggers(estate, resultRelInfo); - - /* Handle queued AFTER triggers */ - AfterTriggerEndQuery(estate); - - pfree(values); - pfree(nulls); - if (! cstate->binary) - pfree(cstate->raw_fields); - - pfree(in_functions); - pfree(typioparams); - pfree(defmap); - pfree(defexprs); - - ExecResetTupleTable(estate->es_tupleTable, false); + if (cstate->copy_dest != COPY_OLD_FE && + CopyGetData(cstate, &dummy, 1, 1) > 0) + ereport(ERROR, + (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), + errmsg("received copy data after EOF marker"))); + return false; + } - ExecCloseIndices(resultRelInfo); + if (fld_count != attr_count) + ereport(ERROR, + (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), + errmsg("row field count is %d, expected %d", + (int) fld_count, attr_count))); - FreeExecutorState(estate); + if (file_has_oids) + { + Oid loaded_oid; + + cstate->cur_attname = "oid"; + loaded_oid = + DatumGetObjectId(CopyReadBinaryAttribute(cstate, + 0, + &cstate->oid_in_function, + cstate->oid_typioparam, + -1, + &isnull)); + if (isnull || loaded_oid == InvalidOid) + ereport(ERROR, + (errcode(ERRCODE_BAD_COPY_FILE_FORMAT), + errmsg("invalid OID in COPY data"))); + cstate->cur_attname = NULL; + if (cstate->oids && tupleOid != NULL) + *tupleOid = loaded_oid; + } - if (!pipe) - { - if (FreeFile(cstate->copy_file)) - ereport(ERROR, - (errcode_for_file_access(), - errmsg("could not close file \"%s\": %m", - cstate->filename))); + i = 0; + foreach(cur, cstate->attnumlist) + { + int attnum = lfirst_int(cur); + int m = attnum - 1; + + cstate->cur_attname = NameStr(attr[m]->attname); + i++; + values[m] = CopyReadBinaryAttribute(cstate, + i, + &in_functions[m], + typioparams[m], + attr[m]->atttypmod, + &nulls[m]); + cstate->cur_attname = NULL; + } } /* - * If we skipped writing WAL, then we need to sync the heap (but not - * indexes since those use WAL anyway) + * Now compute and insert any defaults available for the columns not + * provided by the input data. Anything not processed here or above + * will remain NULL. */ - if (hi_options & HEAP_INSERT_SKIP_WAL) - heap_sync(cstate->rel); + for (i = 0; i < num_defaults; i++) + { + /* + * The caller must supply econtext and have switched into the + * per-tuple memory context in it. + */ + Assert(econtext != NULL); + Assert(CurrentMemoryContext == econtext->ecxt_per_tuple_memory); + + values[defmap[i]] = ExecEvalExpr(defexprs[i], econtext, + &nulls[defmap[i]], NULL); + } + + return true; } +/* + * Clean up storage and release resources for COPY FROM. + */ +void +EndCopyFrom(CopyState cstate) +{ + /* No COPY FROM related resources except memory. */ + + EndCopy(cstate); +} /* * Read the next input line and stash it in line_buf, with conversion to @@ -3537,6 +3762,7 @@ copy_dest_receive(TupleTableSlot *slot, DestReceiver *self) /* And send the data */ CopyOneRowTo(cstate, InvalidOid, slot->tts_values, slot->tts_isnull); + myState->processed++; } /* diff --git a/src/backend/commands/dbcommands.c b/src/backend/commands/dbcommands.c index 9a9b4cbf3d..87d9e545b4 100644 --- a/src/backend/commands/dbcommands.c +++ b/src/backend/commands/dbcommands.c @@ -129,8 +129,6 @@ createdb(const CreatedbStmt *stmt) char *dbctype = NULL; int encoding = -1; int dbconnlimit = -1; - int ctype_encoding; - int collate_encoding; int notherbackends; int npreparedxacts; createdb_failure_params fparms; @@ -334,60 +332,7 @@ createdb(const CreatedbStmt *stmt) (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("invalid locale name %s", dbctype))); - /* - * Check whether chosen encoding matches chosen locale settings. This - * restriction is necessary because libc's locale-specific code usually - * fails when presented with data in an encoding it's not expecting. We - * allow mismatch in four cases: - * - * 1. locale encoding = SQL_ASCII, which means that the locale is C/POSIX - * which works with any encoding. - * - * 2. locale encoding = -1, which means that we couldn't determine the - * locale's encoding and have to trust the user to get it right. - * - * 3. selected encoding is UTF8 and platform is win32. This is because - * UTF8 is a pseudo codepage that is supported in all locales since it's - * converted to UTF16 before being used. - * - * 4. selected encoding is SQL_ASCII, but only if you're a superuser. This - * is risky but we have historically allowed it --- notably, the - * regression tests require it. - * - * Note: if you change this policy, fix initdb to match. - */ - ctype_encoding = pg_get_encoding_from_locale(dbctype); - collate_encoding = pg_get_encoding_from_locale(dbcollate); - - if (!(ctype_encoding == encoding || - ctype_encoding == PG_SQL_ASCII || - ctype_encoding == -1 || -#ifdef WIN32 - encoding == PG_UTF8 || -#endif - (encoding == PG_SQL_ASCII && superuser()))) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("encoding %s does not match locale %s", - pg_encoding_to_char(encoding), - dbctype), - errdetail("The chosen LC_CTYPE setting requires encoding %s.", - pg_encoding_to_char(ctype_encoding)))); - - if (!(collate_encoding == encoding || - collate_encoding == PG_SQL_ASCII || - collate_encoding == -1 || -#ifdef WIN32 - encoding == PG_UTF8 || -#endif - (encoding == PG_SQL_ASCII && superuser()))) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("encoding %s does not match locale %s", - pg_encoding_to_char(encoding), - dbcollate), - errdetail("The chosen LC_COLLATE setting requires encoding %s.", - pg_encoding_to_char(collate_encoding)))); + check_encoding_locale_matches(encoding, dbcollate, dbctype); /* * Check that the new encoding and locale settings match the source @@ -710,6 +655,65 @@ createdb(const CreatedbStmt *stmt) PointerGetDatum(&fparms)); } +/* + * Check whether chosen encoding matches chosen locale settings. This + * restriction is necessary because libc's locale-specific code usually + * fails when presented with data in an encoding it's not expecting. We + * allow mismatch in four cases: + * + * 1. locale encoding = SQL_ASCII, which means that the locale is C/POSIX + * which works with any encoding. + * + * 2. locale encoding = -1, which means that we couldn't determine the + * locale's encoding and have to trust the user to get it right. + * + * 3. selected encoding is UTF8 and platform is win32. This is because + * UTF8 is a pseudo codepage that is supported in all locales since it's + * converted to UTF16 before being used. + * + * 4. selected encoding is SQL_ASCII, but only if you're a superuser. This + * is risky but we have historically allowed it --- notably, the + * regression tests require it. + * + * Note: if you change this policy, fix initdb to match. + */ +void +check_encoding_locale_matches(int encoding, const char *collate, const char *ctype) +{ + int ctype_encoding = pg_get_encoding_from_locale(ctype, true); + int collate_encoding = pg_get_encoding_from_locale(collate, true); + + if (!(ctype_encoding == encoding || + ctype_encoding == PG_SQL_ASCII || + ctype_encoding == -1 || +#ifdef WIN32 + encoding == PG_UTF8 || +#endif + (encoding == PG_SQL_ASCII && superuser()))) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("encoding %s does not match locale %s", + pg_encoding_to_char(encoding), + ctype), + errdetail("The chosen LC_CTYPE setting requires encoding %s.", + pg_encoding_to_char(ctype_encoding)))); + + if (!(collate_encoding == encoding || + collate_encoding == PG_SQL_ASCII || + collate_encoding == -1 || +#ifdef WIN32 + encoding == PG_UTF8 || +#endif + (encoding == PG_SQL_ASCII && superuser()))) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("encoding %s does not match locale %s", + pg_encoding_to_char(encoding), + collate), + errdetail("The chosen LC_COLLATE setting requires encoding %s.", + pg_encoding_to_char(collate_encoding)))); +} + /* Error cleanup callback for createdb */ static void createdb_failure_callback(int code, Datum arg) diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c new file mode 100644 index 0000000000..4bb79d4921 --- /dev/null +++ b/src/backend/commands/extension.c @@ -0,0 +1,2736 @@ +/*------------------------------------------------------------------------- + * + * extension.c + * Commands to manipulate extensions + * + * Extensions in PostgreSQL allow management of collections of SQL objects. + * + * All we need internally to manage an extension is an OID so that the + * dependent objects can be associated with it. An extension is created by + * populating the pg_extension catalog from a "control" file. + * The extension control file is parsed with the same parser we use for + * postgresql.conf and recovery.conf. An extension also has an installation + * script file, containing SQL commands to create the extension's objects. + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * src/backend/commands/extension.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include <dirent.h> +#include <limits.h> +#include <unistd.h> + +#include "access/sysattr.h" +#include "access/xact.h" +#include "catalog/dependency.h" +#include "catalog/indexing.h" +#include "catalog/namespace.h" +#include "catalog/pg_depend.h" +#include "catalog/pg_extension.h" +#include "catalog/pg_namespace.h" +#include "catalog/pg_type.h" +#include "commands/alter.h" +#include "commands/comment.h" +#include "commands/extension.h" +#include "commands/trigger.h" +#include "executor/executor.h" +#include "funcapi.h" +#include "mb/pg_wchar.h" +#include "miscadmin.h" +#include "tcop/tcopprot.h" +#include "tcop/utility.h" +#include "utils/builtins.h" +#include "utils/fmgroids.h" +#include "utils/guc.h" +#include "utils/lsyscache.h" +#include "utils/snapmgr.h" +#include "utils/tqual.h" + + +/* Globally visible state variables */ +bool creating_extension = false; +Oid CurrentExtensionObject = InvalidOid; + +/* + * Internal data structure to hold the results of parsing a control file + */ +typedef struct ExtensionControlFile +{ + char *name; /* name of the extension */ + char *directory; /* directory for script files */ + char *default_version; /* default install target version, if any */ + char *module_pathname; /* string to substitute for MODULE_PATHNAME */ + char *comment; /* comment, if any */ + char *schema; /* target schema (allowed if !relocatable) */ + bool relocatable; /* is ALTER EXTENSION SET SCHEMA supported? */ + int encoding; /* encoding of the script file, or -1 */ + List *requires; /* names of prerequisite extensions */ +} ExtensionControlFile; + +/* + * Internal data structure for update path information + */ +typedef struct ExtensionVersionInfo +{ + char *name; /* name of the starting version */ + List *reachable; /* List of ExtensionVersionInfo's */ + bool installable; /* does this version have an install script? */ + /* working state for Dijkstra's algorithm: */ + bool distance_known; /* is distance from start known yet? */ + int distance; /* current worst-case distance estimate */ + struct ExtensionVersionInfo *previous; /* current best predecessor */ +} ExtensionVersionInfo; + +/* Local functions */ +static List *find_update_path(List *evi_list, + ExtensionVersionInfo *evi_start, + ExtensionVersionInfo *evi_target, + bool reinitialize); +static void get_available_versions_for_extension(ExtensionControlFile *pcontrol, + Tuplestorestate *tupstore, + TupleDesc tupdesc); +static void ApplyExtensionUpdates(Oid extensionOid, + ExtensionControlFile *pcontrol, + const char *initialVersion, + List *updateVersions); + + +/* + * get_extension_oid - given an extension name, look up the OID + * + * If missing_ok is false, throw an error if extension name not found. If + * true, just return InvalidOid. + */ +Oid +get_extension_oid(const char *extname, bool missing_ok) +{ + Oid result; + Relation rel; + SysScanDesc scandesc; + HeapTuple tuple; + ScanKeyData entry[1]; + + rel = heap_open(ExtensionRelationId, AccessShareLock); + + ScanKeyInit(&entry[0], + Anum_pg_extension_extname, + BTEqualStrategyNumber, F_NAMEEQ, + CStringGetDatum(extname)); + + scandesc = systable_beginscan(rel, ExtensionNameIndexId, true, + SnapshotNow, 1, entry); + + tuple = systable_getnext(scandesc); + + /* We assume that there can be at most one matching tuple */ + if (HeapTupleIsValid(tuple)) + result = HeapTupleGetOid(tuple); + else + result = InvalidOid; + + systable_endscan(scandesc); + + heap_close(rel, AccessShareLock); + + if (!OidIsValid(result) && !missing_ok) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("extension \"%s\" does not exist", + extname))); + + return result; +} + +/* + * get_extension_name - given an extension OID, look up the name + * + * Returns a palloc'd string, or NULL if no such extension. + */ +char * +get_extension_name(Oid ext_oid) +{ + char *result; + Relation rel; + SysScanDesc scandesc; + HeapTuple tuple; + ScanKeyData entry[1]; + + rel = heap_open(ExtensionRelationId, AccessShareLock); + + ScanKeyInit(&entry[0], + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(ext_oid)); + + scandesc = systable_beginscan(rel, ExtensionOidIndexId, true, + SnapshotNow, 1, entry); + + tuple = systable_getnext(scandesc); + + /* We assume that there can be at most one matching tuple */ + if (HeapTupleIsValid(tuple)) + result = pstrdup(NameStr(((Form_pg_extension) GETSTRUCT(tuple))->extname)); + else + result = NULL; + + systable_endscan(scandesc); + + heap_close(rel, AccessShareLock); + + return result; +} + +/* + * get_extension_schema - given an extension OID, fetch its extnamespace + * + * Returns InvalidOid if no such extension. + */ +static Oid +get_extension_schema(Oid ext_oid) +{ + Oid result; + Relation rel; + SysScanDesc scandesc; + HeapTuple tuple; + ScanKeyData entry[1]; + + rel = heap_open(ExtensionRelationId, AccessShareLock); + + ScanKeyInit(&entry[0], + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(ext_oid)); + + scandesc = systable_beginscan(rel, ExtensionOidIndexId, true, + SnapshotNow, 1, entry); + + tuple = systable_getnext(scandesc); + + /* We assume that there can be at most one matching tuple */ + if (HeapTupleIsValid(tuple)) + result = ((Form_pg_extension) GETSTRUCT(tuple))->extnamespace; + else + result = InvalidOid; + + systable_endscan(scandesc); + + heap_close(rel, AccessShareLock); + + return result; +} + +/* + * Utility functions to check validity of extension and version names + */ +static void +check_valid_extension_name(const char *extensionname) +{ + int namelen = strlen(extensionname); + + /* + * Disallow empty names (the parser rejects empty identifiers anyway, + * but let's check). + */ + if (namelen == 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid extension name: \"%s\"", extensionname), + errdetail("Extension names must not be empty."))); + + /* + * No double dashes, since that would make script filenames ambiguous. + */ + if (strstr(extensionname, "--")) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid extension name: \"%s\"", extensionname), + errdetail("Extension names must not contain \"--\"."))); + + /* + * No leading or trailing dash either. (We could probably allow this, + * but it would require much care in filename parsing and would make + * filenames visually if not formally ambiguous. Since there's no + * real-world use case, let's just forbid it.) + */ + if (extensionname[0] == '-' || extensionname[namelen - 1] == '-') + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid extension name: \"%s\"", extensionname), + errdetail("Extension names must not begin or end with \"-\"."))); + + /* + * No directory separators either (this is sufficient to prevent ".." + * style attacks). + */ + if (first_dir_separator(extensionname) != NULL) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid extension name: \"%s\"", extensionname), + errdetail("Extension names must not contain directory separator characters."))); +} + +static void +check_valid_version_name(const char *versionname) +{ + int namelen = strlen(versionname); + + /* + * Disallow empty names (we could possibly allow this, but there seems + * little point). + */ + if (namelen == 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid extension version name: \"%s\"", versionname), + errdetail("Version names must not be empty."))); + + /* + * No double dashes, since that would make script filenames ambiguous. + */ + if (strstr(versionname, "--")) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid extension version name: \"%s\"", versionname), + errdetail("Version names must not contain \"--\"."))); + + /* + * No leading or trailing dash either. + */ + if (versionname[0] == '-' || versionname[namelen - 1] == '-') + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid extension version name: \"%s\"", versionname), + errdetail("Version names must not begin or end with \"-\"."))); + + /* + * No directory separators either (this is sufficient to prevent ".." + * style attacks). + */ + if (first_dir_separator(versionname) != NULL) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid extension version name: \"%s\"", versionname), + errdetail("Version names must not contain directory separator characters."))); +} + +/* + * Utility functions to handle extension-related path names + */ +static bool +is_extension_control_filename(const char *filename) +{ + const char *extension = strrchr(filename, '.'); + + return (extension != NULL) && (strcmp(extension, ".control") == 0); +} + +static bool +is_extension_script_filename(const char *filename) +{ + const char *extension = strrchr(filename, '.'); + + return (extension != NULL) && (strcmp(extension, ".sql") == 0); +} + +static char * +get_extension_control_directory(void) +{ + char sharepath[MAXPGPATH]; + char *result; + + get_share_path(my_exec_path, sharepath); + result = (char *) palloc(MAXPGPATH); + snprintf(result, MAXPGPATH, "%s/extension", sharepath); + + return result; +} + +static char * +get_extension_control_filename(const char *extname) +{ + char sharepath[MAXPGPATH]; + char *result; + + get_share_path(my_exec_path, sharepath); + result = (char *) palloc(MAXPGPATH); + snprintf(result, MAXPGPATH, "%s/extension/%s.control", + sharepath, extname); + + return result; +} + +static char * +get_extension_script_directory(ExtensionControlFile *control) +{ + char sharepath[MAXPGPATH]; + char *result; + + /* + * The directory parameter can be omitted, absolute, or relative to the + * installation's share directory. + */ + if (!control->directory) + return get_extension_control_directory(); + + if (is_absolute_path(control->directory)) + return pstrdup(control->directory); + + get_share_path(my_exec_path, sharepath); + result = (char *) palloc(MAXPGPATH); + snprintf(result, MAXPGPATH, "%s/%s", sharepath, control->directory); + + return result; +} + +static char * +get_extension_aux_control_filename(ExtensionControlFile *control, + const char *version) +{ + char *result; + char *scriptdir; + + scriptdir = get_extension_script_directory(control); + + result = (char *) palloc(MAXPGPATH); + snprintf(result, MAXPGPATH, "%s/%s--%s.control", + scriptdir, control->name, version); + + pfree(scriptdir); + + return result; +} + +static char * +get_extension_script_filename(ExtensionControlFile *control, + const char *from_version, const char *version) +{ + char *result; + char *scriptdir; + + scriptdir = get_extension_script_directory(control); + + result = (char *) palloc(MAXPGPATH); + if (from_version) + snprintf(result, MAXPGPATH, "%s/%s--%s--%s.sql", + scriptdir, control->name, from_version, version); + else + snprintf(result, MAXPGPATH, "%s/%s--%s.sql", + scriptdir, control->name, version); + + pfree(scriptdir); + + return result; +} + + +/* + * Parse contents of primary or auxiliary control file, and fill in + * fields of *control. We parse primary file if version == NULL, + * else the optional auxiliary file for that version. + * + * Control files are supposed to be very short, half a dozen lines, + * so we don't worry about memory allocation risks here. Also we don't + * worry about what encoding it's in; all values are expected to be ASCII. + */ +static void +parse_extension_control_file(ExtensionControlFile *control, + const char *version) +{ + char *filename; + FILE *file; + ConfigVariable *item, + *head = NULL, + *tail = NULL; + + /* + * Locate the file to read. Auxiliary files are optional. + */ + if (version) + filename = get_extension_aux_control_filename(control, version); + else + filename = get_extension_control_filename(control->name); + + if ((file = AllocateFile(filename, "r")) == NULL) + { + if (version && errno == ENOENT) + { + /* no auxiliary file for this version */ + pfree(filename); + return; + } + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not open extension control file \"%s\": %m", + filename))); + } + + /* + * Parse the file content, using GUC's file parsing code + */ + ParseConfigFp(file, filename, 0, ERROR, &head, &tail); + + FreeFile(file); + + /* + * Convert the ConfigVariable list into ExtensionControlFile entries. + */ + for (item = head; item != NULL; item = item->next) + { + if (strcmp(item->name, "directory") == 0) + { + if (version) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("parameter \"%s\" cannot be set in a secondary extension control file", + item->name))); + + control->directory = pstrdup(item->value); + } + else if (strcmp(item->name, "default_version") == 0) + { + if (version) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("parameter \"%s\" cannot be set in a secondary extension control file", + item->name))); + + control->default_version = pstrdup(item->value); + } + else if (strcmp(item->name, "module_pathname") == 0) + { + control->module_pathname = pstrdup(item->value); + } + else if (strcmp(item->name, "comment") == 0) + { + control->comment = pstrdup(item->value); + } + else if (strcmp(item->name, "schema") == 0) + { + control->schema = pstrdup(item->value); + } + else if (strcmp(item->name, "relocatable") == 0) + { + if (!parse_bool(item->value, &control->relocatable)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("parameter \"%s\" requires a Boolean value", + item->name))); + } + else if (strcmp(item->name, "encoding") == 0) + { + control->encoding = pg_valid_server_encoding(item->value); + if (control->encoding < 0) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("\"%s\" is not a valid encoding name", + item->value))); + } + else if (strcmp(item->name, "requires") == 0) + { + /* Need a modifiable copy of string */ + char *rawnames = pstrdup(item->value); + + /* Parse string into list of identifiers */ + if (!SplitIdentifierString(rawnames, ',', &control->requires)) + { + /* syntax error in name list */ + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("parameter \"%s\" must be a list of extension names", + item->name))); + } + } + else + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("unrecognized parameter \"%s\" in file \"%s\"", + item->name, filename))); + } + + FreeConfigVariables(head); + + if (control->relocatable && control->schema != NULL) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("parameter \"schema\" cannot be specified when \"relocatable\" is true"))); + + pfree(filename); +} + +/* + * Read the primary control file for the specified extension. + */ +static ExtensionControlFile * +read_extension_control_file(const char *extname) +{ + ExtensionControlFile *control; + + /* + * Set up default values. Pointer fields are initially null. + */ + control = (ExtensionControlFile *) palloc0(sizeof(ExtensionControlFile)); + control->name = pstrdup(extname); + control->relocatable = false; + control->encoding = -1; + + /* + * Parse the primary control file. + */ + parse_extension_control_file(control, NULL); + + return control; +} + +/* + * Read the auxiliary control file for the specified extension and version. + * + * Returns a new modified ExtensionControlFile struct; the original struct + * (reflecting just the primary control file) is not modified. + */ +static ExtensionControlFile * +read_extension_aux_control_file(const ExtensionControlFile *pcontrol, + const char *version) +{ + ExtensionControlFile *acontrol; + + /* + * Flat-copy the struct. Pointer fields share values with original. + */ + acontrol = (ExtensionControlFile *) palloc(sizeof(ExtensionControlFile)); + memcpy(acontrol, pcontrol, sizeof(ExtensionControlFile)); + + /* + * Parse the auxiliary control file, overwriting struct fields + */ + parse_extension_control_file(acontrol, version); + + return acontrol; +} + +/* + * Read a SQL script file into a string, and convert to database encoding + */ +static char * +read_extension_script_file(const ExtensionControlFile *control, + const char *filename) +{ + int src_encoding; + int dest_encoding = GetDatabaseEncoding(); + bytea *content; + char *src_str; + char *dest_str; + int len; + + content = read_binary_file(filename, 0, -1); + + /* use database encoding if not given */ + if (control->encoding < 0) + src_encoding = dest_encoding; + else + src_encoding = control->encoding; + + /* make sure that source string is valid in the expected encoding */ + len = VARSIZE_ANY_EXHDR(content); + src_str = VARDATA_ANY(content); + pg_verify_mbstr_len(src_encoding, src_str, len, false); + + /* convert the encoding to the database encoding */ + dest_str = (char *) pg_do_encoding_conversion((unsigned char *) src_str, + len, + src_encoding, + dest_encoding); + + /* if no conversion happened, we have to arrange for null termination */ + if (dest_str == src_str) + { + dest_str = (char *) palloc(len + 1); + memcpy(dest_str, src_str, len); + dest_str[len] = '\0'; + } + + return dest_str; +} + +/* + * Execute given SQL string. + * + * filename is used only to report errors. + * + * Note: it's tempting to just use SPI to execute the string, but that does + * not work very well. The really serious problem is that SPI will parse, + * analyze, and plan the whole string before executing any of it; of course + * this fails if there are any plannable statements referring to objects + * created earlier in the script. A lesser annoyance is that SPI insists + * on printing the whole string as errcontext in case of any error, and that + * could be very long. + */ +static void +execute_sql_string(const char *sql, const char *filename) +{ + List *raw_parsetree_list; + DestReceiver *dest; + ListCell *lc1; + + /* + * Parse the SQL string into a list of raw parse trees. + */ + raw_parsetree_list = pg_parse_query(sql); + + /* All output from SELECTs goes to the bit bucket */ + dest = CreateDestReceiver(DestNone); + + /* + * Do parse analysis, rule rewrite, planning, and execution for each raw + * parsetree. We must fully execute each query before beginning parse + * analysis on the next one, since there may be interdependencies. + */ + foreach(lc1, raw_parsetree_list) + { + Node *parsetree = (Node *) lfirst(lc1); + List *stmt_list; + ListCell *lc2; + + stmt_list = pg_analyze_and_rewrite(parsetree, + sql, + NULL, + 0); + stmt_list = pg_plan_queries(stmt_list, 0, NULL); + + foreach(lc2, stmt_list) + { + Node *stmt = (Node *) lfirst(lc2); + + if (IsA(stmt, TransactionStmt)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("transaction control statements are not allowed within an extension script"))); + + CommandCounterIncrement(); + + PushActiveSnapshot(GetTransactionSnapshot()); + + if (IsA(stmt, PlannedStmt) && + ((PlannedStmt *) stmt)->utilityStmt == NULL) + { + QueryDesc *qdesc; + + qdesc = CreateQueryDesc((PlannedStmt *) stmt, + sql, + GetActiveSnapshot(), NULL, + dest, NULL, 0); + + AfterTriggerBeginQuery(); + ExecutorStart(qdesc, 0); + ExecutorRun(qdesc, ForwardScanDirection, 0); + AfterTriggerEndQuery(qdesc->estate); + ExecutorEnd(qdesc); + + FreeQueryDesc(qdesc); + } + else + { + ProcessUtility(stmt, + sql, + NULL, + false, /* not top level */ + dest, + NULL); + } + + PopActiveSnapshot(); + } + } + + /* Be sure to advance the command counter after the last script command */ + CommandCounterIncrement(); +} + +/* + * Execute the appropriate script file for installing or updating the extension + * + * If from_version isn't NULL, it's an update + */ +static void +execute_extension_script(Oid extensionOid, ExtensionControlFile *control, + const char *from_version, + const char *version, + List *requiredSchemas, + const char *schemaName, Oid schemaOid) +{ + char *filename; + char *save_client_min_messages, + *save_log_min_messages, + *save_search_path; + StringInfoData pathbuf; + ListCell *lc; + + filename = get_extension_script_filename(control, from_version, version); + + /* + * Force client_min_messages and log_min_messages to be at least WARNING, + * so that we won't spam the user with useless NOTICE messages from common + * script actions like creating shell types. + * + * We use the equivalent of SET LOCAL to ensure the setting is undone + * upon error. + */ + save_client_min_messages = + pstrdup(GetConfigOption("client_min_messages", false)); + if (client_min_messages < WARNING) + (void) set_config_option("client_min_messages", "warning", + PGC_USERSET, PGC_S_SESSION, + GUC_ACTION_LOCAL, true); + + save_log_min_messages = + pstrdup(GetConfigOption("log_min_messages", false)); + if (log_min_messages < WARNING) + (void) set_config_option("log_min_messages", "warning", + PGC_SUSET, PGC_S_SESSION, + GUC_ACTION_LOCAL, true); + + /* + * Set up the search path to contain the target schema, then the schemas + * of any prerequisite extensions, and nothing else. In particular this + * makes the target schema be the default creation target namespace. + * + * Note: it might look tempting to use PushOverrideSearchPath for this, + * but we cannot do that. We have to actually set the search_path GUC + * in case the extension script examines or changes it. + */ + save_search_path = pstrdup(GetConfigOption("search_path", false)); + + initStringInfo(&pathbuf); + appendStringInfoString(&pathbuf, quote_identifier(schemaName)); + foreach(lc, requiredSchemas) + { + Oid reqschema = lfirst_oid(lc); + char *reqname = get_namespace_name(reqschema); + + if (reqname) + appendStringInfo(&pathbuf, ", %s", quote_identifier(reqname)); + } + + (void) set_config_option("search_path", pathbuf.data, + PGC_USERSET, PGC_S_SESSION, + GUC_ACTION_LOCAL, true); + + /* + * Set creating_extension and related variables so that + * recordDependencyOnCurrentExtension and other functions do the right + * things. On failure, ensure we reset these variables. + */ + creating_extension = true; + CurrentExtensionObject = extensionOid; + PG_TRY(); + { + char *sql = read_extension_script_file(control, filename); + + /* + * If it's not relocatable, substitute the target schema name for + * occcurrences of @extschema@. + * + * For a relocatable extension, we just run the script as-is. + * There cannot be any need for @extschema@, else it wouldn't + * be relocatable. + */ + if (!control->relocatable) + { + const char *qSchemaName = quote_identifier(schemaName); + + sql = text_to_cstring( + DatumGetTextPP( + DirectFunctionCall3(replace_text, + CStringGetTextDatum(sql), + CStringGetTextDatum("@extschema@"), + CStringGetTextDatum(qSchemaName)))); + } + + /* + * If module_pathname was set in the control file, substitute its + * value for occurrences of MODULE_PATHNAME. + */ + if (control->module_pathname) + { + sql = text_to_cstring( + DatumGetTextPP( + DirectFunctionCall3(replace_text, + CStringGetTextDatum(sql), + CStringGetTextDatum("MODULE_PATHNAME"), + CStringGetTextDatum(control->module_pathname)))); + } + + execute_sql_string(sql, filename); + } + PG_CATCH(); + { + creating_extension = false; + CurrentExtensionObject = InvalidOid; + PG_RE_THROW(); + } + PG_END_TRY(); + + creating_extension = false; + CurrentExtensionObject = InvalidOid; + + /* + * Restore GUC variables for the remainder of the current transaction. + * Again use SET LOCAL, so we won't affect the session value. + */ + (void) set_config_option("search_path", save_search_path, + PGC_USERSET, PGC_S_SESSION, + GUC_ACTION_LOCAL, true); + (void) set_config_option("client_min_messages", save_client_min_messages, + PGC_USERSET, PGC_S_SESSION, + GUC_ACTION_LOCAL, true); + (void) set_config_option("log_min_messages", save_log_min_messages, + PGC_SUSET, PGC_S_SESSION, + GUC_ACTION_LOCAL, true); +} + +/* + * Find or create an ExtensionVersionInfo for the specified version name + * + * Currently, we just use a List of the ExtensionVersionInfo's. Searching + * for them therefore uses about O(N^2) time when there are N versions of + * the extension. We could change the data structure to a hash table if + * this ever becomes a bottleneck. + */ +static ExtensionVersionInfo * +get_ext_ver_info(const char *versionname, List **evi_list) +{ + ExtensionVersionInfo *evi; + ListCell *lc; + + foreach(lc, *evi_list) + { + evi = (ExtensionVersionInfo *) lfirst(lc); + if (strcmp(evi->name, versionname) == 0) + return evi; + } + + evi = (ExtensionVersionInfo *) palloc(sizeof(ExtensionVersionInfo)); + evi->name = pstrdup(versionname); + evi->reachable = NIL; + evi->installable = false; + /* initialize for later application of Dijkstra's algorithm */ + evi->distance_known = false; + evi->distance = INT_MAX; + evi->previous = NULL; + + *evi_list = lappend(*evi_list, evi); + + return evi; +} + +/* + * Locate the nearest unprocessed ExtensionVersionInfo + * + * This part of the algorithm is also about O(N^2). A priority queue would + * make it much faster, but for now there's no need. + */ +static ExtensionVersionInfo * +get_nearest_unprocessed_vertex(List *evi_list) +{ + ExtensionVersionInfo *evi = NULL; + ListCell *lc; + + foreach(lc, evi_list) + { + ExtensionVersionInfo *evi2 = (ExtensionVersionInfo *) lfirst(lc); + + /* only vertices whose distance is still uncertain are candidates */ + if (evi2->distance_known) + continue; + /* remember the closest such vertex */ + if (evi == NULL || + evi->distance > evi2->distance) + evi = evi2; + } + + return evi; +} + +/* + * Obtain information about the set of update scripts available for the + * specified extension. The result is a List of ExtensionVersionInfo + * structs, each with a subsidiary list of the ExtensionVersionInfos for + * the versions that can be reached in one step from that version. + */ +static List * +get_ext_ver_list(ExtensionControlFile *control) +{ + List *evi_list = NIL; + int extnamelen = strlen(control->name); + char *location; + DIR *dir; + struct dirent *de; + + location = get_extension_script_directory(control); + dir = AllocateDir(location); + while ((de = ReadDir(dir, location)) != NULL) + { + char *vername; + char *vername2; + ExtensionVersionInfo *evi; + ExtensionVersionInfo *evi2; + + /* must be a .sql file ... */ + if (!is_extension_script_filename(de->d_name)) + continue; + + /* ... matching extension name followed by separator */ + if (strncmp(de->d_name, control->name, extnamelen) != 0 || + de->d_name[extnamelen] != '-' || + de->d_name[extnamelen + 1] != '-') + continue; + + /* extract version name(s) from 'extname--something.sql' filename */ + vername = pstrdup(de->d_name + extnamelen + 2); + *strrchr(vername, '.') = '\0'; + vername2 = strstr(vername, "--"); + if (!vername2) + { + /* It's an install, not update, script; record its version name */ + evi = get_ext_ver_info(vername, &evi_list); + evi->installable = true; + continue; + } + *vername2 = '\0'; /* terminate first version */ + vername2 += 2; /* and point to second */ + + /* if there's a third --, it's bogus, ignore it */ + if (strstr(vername2, "--")) + continue; + + /* Create ExtensionVersionInfos and link them together */ + evi = get_ext_ver_info(vername, &evi_list); + evi2 = get_ext_ver_info(vername2, &evi_list); + evi->reachable = lappend(evi->reachable, evi2); + } + FreeDir(dir); + + return evi_list; +} + +/* + * Given an initial and final version name, identify the sequence of update + * scripts that have to be applied to perform that update. + * + * Result is a List of names of versions to transition through (the initial + * version is *not* included). + */ +static List * +identify_update_path(ExtensionControlFile *control, + const char *oldVersion, const char *newVersion) +{ + List *result; + List *evi_list; + ExtensionVersionInfo *evi_start; + ExtensionVersionInfo *evi_target; + + /* Extract the version update graph from the script directory */ + evi_list = get_ext_ver_list(control); + + /* Initialize start and end vertices */ + evi_start = get_ext_ver_info(oldVersion, &evi_list); + evi_target = get_ext_ver_info(newVersion, &evi_list); + + /* Find shortest path */ + result = find_update_path(evi_list, evi_start, evi_target, false); + + if (result == NIL) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("extension \"%s\" has no update path from version \"%s\" to version \"%s\"", + control->name, oldVersion, newVersion))); + + return result; +} + +/* + * Apply Dijkstra's algorithm to find the shortest path from evi_start to + * evi_target. + * + * If reinitialize is false, assume the ExtensionVersionInfo list has not + * been used for this before, and the initialization done by get_ext_ver_info + * is still good. + * + * Result is a List of names of versions to transition through (the initial + * version is *not* included). Returns NIL if no such path. + */ +static List * +find_update_path(List *evi_list, + ExtensionVersionInfo *evi_start, + ExtensionVersionInfo *evi_target, + bool reinitialize) +{ + List *result; + ExtensionVersionInfo *evi; + ListCell *lc; + + /* Caller error if start == target */ + Assert(evi_start != evi_target); + + if (reinitialize) + { + foreach(lc, evi_list) + { + evi = (ExtensionVersionInfo *) lfirst(lc); + evi->distance_known = false; + evi->distance = INT_MAX; + evi->previous = NULL; + } + } + + evi_start->distance = 0; + + while ((evi = get_nearest_unprocessed_vertex(evi_list)) != NULL) + { + if (evi->distance == INT_MAX) + break; /* all remaining vertices are unreachable */ + evi->distance_known = true; + if (evi == evi_target) + break; /* found shortest path to target */ + foreach(lc, evi->reachable) + { + ExtensionVersionInfo *evi2 = (ExtensionVersionInfo *) lfirst(lc); + int newdist; + + newdist = evi->distance + 1; + if (newdist < evi2->distance) + { + evi2->distance = newdist; + evi2->previous = evi; + } + else if (newdist == evi2->distance && + evi2->previous != NULL && + strcmp(evi->name, evi2->previous->name) < 0) + { + /* + * Break ties in favor of the version name that comes first + * according to strcmp(). This behavior is undocumented and + * users shouldn't rely on it. We do it just to ensure that + * if there is a tie, the update path that is chosen does not + * depend on random factors like the order in which directory + * entries get visited. + */ + evi2->previous = evi; + } + } + } + + /* Return NIL if target is not reachable from start */ + if (!evi_target->distance_known) + return NIL; + + /* Build and return list of version names representing the update path */ + result = NIL; + for (evi = evi_target; evi != evi_start; evi = evi->previous) + result = lcons(evi->name, result); + + return result; +} + +/* + * CREATE EXTENSION + */ +void +CreateExtension(CreateExtensionStmt *stmt) +{ + DefElem *d_schema = NULL; + DefElem *d_new_version = NULL; + DefElem *d_old_version = NULL; + char *schemaName; + Oid schemaOid; + char *versionName; + char *oldVersionName; + Oid extowner = GetUserId(); + ExtensionControlFile *pcontrol; + ExtensionControlFile *control; + List *updateVersions; + List *requiredExtensions; + List *requiredSchemas; + Oid extensionOid; + ListCell *lc; + + /* Must be super user */ + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied to create extension \"%s\"", + stmt->extname), + errhint("Must be superuser to create an extension."))); + + /* + * We use global variables to track the extension being created, so we + * can create only one extension at the same time. + */ + if (creating_extension) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("nested CREATE EXTENSION is not supported"))); + + /* Check extension name validity before any filesystem access */ + check_valid_extension_name(stmt->extname); + + /* + * Check for duplicate extension name. The unique index on + * pg_extension.extname would catch this anyway, and serves as a backstop + * in case of race conditions; but this is a friendlier error message. + */ + if (get_extension_oid(stmt->extname, true) != InvalidOid) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("extension \"%s\" already exists", stmt->extname))); + + /* + * Read the primary control file. Note we assume that it does not contain + * any non-ASCII data, so there is no need to worry about encoding at this + * point. + */ + pcontrol = read_extension_control_file(stmt->extname); + + /* + * Read the statement option list + */ + foreach(lc, stmt->options) + { + DefElem *defel = (DefElem *) lfirst(lc); + + if (strcmp(defel->defname, "schema") == 0) + { + if (d_schema) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + d_schema = defel; + } + else if (strcmp(defel->defname, "new_version") == 0) + { + if (d_new_version) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + d_new_version = defel; + } + else if (strcmp(defel->defname, "old_version") == 0) + { + if (d_old_version) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + d_old_version = defel; + } + else + elog(ERROR, "unrecognized option: %s", defel->defname); + } + + /* + * Determine the version to install + */ + if (d_new_version && d_new_version->arg) + versionName = strVal(d_new_version->arg); + else if (pcontrol->default_version) + versionName = pcontrol->default_version; + else + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("version to install must be specified"))); + versionName = NULL; /* keep compiler quiet */ + } + check_valid_version_name(versionName); + + /* + * Determine the (unpackaged) version to update from, if any, and then + * figure out what sequence of update scripts we need to apply. + */ + if (d_old_version && d_old_version->arg) + { + oldVersionName = strVal(d_old_version->arg); + check_valid_version_name(oldVersionName); + + if (strcmp(oldVersionName, versionName) == 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("FROM version must be different from installation target version \"%s\"", + versionName))); + + updateVersions = identify_update_path(pcontrol, + oldVersionName, + versionName); + + if (list_length(updateVersions) == 1) + { + /* + * Simple case where there's just one update script to run. + * We will not need any follow-on update steps. + */ + Assert(strcmp((char *) linitial(updateVersions), versionName) == 0); + updateVersions = NIL; + } + else + { + /* + * Multi-step sequence. We treat this as installing the version + * that is the target of the first script, followed by successive + * updates to the later versions. + */ + versionName = (char *) linitial(updateVersions); + updateVersions = list_delete_first(updateVersions); + } + } + else + { + oldVersionName = NULL; + updateVersions = NIL; + } + + /* + * Fetch control parameters for installation target version + */ + control = read_extension_aux_control_file(pcontrol, versionName); + + /* + * Determine the target schema to install the extension into + */ + if (d_schema && d_schema->arg) + { + /* + * User given schema, CREATE EXTENSION ... WITH SCHEMA ... + * + * It's an error to give a schema different from control->schema if + * control->schema is specified. + */ + schemaName = strVal(d_schema->arg); + + if (control->schema != NULL && + strcmp(control->schema, schemaName) != 0) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("extension \"%s\" must be installed in schema \"%s\"", + control->name, + control->schema))); + + /* If the user is giving us the schema name, it must exist already */ + schemaOid = get_namespace_oid(schemaName, false); + } + else if (control->schema != NULL) + { + /* + * The extension is not relocatable and the author gave us a schema + * for it. We create the schema here if it does not already exist. + */ + schemaName = control->schema; + schemaOid = get_namespace_oid(schemaName, true); + + if (schemaOid == InvalidOid) + { + schemaOid = NamespaceCreate(schemaName, extowner); + /* Advance cmd counter to make the namespace visible */ + CommandCounterIncrement(); + } + } + else + { + /* + * Else, use the current default creation namespace, which is the + * first explicit entry in the search_path. + */ + List *search_path = fetch_search_path(false); + + if (search_path == NIL) /* probably can't happen */ + elog(ERROR, "there is no default creation target"); + schemaOid = linitial_oid(search_path); + schemaName = get_namespace_name(schemaOid); + if (schemaName == NULL) /* recently-deleted namespace? */ + elog(ERROR, "there is no default creation target"); + + list_free(search_path); + } + + /* + * If we didn't already know user is superuser, we would probably want + * to do pg_namespace_aclcheck(schemaOid, extowner, ACL_CREATE) here. + */ + + /* + * Look up the prerequisite extensions, and build lists of their OIDs + * and the OIDs of their target schemas. + */ + requiredExtensions = NIL; + requiredSchemas = NIL; + foreach(lc, control->requires) + { + char *curreq = (char *) lfirst(lc); + Oid reqext; + Oid reqschema; + + /* + * We intentionally don't use get_extension_oid's default error + * message here, because it would be confusing in this context. + */ + reqext = get_extension_oid(curreq, true); + if (!OidIsValid(reqext)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("required extension \"%s\" is not installed", + curreq))); + reqschema = get_extension_schema(reqext); + requiredExtensions = lappend_oid(requiredExtensions, reqext); + requiredSchemas = lappend_oid(requiredSchemas, reqschema); + } + + /* + * Insert new tuple into pg_extension, and create dependency entries. + */ + extensionOid = InsertExtensionTuple(control->name, extowner, + schemaOid, control->relocatable, + versionName, + PointerGetDatum(NULL), + PointerGetDatum(NULL), + requiredExtensions); + + /* + * Apply any control-file comment on extension + */ + if (control->comment != NULL) + CreateComments(extensionOid, ExtensionRelationId, 0, control->comment); + + /* + * Execute the installation script file + */ + execute_extension_script(extensionOid, control, + oldVersionName, versionName, + requiredSchemas, + schemaName, schemaOid); + + /* + * If additional update scripts have to be executed, apply the updates + * as though a series of ALTER EXTENSION UPDATE commands were given + */ + ApplyExtensionUpdates(extensionOid, pcontrol, + versionName, updateVersions); +} + +/* + * InsertExtensionTuple + * + * Insert the new pg_extension row, and create extension's dependency entries. + * Return the OID assigned to the new row. + * + * This is exported for the benefit of pg_upgrade, which has to create a + * pg_extension entry (and the extension-level dependencies) without + * actually running the extension's script. + * + * extConfig and extCondition should be arrays or PointerGetDatum(NULL). + * We declare them as plain Datum to avoid needing array.h in extension.h. + */ +Oid +InsertExtensionTuple(const char *extName, Oid extOwner, + Oid schemaOid, bool relocatable, const char *extVersion, + Datum extConfig, Datum extCondition, + List *requiredExtensions) +{ + Oid extensionOid; + Relation rel; + Datum values[Natts_pg_extension]; + bool nulls[Natts_pg_extension]; + HeapTuple tuple; + ObjectAddress myself; + ObjectAddress nsp; + ListCell *lc; + + /* + * Build and insert the pg_extension tuple + */ + rel = heap_open(ExtensionRelationId, RowExclusiveLock); + + memset(values, 0, sizeof(values)); + memset(nulls, 0, sizeof(nulls)); + + values[Anum_pg_extension_extname - 1] = + DirectFunctionCall1(namein, CStringGetDatum(extName)); + values[Anum_pg_extension_extowner - 1] = ObjectIdGetDatum(extOwner); + values[Anum_pg_extension_extnamespace - 1] = ObjectIdGetDatum(schemaOid); + values[Anum_pg_extension_extrelocatable - 1] = BoolGetDatum(relocatable); + values[Anum_pg_extension_extversion - 1] = CStringGetTextDatum(extVersion); + + if (extConfig == PointerGetDatum(NULL)) + nulls[Anum_pg_extension_extconfig - 1] = true; + else + values[Anum_pg_extension_extconfig - 1] = extConfig; + + if (extCondition == PointerGetDatum(NULL)) + nulls[Anum_pg_extension_extcondition - 1] = true; + else + values[Anum_pg_extension_extcondition - 1] = extCondition; + + tuple = heap_form_tuple(rel->rd_att, values, nulls); + + extensionOid = simple_heap_insert(rel, tuple); + CatalogUpdateIndexes(rel, tuple); + + heap_freetuple(tuple); + heap_close(rel, RowExclusiveLock); + + /* + * Record dependencies on owner, schema, and prerequisite extensions + */ + recordDependencyOnOwner(ExtensionRelationId, extensionOid, extOwner); + + myself.classId = ExtensionRelationId; + myself.objectId = extensionOid; + myself.objectSubId = 0; + + nsp.classId = NamespaceRelationId; + nsp.objectId = schemaOid; + nsp.objectSubId = 0; + + recordDependencyOn(&myself, &nsp, DEPENDENCY_NORMAL); + + foreach(lc, requiredExtensions) + { + Oid reqext = lfirst_oid(lc); + ObjectAddress otherext; + + otherext.classId = ExtensionRelationId; + otherext.objectId = reqext; + otherext.objectSubId = 0; + + recordDependencyOn(&myself, &otherext, DEPENDENCY_NORMAL); + } + + return extensionOid; +} + + +/* + * RemoveExtensions + * Implements DROP EXTENSION. + */ +void +RemoveExtensions(DropStmt *drop) +{ + ObjectAddresses *objects; + ListCell *cell; + + /* + * First we identify all the extensions, then we delete them in a single + * performMultipleDeletions() call. This is to avoid unwanted DROP + * RESTRICT errors if one of the extensions depends on another. + */ + objects = new_object_addresses(); + + foreach(cell, drop->objects) + { + List *names = (List *) lfirst(cell); + char *extensionName; + Oid extensionId; + ObjectAddress object; + + if (list_length(names) != 1) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("extension name cannot be qualified"))); + extensionName = strVal(linitial(names)); + + extensionId = get_extension_oid(extensionName, drop->missing_ok); + + if (!OidIsValid(extensionId)) + { + ereport(NOTICE, + (errmsg("extension \"%s\" does not exist, skipping", + extensionName))); + continue; + } + + /* + * Permission check. For now, insist on superuser-ness; later we + * might want to relax that to being owner of the extension. + */ + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("permission denied to drop extension \"%s\"", + extensionName), + errhint("Must be superuser to drop an extension."))); + + object.classId = ExtensionRelationId; + object.objectId = extensionId; + object.objectSubId = 0; + + add_exact_object_address(&object, objects); + } + + /* + * Do the deletions. Objects contained in the extension(s) are removed by + * means of their dependency links to the extensions. + */ + performMultipleDeletions(objects, drop->behavior); + + free_object_addresses(objects); +} + + +/* + * Guts of extension deletion. + * + * All we need do here is remove the pg_extension tuple itself. Everything + * else is taken care of by the dependency infrastructure. + */ +void +RemoveExtensionById(Oid extId) +{ + Relation rel; + SysScanDesc scandesc; + HeapTuple tuple; + ScanKeyData entry[1]; + + rel = heap_open(ExtensionRelationId, RowExclusiveLock); + + ScanKeyInit(&entry[0], + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(extId)); + scandesc = systable_beginscan(rel, ExtensionOidIndexId, true, + SnapshotNow, 1, entry); + + tuple = systable_getnext(scandesc); + + /* We assume that there can be at most one matching tuple */ + if (HeapTupleIsValid(tuple)) + simple_heap_delete(rel, &tuple->t_self); + + systable_endscan(scandesc); + + heap_close(rel, RowExclusiveLock); +} + +/* + * This function lists the available extensions (one row per primary control + * file in the control directory). We parse each control file and report the + * interesting fields. + * + * The system view pg_available_extensions provides a user interface to this + * SRF, adding information about whether the extensions are installed in the + * current DB. + */ +Datum +pg_available_extensions(PG_FUNCTION_ARGS) +{ + ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + TupleDesc tupdesc; + Tuplestorestate *tupstore; + MemoryContext per_query_ctx; + MemoryContext oldcontext; + char *location; + DIR *dir; + struct dirent *de; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to list available extensions")))); + + /* check to see if caller supports us returning a tuplestore */ + if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("set-valued function called in context that cannot accept a set"))); + if (!(rsinfo->allowedModes & SFRM_Materialize)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("materialize mode required, but it is not " \ + "allowed in this context"))); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + /* Build tuplestore to hold the result rows */ + per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; + oldcontext = MemoryContextSwitchTo(per_query_ctx); + + tupstore = tuplestore_begin_heap(true, false, work_mem); + rsinfo->returnMode = SFRM_Materialize; + rsinfo->setResult = tupstore; + rsinfo->setDesc = tupdesc; + + MemoryContextSwitchTo(oldcontext); + + location = get_extension_control_directory(); + dir = AllocateDir(location); + + /* + * If the control directory doesn't exist, we want to silently return + * an empty set. Any other error will be reported by ReadDir. + */ + if (dir == NULL && errno == ENOENT) + { + /* do nothing */ + } + else + { + while ((de = ReadDir(dir, location)) != NULL) + { + ExtensionControlFile *control; + char *extname; + Datum values[3]; + bool nulls[3]; + + if (!is_extension_control_filename(de->d_name)) + continue; + + /* extract extension name from 'name.control' filename */ + extname = pstrdup(de->d_name); + *strrchr(extname, '.') = '\0'; + + /* ignore it if it's an auxiliary control file */ + if (strstr(extname, "--")) + continue; + + control = read_extension_control_file(extname); + + memset(values, 0, sizeof(values)); + memset(nulls, 0, sizeof(nulls)); + + /* name */ + values[0] = DirectFunctionCall1(namein, + CStringGetDatum(control->name)); + /* default_version */ + if (control->default_version == NULL) + nulls[1] = true; + else + values[1] = CStringGetTextDatum(control->default_version); + /* comment */ + if (control->comment == NULL) + nulls[2] = true; + else + values[2] = CStringGetTextDatum(control->comment); + + tuplestore_putvalues(tupstore, tupdesc, values, nulls); + } + + FreeDir(dir); + } + + /* clean up and return the tuplestore */ + tuplestore_donestoring(tupstore); + + return (Datum) 0; +} + +/* + * This function lists the available extension versions (one row per + * extension installation script). For each version, we parse the related + * control file(s) and report the interesting fields. + * + * The system view pg_available_extension_versions provides a user interface + * to this SRF, adding information about which versions are installed in the + * current DB. + */ +Datum +pg_available_extension_versions(PG_FUNCTION_ARGS) +{ + ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + TupleDesc tupdesc; + Tuplestorestate *tupstore; + MemoryContext per_query_ctx; + MemoryContext oldcontext; + char *location; + DIR *dir; + struct dirent *de; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to list available extensions")))); + + /* check to see if caller supports us returning a tuplestore */ + if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("set-valued function called in context that cannot accept a set"))); + if (!(rsinfo->allowedModes & SFRM_Materialize)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("materialize mode required, but it is not " \ + "allowed in this context"))); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + /* Build tuplestore to hold the result rows */ + per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; + oldcontext = MemoryContextSwitchTo(per_query_ctx); + + tupstore = tuplestore_begin_heap(true, false, work_mem); + rsinfo->returnMode = SFRM_Materialize; + rsinfo->setResult = tupstore; + rsinfo->setDesc = tupdesc; + + MemoryContextSwitchTo(oldcontext); + + location = get_extension_control_directory(); + dir = AllocateDir(location); + + /* + * If the control directory doesn't exist, we want to silently return + * an empty set. Any other error will be reported by ReadDir. + */ + if (dir == NULL && errno == ENOENT) + { + /* do nothing */ + } + else + { + while ((de = ReadDir(dir, location)) != NULL) + { + ExtensionControlFile *control; + char *extname; + + if (!is_extension_control_filename(de->d_name)) + continue; + + /* extract extension name from 'name.control' filename */ + extname = pstrdup(de->d_name); + *strrchr(extname, '.') = '\0'; + + /* ignore it if it's an auxiliary control file */ + if (strstr(extname, "--")) + continue; + + /* read the control file */ + control = read_extension_control_file(extname); + + /* scan extension's script directory for install scripts */ + get_available_versions_for_extension(control, tupstore, tupdesc); + } + + FreeDir(dir); + } + + /* clean up and return the tuplestore */ + tuplestore_donestoring(tupstore); + + return (Datum) 0; +} + +/* + * Inner loop for pg_available_extension_versions: + * read versions of one extension, add rows to tupstore + */ +static void +get_available_versions_for_extension(ExtensionControlFile *pcontrol, + Tuplestorestate *tupstore, + TupleDesc tupdesc) +{ + int extnamelen = strlen(pcontrol->name); + char *location; + DIR *dir; + struct dirent *de; + + location = get_extension_script_directory(pcontrol); + dir = AllocateDir(location); + /* Note this will fail if script directory doesn't exist */ + while ((de = ReadDir(dir, location)) != NULL) + { + ExtensionControlFile *control; + char *vername; + Datum values[6]; + bool nulls[6]; + + /* must be a .sql file ... */ + if (!is_extension_script_filename(de->d_name)) + continue; + + /* ... matching extension name followed by separator */ + if (strncmp(de->d_name, pcontrol->name, extnamelen) != 0 || + de->d_name[extnamelen] != '-' || + de->d_name[extnamelen + 1] != '-') + continue; + + /* extract version name from 'extname--something.sql' filename */ + vername = pstrdup(de->d_name + extnamelen + 2); + *strrchr(vername, '.') = '\0'; + + /* ignore it if it's an update script */ + if (strstr(vername, "--")) + continue; + + /* + * Fetch parameters for specific version (pcontrol is not changed) + */ + control = read_extension_aux_control_file(pcontrol, vername); + + memset(values, 0, sizeof(values)); + memset(nulls, 0, sizeof(nulls)); + + /* name */ + values[0] = DirectFunctionCall1(namein, + CStringGetDatum(control->name)); + /* version */ + values[1] = CStringGetTextDatum(vername); + /* relocatable */ + values[2] = BoolGetDatum(control->relocatable); + /* schema */ + if (control->schema == NULL) + nulls[3] = true; + else + values[3] = DirectFunctionCall1(namein, + CStringGetDatum(control->schema)); + /* requires */ + if (control->requires == NIL) + nulls[4] = true; + else + { + Datum *datums; + int ndatums; + ArrayType *a; + ListCell *lc; + + ndatums = list_length(control->requires); + datums = (Datum *) palloc(ndatums * sizeof(Datum)); + ndatums = 0; + foreach(lc, control->requires) + { + char *curreq = (char *) lfirst(lc); + + datums[ndatums++] = + DirectFunctionCall1(namein, CStringGetDatum(curreq)); + } + a = construct_array(datums, ndatums, + NAMEOID, + NAMEDATALEN, false, 'c'); + values[4] = PointerGetDatum(a); + } + /* comment */ + if (control->comment == NULL) + nulls[5] = true; + else + values[5] = CStringGetTextDatum(control->comment); + + tuplestore_putvalues(tupstore, tupdesc, values, nulls); + } + + FreeDir(dir); +} + +/* + * This function reports the version update paths that exist for the + * specified extension. + */ +Datum +pg_extension_update_paths(PG_FUNCTION_ARGS) +{ + Name extname = PG_GETARG_NAME(0); + ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + TupleDesc tupdesc; + Tuplestorestate *tupstore; + MemoryContext per_query_ctx; + MemoryContext oldcontext; + List *evi_list; + ExtensionControlFile *control; + ListCell *lc1; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to list extension update paths")))); + + /* Check extension name validity before any filesystem access */ + check_valid_extension_name(NameStr(*extname)); + + /* check to see if caller supports us returning a tuplestore */ + if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("set-valued function called in context that cannot accept a set"))); + if (!(rsinfo->allowedModes & SFRM_Materialize)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("materialize mode required, but it is not " \ + "allowed in this context"))); + + /* Build a tuple descriptor for our result type */ + if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + /* Build tuplestore to hold the result rows */ + per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; + oldcontext = MemoryContextSwitchTo(per_query_ctx); + + tupstore = tuplestore_begin_heap(true, false, work_mem); + rsinfo->returnMode = SFRM_Materialize; + rsinfo->setResult = tupstore; + rsinfo->setDesc = tupdesc; + + MemoryContextSwitchTo(oldcontext); + + /* Read the extension's control file */ + control = read_extension_control_file(NameStr(*extname)); + + /* Extract the version update graph from the script directory */ + evi_list = get_ext_ver_list(control); + + /* Iterate over all pairs of versions */ + foreach(lc1, evi_list) + { + ExtensionVersionInfo *evi1 = (ExtensionVersionInfo *) lfirst(lc1); + ListCell *lc2; + + foreach(lc2, evi_list) + { + ExtensionVersionInfo *evi2 = (ExtensionVersionInfo *) lfirst(lc2); + List *path; + Datum values[3]; + bool nulls[3]; + + if (evi1 == evi2) + continue; + + /* Find shortest path from evi1 to evi2 */ + path = find_update_path(evi_list, evi1, evi2, true); + + /* Emit result row */ + memset(values, 0, sizeof(values)); + memset(nulls, 0, sizeof(nulls)); + + /* source */ + values[0] = CStringGetTextDatum(evi1->name); + /* target */ + values[1] = CStringGetTextDatum(evi2->name); + /* path */ + if (path == NIL) + nulls[2] = true; + else + { + StringInfoData pathbuf; + ListCell *lcv; + + initStringInfo(&pathbuf); + /* The path doesn't include start vertex, but show it */ + appendStringInfoString(&pathbuf, evi1->name); + foreach(lcv, path) + { + char *versionName = (char *) lfirst(lcv); + + appendStringInfoString(&pathbuf, "--"); + appendStringInfoString(&pathbuf, versionName); + } + values[2] = CStringGetTextDatum(pathbuf.data); + pfree(pathbuf.data); + } + + tuplestore_putvalues(tupstore, tupdesc, values, nulls); + } + } + + /* clean up and return the tuplestore */ + tuplestore_donestoring(tupstore); + + return (Datum) 0; +} + +/* + * pg_extension_config_dump + * + * Record information about a configuration table that belongs to an + * extension being created, but whose contents should be dumped in whole + * or in part during pg_dump. + */ +Datum +pg_extension_config_dump(PG_FUNCTION_ARGS) +{ + Oid tableoid = PG_GETARG_OID(0); + text *wherecond = PG_GETARG_TEXT_P(1); + char *tablename; + Relation extRel; + ScanKeyData key[1]; + SysScanDesc extScan; + HeapTuple extTup; + Datum arrayDatum; + Datum elementDatum; + int arrayIndex; + bool isnull; + Datum repl_val[Natts_pg_extension]; + bool repl_null[Natts_pg_extension]; + bool repl_repl[Natts_pg_extension]; + ArrayType *a; + + /* + * We only allow this to be called from an extension's SQL script. + * We shouldn't need any permissions check beyond that. + */ + if (!creating_extension) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("pg_extension_config_dump() can only be called " + "from a SQL script executed by CREATE EXTENSION"))); + + /* + * Check that the table exists and is a member of the extension being + * created. This ensures that we don't need to register a dependency + * to protect the extconfig entry. + */ + tablename = get_rel_name(tableoid); + if (tablename == NULL) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_TABLE), + errmsg("OID %u does not refer to a table", tableoid))); + if (getExtensionOfObject(RelationRelationId, tableoid) != + CurrentExtensionObject) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("table \"%s\" is not a member of the extension being created", + tablename))); + + /* + * Add the table OID and WHERE condition to the extension's extconfig + * and extcondition arrays. + */ + + /* Find the pg_extension tuple */ + extRel = heap_open(ExtensionRelationId, RowExclusiveLock); + + ScanKeyInit(&key[0], + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(CurrentExtensionObject)); + + extScan = systable_beginscan(extRel, ExtensionOidIndexId, true, + SnapshotNow, 1, key); + + extTup = systable_getnext(extScan); + + if (!HeapTupleIsValid(extTup)) /* should not happen */ + elog(ERROR, "extension with oid %u does not exist", + CurrentExtensionObject); + + memset(repl_val, 0, sizeof(repl_val)); + memset(repl_null, false, sizeof(repl_null)); + memset(repl_repl, false, sizeof(repl_repl)); + + /* Build or modify the extconfig value */ + elementDatum = ObjectIdGetDatum(tableoid); + + arrayDatum = heap_getattr(extTup, Anum_pg_extension_extconfig, + RelationGetDescr(extRel), &isnull); + if (isnull) + { + a = construct_array(&elementDatum, 1, + OIDOID, + sizeof(Oid), true, 'i'); + } + else + { + a = DatumGetArrayTypeP(arrayDatum); + Assert(ARR_ELEMTYPE(a) == OIDOID); + Assert(ARR_NDIM(a) == 1); + Assert(ARR_LBOUND(a)[0] == 1); + + arrayIndex = ARR_DIMS(a)[0] + 1; /* add after end */ + + a = array_set(a, 1, &arrayIndex, + elementDatum, + false, + -1 /* varlena array */ , + sizeof(Oid) /* OID's typlen */ , + true /* OID's typbyval */ , + 'i' /* OID's typalign */ ); + } + repl_val[Anum_pg_extension_extconfig - 1] = PointerGetDatum(a); + repl_repl[Anum_pg_extension_extconfig - 1] = true; + + /* Build or modify the extcondition value */ + elementDatum = PointerGetDatum(wherecond); + + arrayDatum = heap_getattr(extTup, Anum_pg_extension_extcondition, + RelationGetDescr(extRel), &isnull); + if (isnull) + { + a = construct_array(&elementDatum, 1, + TEXTOID, + -1, false, 'i'); + } + else + { + a = DatumGetArrayTypeP(arrayDatum); + Assert(ARR_ELEMTYPE(a) == TEXTOID); + Assert(ARR_NDIM(a) == 1); + Assert(ARR_LBOUND(a)[0] == 1); + + arrayIndex = ARR_DIMS(a)[0] + 1; /* add after end */ + + a = array_set(a, 1, &arrayIndex, + elementDatum, + false, + -1 /* varlena array */ , + -1 /* TEXT's typlen */ , + false /* TEXT's typbyval */ , + 'i' /* TEXT's typalign */ ); + } + repl_val[Anum_pg_extension_extcondition - 1] = PointerGetDatum(a); + repl_repl[Anum_pg_extension_extcondition - 1] = true; + + extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel), + repl_val, repl_null, repl_repl); + + simple_heap_update(extRel, &extTup->t_self, extTup); + CatalogUpdateIndexes(extRel, extTup); + + systable_endscan(extScan); + + heap_close(extRel, RowExclusiveLock); + + PG_RETURN_VOID(); +} + +/* + * Execute ALTER EXTENSION SET SCHEMA + */ +void +AlterExtensionNamespace(List *names, const char *newschema) +{ + char *extensionName; + Oid extensionOid; + Oid nspOid; + Oid oldNspOid = InvalidOid; + Relation extRel; + ScanKeyData key[2]; + SysScanDesc extScan; + HeapTuple extTup; + Form_pg_extension extForm; + Relation depRel; + SysScanDesc depScan; + HeapTuple depTup; + + if (list_length(names) != 1) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("extension name cannot be qualified"))); + extensionName = strVal(linitial(names)); + + extensionOid = get_extension_oid(extensionName, false); + + nspOid = LookupCreationNamespace(newschema); + + /* this might later become an ownership test */ + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to use ALTER EXTENSION")))); + + /* Locate the pg_extension tuple */ + extRel = heap_open(ExtensionRelationId, RowExclusiveLock); + + ScanKeyInit(&key[0], + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(extensionOid)); + + extScan = systable_beginscan(extRel, ExtensionOidIndexId, true, + SnapshotNow, 1, key); + + extTup = systable_getnext(extScan); + + if (!HeapTupleIsValid(extTup)) /* should not happen */ + elog(ERROR, "extension with oid %u does not exist", extensionOid); + + /* Copy tuple so we can modify it below */ + extTup = heap_copytuple(extTup); + extForm = (Form_pg_extension) GETSTRUCT(extTup); + + systable_endscan(extScan); + + /* + * If the extension is already in the target schema, just silently + * do nothing. + */ + if (extForm->extnamespace == nspOid) + { + heap_close(extRel, RowExclusiveLock); + return; + } + + /* Check extension is supposed to be relocatable */ + if (!extForm->extrelocatable) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("extension \"%s\" does not support SET SCHEMA", + NameStr(extForm->extname)))); + + /* + * Scan pg_depend to find objects that depend directly on the extension, + * and alter each one's schema. + */ + depRel = heap_open(DependRelationId, AccessShareLock); + + ScanKeyInit(&key[0], + Anum_pg_depend_refclassid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(ExtensionRelationId)); + ScanKeyInit(&key[1], + Anum_pg_depend_refobjid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(extensionOid)); + + depScan = systable_beginscan(depRel, DependReferenceIndexId, true, + SnapshotNow, 2, key); + + while (HeapTupleIsValid(depTup = systable_getnext(depScan))) + { + Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup); + ObjectAddress dep; + Oid dep_oldNspOid; + + /* + * Ignore non-membership dependencies. (Currently, the only other + * case we could see here is a normal dependency from another + * extension.) + */ + if (pg_depend->deptype != DEPENDENCY_EXTENSION) + continue; + + dep.classId = pg_depend->classid; + dep.objectId = pg_depend->objid; + dep.objectSubId = pg_depend->objsubid; + + if (dep.objectSubId != 0) /* should not happen */ + elog(ERROR, "extension should not have a sub-object dependency"); + + dep_oldNspOid = AlterObjectNamespace_oid(dep.classId, + dep.objectId, + nspOid); + + /* + * Remember previous namespace of first object that has one + */ + if (oldNspOid == InvalidOid && dep_oldNspOid != InvalidOid) + oldNspOid = dep_oldNspOid; + + /* + * If not all the objects had the same old namespace (ignoring any + * that are not in namespaces), complain. + */ + if (dep_oldNspOid != InvalidOid && dep_oldNspOid != oldNspOid) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("extension \"%s\" does not support SET SCHEMA", + NameStr(extForm->extname)), + errdetail("%s is not in the extension's schema \"%s\"", + getObjectDescription(&dep), + get_namespace_name(oldNspOid)))); + } + + systable_endscan(depScan); + + relation_close(depRel, AccessShareLock); + + /* Now adjust pg_extension.extnamespace */ + extForm->extnamespace = nspOid; + + simple_heap_update(extRel, &extTup->t_self, extTup); + CatalogUpdateIndexes(extRel, extTup); + + heap_close(extRel, RowExclusiveLock); + + /* update dependencies to point to the new schema */ + changeDependencyFor(ExtensionRelationId, extensionOid, + NamespaceRelationId, oldNspOid, nspOid); +} + +/* + * Execute ALTER EXTENSION UPDATE + */ +void +ExecAlterExtensionStmt(AlterExtensionStmt *stmt) +{ + DefElem *d_new_version = NULL; + char *versionName; + char *oldVersionName; + ExtensionControlFile *control; + Oid extensionOid; + Relation extRel; + ScanKeyData key[1]; + SysScanDesc extScan; + HeapTuple extTup; + List *updateVersions; + Datum datum; + bool isnull; + ListCell *lc; + + /* + * For now, insist on superuser privilege. Later we might want to + * relax this to ownership of the extension. + */ + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to use ALTER EXTENSION")))); + + /* + * We use global variables to track the extension being created, so we + * can create/update only one extension at the same time. + */ + if (creating_extension) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("nested ALTER EXTENSION is not supported"))); + + /* + * Look up the extension --- it must already exist in pg_extension + */ + extRel = heap_open(ExtensionRelationId, AccessShareLock); + + ScanKeyInit(&key[0], + Anum_pg_extension_extname, + BTEqualStrategyNumber, F_NAMEEQ, + CStringGetDatum(stmt->extname)); + + extScan = systable_beginscan(extRel, ExtensionNameIndexId, true, + SnapshotNow, 1, key); + + extTup = systable_getnext(extScan); + + if (!HeapTupleIsValid(extTup)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("extension \"%s\" does not exist", + stmt->extname))); + + extensionOid = HeapTupleGetOid(extTup); + + /* + * Determine the existing version we are updating from + */ + datum = heap_getattr(extTup, Anum_pg_extension_extversion, + RelationGetDescr(extRel), &isnull); + if (isnull) + elog(ERROR, "extversion is null"); + oldVersionName = text_to_cstring(DatumGetTextPP(datum)); + + systable_endscan(extScan); + + heap_close(extRel, AccessShareLock); + + /* + * Read the primary control file. Note we assume that it does not contain + * any non-ASCII data, so there is no need to worry about encoding at this + * point. + */ + control = read_extension_control_file(stmt->extname); + + /* + * Read the statement option list + */ + foreach(lc, stmt->options) + { + DefElem *defel = (DefElem *) lfirst(lc); + + if (strcmp(defel->defname, "new_version") == 0) + { + if (d_new_version) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + d_new_version = defel; + } + else + elog(ERROR, "unrecognized option: %s", defel->defname); + } + + /* + * Determine the version to update to + */ + if (d_new_version && d_new_version->arg) + versionName = strVal(d_new_version->arg); + else if (control->default_version) + versionName = control->default_version; + else + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("version to install must be specified"))); + versionName = NULL; /* keep compiler quiet */ + } + check_valid_version_name(versionName); + + /* + * If we're already at that version, just say so + */ + if (strcmp(oldVersionName, versionName) == 0) + { + ereport(NOTICE, + (errmsg("version \"%s\" of extension \"%s\" is already installed", + versionName, stmt->extname))); + return; + } + + /* + * Identify the series of update script files we need to execute + */ + updateVersions = identify_update_path(control, + oldVersionName, + versionName); + + /* + * Update the pg_extension row and execute the update scripts, one at a + * time + */ + ApplyExtensionUpdates(extensionOid, control, + oldVersionName, updateVersions); +} + +/* + * Apply a series of update scripts as though individual ALTER EXTENSION + * UPDATE commands had been given, including altering the pg_extension row + * and dependencies each time. + * + * This might be more work than necessary, but it ensures that old update + * scripts don't break if newer versions have different control parameters. + */ +static void +ApplyExtensionUpdates(Oid extensionOid, + ExtensionControlFile *pcontrol, + const char *initialVersion, + List *updateVersions) +{ + const char *oldVersionName = initialVersion; + ListCell *lcv; + + foreach(lcv, updateVersions) + { + char *versionName = (char *) lfirst(lcv); + ExtensionControlFile *control; + char *schemaName; + Oid schemaOid; + List *requiredExtensions; + List *requiredSchemas; + Relation extRel; + ScanKeyData key[1]; + SysScanDesc extScan; + HeapTuple extTup; + Form_pg_extension extForm; + Datum values[Natts_pg_extension]; + bool nulls[Natts_pg_extension]; + bool repl[Natts_pg_extension]; + ObjectAddress myself; + ListCell *lc; + + /* + * Fetch parameters for specific version (pcontrol is not changed) + */ + control = read_extension_aux_control_file(pcontrol, versionName); + + /* Find the pg_extension tuple */ + extRel = heap_open(ExtensionRelationId, RowExclusiveLock); + + ScanKeyInit(&key[0], + ObjectIdAttributeNumber, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(extensionOid)); + + extScan = systable_beginscan(extRel, ExtensionOidIndexId, true, + SnapshotNow, 1, key); + + extTup = systable_getnext(extScan); + + if (!HeapTupleIsValid(extTup)) /* should not happen */ + elog(ERROR, "extension with oid %u does not exist", + extensionOid); + + extForm = (Form_pg_extension) GETSTRUCT(extTup); + + /* + * Determine the target schema (set by original install) + */ + schemaOid = extForm->extnamespace; + schemaName = get_namespace_name(schemaOid); + + /* + * Modify extrelocatable and extversion in the pg_extension tuple + */ + memset(values, 0, sizeof(values)); + memset(nulls, 0, sizeof(nulls)); + memset(repl, 0, sizeof(repl)); + + values[Anum_pg_extension_extrelocatable - 1] = + BoolGetDatum(control->relocatable); + repl[Anum_pg_extension_extrelocatable - 1] = true; + values[Anum_pg_extension_extversion - 1] = + CStringGetTextDatum(versionName); + repl[Anum_pg_extension_extversion - 1] = true; + + extTup = heap_modify_tuple(extTup, RelationGetDescr(extRel), + values, nulls, repl); + + simple_heap_update(extRel, &extTup->t_self, extTup); + CatalogUpdateIndexes(extRel, extTup); + + systable_endscan(extScan); + + heap_close(extRel, RowExclusiveLock); + + /* + * Look up the prerequisite extensions for this version, and build + * lists of their OIDs and the OIDs of their target schemas. + */ + requiredExtensions = NIL; + requiredSchemas = NIL; + foreach(lc, control->requires) + { + char *curreq = (char *) lfirst(lc); + Oid reqext; + Oid reqschema; + + /* + * We intentionally don't use get_extension_oid's default error + * message here, because it would be confusing in this context. + */ + reqext = get_extension_oid(curreq, true); + if (!OidIsValid(reqext)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("required extension \"%s\" is not installed", + curreq))); + reqschema = get_extension_schema(reqext); + requiredExtensions = lappend_oid(requiredExtensions, reqext); + requiredSchemas = lappend_oid(requiredSchemas, reqschema); + } + + /* + * Remove and recreate dependencies on prerequisite extensions + */ + deleteDependencyRecordsForClass(ExtensionRelationId, extensionOid, + ExtensionRelationId, + DEPENDENCY_NORMAL); + + myself.classId = ExtensionRelationId; + myself.objectId = extensionOid; + myself.objectSubId = 0; + + foreach(lc, requiredExtensions) + { + Oid reqext = lfirst_oid(lc); + ObjectAddress otherext; + + otherext.classId = ExtensionRelationId; + otherext.objectId = reqext; + otherext.objectSubId = 0; + + recordDependencyOn(&myself, &otherext, DEPENDENCY_NORMAL); + } + + /* + * Finally, execute the update script file + */ + execute_extension_script(extensionOid, control, + oldVersionName, versionName, + requiredSchemas, + schemaName, schemaOid); + + /* + * Update prior-version name and loop around. Since execute_sql_string + * did a final CommandCounterIncrement, we can update the pg_extension + * row again. + */ + oldVersionName = versionName; + } +} + +/* + * Execute ALTER EXTENSION ADD/DROP + */ +void +ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt) +{ + ObjectAddress extension; + ObjectAddress object; + Relation relation; + Oid oldExtension; + + /* + * For now, insist on superuser privilege. Later we might want to + * relax this to ownership of the target object and the extension. + */ + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to use ALTER EXTENSION")))); + + /* Do this next to fail on nonexistent extension */ + extension.classId = ExtensionRelationId; + extension.objectId = get_extension_oid(stmt->extname, false); + extension.objectSubId = 0; + + /* + * Translate the parser representation that identifies the object into + * an ObjectAddress. get_object_address() will throw an error if the + * object does not exist, and will also acquire a lock on the object to + * guard against concurrent DROP and ALTER EXTENSION ADD/DROP operations. + */ + object = get_object_address(stmt->objtype, stmt->objname, stmt->objargs, + &relation, ShareUpdateExclusiveLock); + + /* + * Check existing extension membership. + */ + oldExtension = getExtensionOfObject(object.classId, object.objectId); + + if (stmt->action > 0) + { + /* + * ADD, so complain if object is already attached to some extension. + */ + if (OidIsValid(oldExtension)) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("%s is already a member of extension \"%s\"", + getObjectDescription(&object), + get_extension_name(oldExtension)))); + + /* + * OK, add the dependency. + */ + recordDependencyOn(&object, &extension, DEPENDENCY_EXTENSION); + } + else + { + /* + * DROP, so complain if it's not a member. + */ + if (oldExtension != extension.objectId) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("%s is not a member of extension \"%s\"", + getObjectDescription(&object), + stmt->extname))); + + /* + * OK, drop the dependency. + */ + if (deleteDependencyRecordsForClass(object.classId, object.objectId, + ExtensionRelationId, + DEPENDENCY_EXTENSION) != 1) + elog(ERROR, "unexpected number of extension dependency records"); + } + + /* + * If get_object_address() opened the relation for us, we close it to keep + * the reference count correct - but we retain any locks acquired by + * get_object_address() until commit time, to guard against concurrent + * activity. + */ + if (relation != NULL) + relation_close(relation, NoLock); +} diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c index e9248f64c0..5bdad939d8 100644 --- a/src/backend/commands/foreigncmds.c +++ b/src/backend/commands/foreigncmds.c @@ -398,6 +398,8 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt) Oid fdwhandler; Datum fdwoptions; Oid ownerId; + ObjectAddress myself; + ObjectAddress referenced; /* Must be super user */ if (!superuser()) @@ -458,15 +460,13 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt) heap_freetuple(tuple); + /* record dependencies */ + myself.classId = ForeignDataWrapperRelationId; + myself.objectId = fdwId; + myself.objectSubId = 0; + if (fdwvalidator) { - ObjectAddress myself; - ObjectAddress referenced; - - myself.classId = ForeignDataWrapperRelationId; - myself.objectId = fdwId; - myself.objectSubId = 0; - referenced.classId = ProcedureRelationId; referenced.objectId = fdwvalidator; referenced.objectSubId = 0; @@ -490,6 +490,9 @@ CreateForeignDataWrapper(CreateFdwStmt *stmt) recordDependencyOnOwner(ForeignDataWrapperRelationId, fdwId, ownerId); + /* dependency on extension */ + recordDependencyOnCurrentExtension(&myself); + /* Post creation hook for new foreign data wrapper */ InvokeObjectAccessHook(OAT_POST_CREATE, ForeignDataWrapperRelationId, fdwId, 0); @@ -796,7 +799,7 @@ CreateForeignServer(CreateForeignServerStmt *stmt) heap_freetuple(tuple); - /* Add dependency on FDW and owner */ + /* record dependencies */ myself.classId = ForeignServerRelationId; myself.objectId = srvId; myself.objectSubId = 0; @@ -808,6 +811,9 @@ CreateForeignServer(CreateForeignServerStmt *stmt) recordDependencyOnOwner(ForeignServerRelationId, srvId, ownerId); + /* dependency on extension */ + recordDependencyOnCurrentExtension(&myself); + /* Post creation hook for new foreign server */ InvokeObjectAccessHook(OAT_POST_CREATE, ForeignServerRelationId, srvId, 0); @@ -1079,8 +1085,13 @@ CreateUserMapping(CreateUserMappingStmt *stmt) recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); if (OidIsValid(useId)) + { /* Record the mapped user dependency */ recordDependencyOnOwner(UserMappingRelationId, umId, useId); + } + + /* dependency on extension */ + recordDependencyOnCurrentExtension(&myself); /* Post creation hook for new user mapping */ InvokeObjectAccessHook(OAT_POST_CREATE, UserMappingRelationId, umId, 0); diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index 2a2b7c732e..3f25b3bf02 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -87,7 +87,7 @@ compute_return_type(TypeName *returnType, Oid languageOid, Oid rettype; Type typtup; - typtup = LookupTypeName(NULL, returnType, NULL); + typtup = LookupTypeName(NULL, returnType, NULL, NULL); if (typtup) { @@ -207,7 +207,7 @@ examine_parameter_list(List *parameters, Oid languageOid, Oid toid; Type typtup; - typtup = LookupTypeName(NULL, t, NULL); + typtup = LookupTypeName(NULL, t, NULL, NULL); if (typtup) { if (!((Form_pg_type) GETSTRUCT(typtup))->typisdefined) @@ -1762,6 +1762,9 @@ CreateCast(CreateCastStmt *stmt) recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } + /* dependency on extension */ + recordDependencyOnCurrentExtension(&myself); + /* Post creation hook for new cast */ InvokeObjectAccessHook(OAT_POST_CREATE, CastRelationId, myself.objectId, 0); @@ -1875,13 +1878,7 @@ AlterFunctionNamespace(List *name, List *argtypes, bool isagg, const char *newschema) { Oid procOid; - Oid oldNspOid; Oid nspOid; - HeapTuple tup; - Relation procRel; - Form_pg_proc proc; - - procRel = heap_open(ProcedureRelationId, RowExclusiveLock); /* get function OID */ if (isagg) @@ -1889,20 +1886,33 @@ AlterFunctionNamespace(List *name, List *argtypes, bool isagg, else procOid = LookupFuncNameTypeNames(name, argtypes, false); - /* check permissions on function */ - if (!pg_proc_ownercheck(procOid, GetUserId())) - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, - NameListToString(name)); + /* get schema OID and check its permissions */ + nspOid = LookupCreationNamespace(newschema); + + AlterFunctionNamespace_oid(procOid, nspOid); +} + +Oid +AlterFunctionNamespace_oid(Oid procOid, Oid nspOid) +{ + Oid oldNspOid; + HeapTuple tup; + Relation procRel; + Form_pg_proc proc; + + procRel = heap_open(ProcedureRelationId, RowExclusiveLock); tup = SearchSysCacheCopy1(PROCOID, ObjectIdGetDatum(procOid)); if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for function %u", procOid); proc = (Form_pg_proc) GETSTRUCT(tup); - oldNspOid = proc->pronamespace; + /* check permissions on function */ + if (!pg_proc_ownercheck(procOid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + NameStr(proc->proname)); - /* get schema OID and check its permissions */ - nspOid = LookupCreationNamespace(newschema); + oldNspOid = proc->pronamespace; /* common checks on switching namespaces */ CheckSetNamespace(oldNspOid, nspOid, ProcedureRelationId, procOid); @@ -1916,7 +1926,7 @@ AlterFunctionNamespace(List *name, List *argtypes, bool isagg, (errcode(ERRCODE_DUPLICATE_FUNCTION), errmsg("function \"%s\" already exists in schema \"%s\"", NameStr(proc->proname), - newschema))); + get_namespace_name(nspOid)))); /* OK, modify the pg_proc row */ @@ -1930,11 +1940,13 @@ AlterFunctionNamespace(List *name, List *argtypes, bool isagg, if (changeDependencyFor(ProcedureRelationId, procOid, NamespaceRelationId, oldNspOid, nspOid) != 1) elog(ERROR, "failed to change schema dependency for function \"%s\"", - NameListToString(name)); + NameStr(proc->proname)); heap_freetuple(tup); heap_close(procRel, RowExclusiveLock); + + return oldNspOid; } diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 94ed437002..c8e21b68f5 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -58,6 +58,7 @@ /* non-export function prototypes */ static void CheckPredicate(Expr *predicate); static void ComputeIndexAttrs(IndexInfo *indexInfo, + Oid *collationOidP, Oid *classOidP, int16 *colOptionP, List *attList, @@ -124,6 +125,7 @@ DefineIndex(RangeVar *heapRelation, bool quiet, bool concurrent) { + Oid *collationObjectId; Oid *classObjectId; Oid accessMethodId; Oid relationId; @@ -345,9 +347,10 @@ DefineIndex(RangeVar *heapRelation, indexInfo->ii_Concurrent = concurrent; indexInfo->ii_BrokenHotChain = false; + collationObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid)); classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid)); coloptions = (int16 *) palloc(numberOfAttributes * sizeof(int16)); - ComputeIndexAttrs(indexInfo, classObjectId, coloptions, attributeList, + ComputeIndexAttrs(indexInfo, collationObjectId, classObjectId, coloptions, attributeList, exclusionOpNames, relationId, accessMethodName, accessMethodId, amcanorder, isconstraint); @@ -392,7 +395,7 @@ DefineIndex(RangeVar *heapRelation, indexRelationId = index_create(rel, indexRelationName, indexRelationId, indexInfo, indexColNames, - accessMethodId, tablespaceId, classObjectId, + accessMethodId, tablespaceId, collationObjectId, classObjectId, coloptions, reloptions, primary, isconstraint, deferrable, initdeferred, allowSystemTableMods, @@ -764,6 +767,7 @@ CheckPredicate(Expr *predicate) */ static void ComputeIndexAttrs(IndexInfo *indexInfo, + Oid *collationOidP, Oid *classOidP, int16 *colOptionP, List *attList, /* list of IndexElem's */ @@ -800,6 +804,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo, { IndexElem *attribute = (IndexElem *) lfirst(lc); Oid atttype; + Oid attcollation; /* * Process the column-or-expression to be indexed. @@ -829,6 +834,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo, attform = (Form_pg_attribute) GETSTRUCT(atttuple); indexInfo->ii_KeyAttrNumbers[attn] = attform->attnum; atttype = attform->atttypid; + attcollation = attform->attcollation; ReleaseSysCache(atttuple); } else if (attribute->expr && IsA(attribute->expr, Var) && @@ -839,6 +845,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo, indexInfo->ii_KeyAttrNumbers[attn] = var->varattno; atttype = get_atttype(relId, var->varattno); + attcollation = var->varcollid; } else { @@ -848,6 +855,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo, indexInfo->ii_Expressions = lappend(indexInfo->ii_Expressions, attribute->expr); atttype = exprType(attribute->expr); + attcollation = exprCollation(attribute->expr); /* * We don't currently support generation of an actual query plan @@ -875,6 +883,20 @@ ComputeIndexAttrs(IndexInfo *indexInfo, } /* + * Collation override + */ + if (attribute->collation) + { + if (!type_is_collatable(atttype)) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("collations are not supported by type %s", + format_type_be(atttype)))); + attcollation = get_collation_oid(attribute->collation, false); + } + collationOidP[attn] = attcollation; + + /* * Identify the opclass to use. */ classOidP[attn] = GetIndexOpClass(attribute->opclass, diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c index 662b942038..68072dd421 100644 --- a/src/backend/commands/opclasscmds.c +++ b/src/backend/commands/opclasscmds.c @@ -309,6 +309,9 @@ CreateOpFamily(char *amname, char *opfname, Oid namespaceoid, Oid amoid) /* dependency on owner */ recordDependencyOnOwner(OperatorFamilyRelationId, opfamilyoid, GetUserId()); + /* dependency on extension */ + recordDependencyOnCurrentExtension(&myself); + /* Post creation hook for new operator family */ InvokeObjectAccessHook(OAT_POST_CREATE, OperatorFamilyRelationId, opfamilyoid, 0); @@ -709,6 +712,9 @@ DefineOpClass(CreateOpClassStmt *stmt) /* dependency on owner */ recordDependencyOnOwner(OperatorClassRelationId, opclassoid, GetUserId()); + /* dependency on extension */ + recordDependencyOnCurrentExtension(&myself); + /* Post creation hook for new operator class */ InvokeObjectAccessHook(OAT_POST_CREATE, OperatorClassRelationId, opclassoid, 0); @@ -1997,28 +2003,48 @@ AlterOpClassNamespace(List *name, char *access_method, const char *newschema) { Oid amOid; Relation rel; - Oid oid; + Oid opclassOid; Oid nspOid; amOid = get_am_oid(access_method, false); rel = heap_open(OperatorClassRelationId, RowExclusiveLock); - /* Look up the opclass. */ - oid = get_opclass_oid(amOid, name, false); + /* Look up the opclass */ + opclassOid = get_opclass_oid(amOid, name, false); /* get schema OID */ nspOid = LookupCreationNamespace(newschema); - AlterObjectNamespace(rel, CLAOID, OperatorClassRelationId, - oid, nspOid, - Anum_pg_opfamily_opfname, - Anum_pg_opfamily_opfnamespace, - Anum_pg_opfamily_opfowner, - ACL_KIND_OPCLASS, - false); + AlterObjectNamespace(rel, CLAOID, -1, + opclassOid, nspOid, + Anum_pg_opclass_opcname, + Anum_pg_opclass_opcnamespace, + Anum_pg_opclass_opcowner, + ACL_KIND_OPCLASS); - heap_close(rel, NoLock); + heap_close(rel, RowExclusiveLock); +} + +Oid +AlterOpClassNamespace_oid(Oid opclassOid, Oid newNspOid) +{ + Oid oldNspOid; + Relation rel; + + rel = heap_open(OperatorClassRelationId, RowExclusiveLock); + + oldNspOid = + AlterObjectNamespace(rel, CLAOID, -1, + opclassOid, newNspOid, + Anum_pg_opclass_opcname, + Anum_pg_opclass_opcnamespace, + Anum_pg_opclass_opcowner, + ACL_KIND_OPCLASS); + + heap_close(rel, RowExclusiveLock); + + return oldNspOid; } /* @@ -2186,26 +2212,46 @@ AlterOpFamilyNamespace(List *name, char *access_method, const char *newschema) { Oid amOid; Relation rel; + Oid opfamilyOid; Oid nspOid; - Oid oid; amOid = get_am_oid(access_method, false); rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock); /* Look up the opfamily */ - oid = get_opfamily_oid(amOid, name, false); + opfamilyOid = get_opfamily_oid(amOid, name, false); /* get schema OID */ nspOid = LookupCreationNamespace(newschema); - AlterObjectNamespace(rel, OPFAMILYOID, OperatorFamilyRelationId, - oid, nspOid, + AlterObjectNamespace(rel, OPFAMILYOID, -1, + opfamilyOid, nspOid, Anum_pg_opfamily_opfname, Anum_pg_opfamily_opfnamespace, Anum_pg_opfamily_opfowner, - ACL_KIND_OPFAMILY, - false); + ACL_KIND_OPFAMILY); - heap_close(rel, NoLock); + heap_close(rel, RowExclusiveLock); +} + +Oid +AlterOpFamilyNamespace_oid(Oid opfamilyOid, Oid newNspOid) +{ + Oid oldNspOid; + Relation rel; + + rel = heap_open(OperatorFamilyRelationId, RowExclusiveLock); + + oldNspOid = + AlterObjectNamespace(rel, OPFAMILYOID, -1, + opfamilyOid, newNspOid, + Anum_pg_opfamily_opfname, + Anum_pg_opfamily_opfnamespace, + Anum_pg_opfamily_opfowner, + ACL_KIND_OPFAMILY); + + heap_close(rel, RowExclusiveLock); + + return oldNspOid; } diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c index 35bb76162d..b4374a62f4 100644 --- a/src/backend/commands/operatorcmds.c +++ b/src/backend/commands/operatorcmds.c @@ -477,12 +477,32 @@ AlterOperatorNamespace(List *names, List *argtypes, const char *newschema) /* get schema OID */ nspOid = LookupCreationNamespace(newschema); - AlterObjectNamespace(rel, OPEROID, OperatorRelationId, operOid, nspOid, + AlterObjectNamespace(rel, OPEROID, -1, + operOid, nspOid, Anum_pg_operator_oprname, Anum_pg_operator_oprnamespace, Anum_pg_operator_oprowner, - ACL_KIND_OPER, - false); + ACL_KIND_OPER); - heap_close(rel, NoLock); + heap_close(rel, RowExclusiveLock); +} + +Oid +AlterOperatorNamespace_oid(Oid operOid, Oid newNspOid) +{ + Oid oldNspOid; + Relation rel; + + rel = heap_open(OperatorRelationId, RowExclusiveLock); + + oldNspOid = AlterObjectNamespace(rel, OPEROID, -1, + operOid, newNspOid, + Anum_pg_operator_oprname, + Anum_pg_operator_oprnamespace, + Anum_pg_operator_oprowner, + ACL_KIND_OPER); + + heap_close(rel, RowExclusiveLock); + + return oldNspOid; } diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c index 3860105b26..b36f31ee6d 100644 --- a/src/backend/commands/proclang.c +++ b/src/backend/commands/proclang.c @@ -388,20 +388,25 @@ create_proc_lang(const char *languageName, bool replace, * Create dependencies for the new language. If we are updating an * existing language, first delete any existing pg_depend entries. * (However, since we are not changing ownership or permissions, the - * shared dependencies do *not* need to change, and we leave them alone.) + * shared dependencies do *not* need to change, and we leave them alone. + * We also don't change any pre-existing extension-membership dependency.) */ myself.classId = LanguageRelationId; myself.objectId = HeapTupleGetOid(tup); myself.objectSubId = 0; if (is_update) - deleteDependencyRecordsFor(myself.classId, myself.objectId); + deleteDependencyRecordsFor(myself.classId, myself.objectId, true); /* dependency on owner of language */ if (!is_update) recordDependencyOnOwner(myself.classId, myself.objectId, languageOwner); + /* dependency on extension */ + if (!is_update) + recordDependencyOnCurrentExtension(&myself); + /* dependency on the PL handler function */ referenced.classId = ProcedureRelationId; referenced.objectId = handlerOid; diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c index b927e76abd..27917fc6c0 100644 --- a/src/backend/commands/seclabel.c +++ b/src/backend/commands/seclabel.c @@ -15,6 +15,7 @@ #include "catalog/catalog.h" #include "catalog/indexing.h" #include "catalog/namespace.h" +#include "catalog/pg_collation.h" #include "catalog/pg_seclabel.h" #include "commands/seclabel.h" #include "miscadmin.h" @@ -194,6 +195,7 @@ GetSecurityLabel(const ObjectAddress *object, const char *provider) Anum_pg_seclabel_provider, BTEqualStrategyNumber, F_TEXTEQ, CStringGetTextDatum(provider)); + ScanKeyEntryInitializeCollation(&keys[3], DEFAULT_COLLATION_OID); pg_seclabel = heap_open(SecLabelRelationId, AccessShareLock); @@ -263,6 +265,7 @@ SetSecurityLabel(const ObjectAddress *object, Anum_pg_seclabel_provider, BTEqualStrategyNumber, F_TEXTEQ, CStringGetTextDatum(provider)); + ScanKeyEntryInitializeCollation(&keys[3], DEFAULT_COLLATION_OID); pg_seclabel = heap_open(SecLabelRelationId, RowExclusiveLock); diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 0ff722d6f8..80ad516de1 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -143,53 +143,53 @@ DefineSequence(CreateSeqStmt *seq) switch (i) { case SEQ_COL_NAME: - coldef->typeName = makeTypeNameFromOid(NAMEOID, -1); + coldef->typeName = makeTypeNameFromOid(NAMEOID, -1, InvalidOid); coldef->colname = "sequence_name"; namestrcpy(&name, seq->sequence->relname); value[i - 1] = NameGetDatum(&name); break; case SEQ_COL_LASTVAL: - coldef->typeName = makeTypeNameFromOid(INT8OID, -1); + coldef->typeName = makeTypeNameFromOid(INT8OID, -1, InvalidOid); coldef->colname = "last_value"; value[i - 1] = Int64GetDatumFast(new.last_value); break; case SEQ_COL_STARTVAL: - coldef->typeName = makeTypeNameFromOid(INT8OID, -1); + coldef->typeName = makeTypeNameFromOid(INT8OID, -1, InvalidOid); coldef->colname = "start_value"; value[i - 1] = Int64GetDatumFast(new.start_value); break; case SEQ_COL_INCBY: - coldef->typeName = makeTypeNameFromOid(INT8OID, -1); + coldef->typeName = makeTypeNameFromOid(INT8OID, -1, InvalidOid); coldef->colname = "increment_by"; value[i - 1] = Int64GetDatumFast(new.increment_by); break; case SEQ_COL_MAXVALUE: - coldef->typeName = makeTypeNameFromOid(INT8OID, -1); + coldef->typeName = makeTypeNameFromOid(INT8OID, -1, InvalidOid); coldef->colname = "max_value"; value[i - 1] = Int64GetDatumFast(new.max_value); break; case SEQ_COL_MINVALUE: - coldef->typeName = makeTypeNameFromOid(INT8OID, -1); + coldef->typeName = makeTypeNameFromOid(INT8OID, -1, InvalidOid); coldef->colname = "min_value"; value[i - 1] = Int64GetDatumFast(new.min_value); break; case SEQ_COL_CACHE: - coldef->typeName = makeTypeNameFromOid(INT8OID, -1); + coldef->typeName = makeTypeNameFromOid(INT8OID, -1, InvalidOid); coldef->colname = "cache_value"; value[i - 1] = Int64GetDatumFast(new.cache_value); break; case SEQ_COL_LOG: - coldef->typeName = makeTypeNameFromOid(INT8OID, -1); + coldef->typeName = makeTypeNameFromOid(INT8OID, -1, InvalidOid); coldef->colname = "log_cnt"; value[i - 1] = Int64GetDatum((int64) 1); break; case SEQ_COL_CYCLE: - coldef->typeName = makeTypeNameFromOid(BOOLOID, -1); + coldef->typeName = makeTypeNameFromOid(BOOLOID, -1, InvalidOid); coldef->colname = "is_cycled"; value[i - 1] = BoolGetDatum(new.is_cycled); break; case SEQ_COL_CALLED: - coldef->typeName = makeTypeNameFromOid(BOOLOID, -1); + coldef->typeName = makeTypeNameFromOid(BOOLOID, -1, InvalidOid); coldef->colname = "is_called"; value[i - 1] = BoolGetDatum(false); break; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index e935b7406c..5789a39ba3 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -27,6 +27,7 @@ #include "catalog/indexing.h" #include "catalog/namespace.h" #include "catalog/objectaccess.h" +#include "catalog/pg_collation.h" #include "catalog/pg_constraint.h" #include "catalog/pg_depend.h" #include "catalog/pg_foreign_table.h" @@ -141,7 +142,7 @@ typedef struct AlteredTableInfo List *constraints; /* List of NewConstraint */ List *newvals; /* List of NewColumnValue */ bool new_notnull; /* T if we added new NOT NULL constraints */ - bool new_changeoids; /* T if we added/dropped the OID column */ + bool rewrite; /* T if a rewrite is forced */ Oid newTableSpace; /* new tablespace; 0 means no change */ /* Objects to rebuild after completing ALTER TYPE operations */ List *changedConstraintOids; /* OIDs of constraints to rebuild */ @@ -254,6 +255,7 @@ static void AlterIndexNamespaces(Relation classRel, Relation rel, static void AlterSeqNamespaces(Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, const char *newNspName, LOCKMODE lockmode); +static void ATExecValidateConstraint(Relation rel, const char *constrName); static int transformColumnNameList(Oid relId, List *colList, int16 *attnums, Oid *atttypids); static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid, @@ -264,7 +266,7 @@ static Oid transformFkeyCheckAttrs(Relation pkrel, int numattrs, int16 *attnums, Oid *opclasses); static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts); -static void validateForeignKeyConstraint(Constraint *fkconstraint, +static void validateForeignKeyConstraint(char *conname, Relation rel, Relation pkrel, Oid pkindOid, Oid constraintOid); static void createForeignKeyTriggers(Relation rel, Constraint *fkconstraint, @@ -292,7 +294,7 @@ static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recu AlterTableCmd *cmd, LOCKMODE lockmode); static void ATExecAddColumn(AlteredTableInfo *tab, Relation rel, ColumnDef *colDef, bool isOid, LOCKMODE lockmode); -static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid); +static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid, Oid collid); static void ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd, LOCKMODE lockmode); static void ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode); @@ -335,6 +337,7 @@ static void ATPrepAlterColumnType(List **wqueue, AlteredTableInfo *tab, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode); +static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno); static void ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, const char *colName, TypeName *typeName, LOCKMODE lockmode); static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode); @@ -1421,6 +1424,7 @@ MergeAttributes(List *schema, List *supers, char relpersistence, { Oid defTypeId; int32 deftypmod; + Oid defCollId; /* * Yes, try to merge the two column definitions. They must @@ -1430,7 +1434,7 @@ MergeAttributes(List *schema, List *supers, char relpersistence, (errmsg("merging multiple inherited definitions of column \"%s\"", attributeName))); def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1); - typenameTypeIdAndMod(NULL, def->typeName, &defTypeId, &deftypmod); + typenameTypeIdModColl(NULL, def->typeName, &defTypeId, &deftypmod, &defCollId); if (defTypeId != attribute->atttypid || deftypmod != attribute->atttypmod) ereport(ERROR, @@ -1440,6 +1444,14 @@ MergeAttributes(List *schema, List *supers, char relpersistence, errdetail("%s versus %s", TypeNameToString(def->typeName), format_type_be(attribute->atttypid)))); + if (defCollId != attribute->attcollation) + ereport(ERROR, + (errcode(ERRCODE_COLLATION_MISMATCH), + errmsg("inherited column \"%s\" has a collation conflict", + attributeName), + errdetail("\"%s\" versus \"%s\"", + get_collation_name(defCollId), + get_collation_name(attribute->attcollation)))); /* Copy storage parameter */ if (def->storage == 0) @@ -1467,7 +1479,8 @@ MergeAttributes(List *schema, List *supers, char relpersistence, def = makeNode(ColumnDef); def->colname = pstrdup(attributeName); def->typeName = makeTypeNameFromOid(attribute->atttypid, - attribute->atttypmod); + attribute->atttypmod, + attribute->attcollation); def->inhcount = 1; def->is_local = false; def->is_not_null = attribute->attnotnull; @@ -1593,6 +1606,8 @@ MergeAttributes(List *schema, List *supers, char relpersistence, newTypeId; int32 deftypmod, newtypmod; + Oid defcollid, + newcollid; /* * Yes, try to merge the two column definitions. They must @@ -1602,8 +1617,8 @@ MergeAttributes(List *schema, List *supers, char relpersistence, (errmsg("merging column \"%s\" with inherited definition", attributeName))); def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1); - typenameTypeIdAndMod(NULL, def->typeName, &defTypeId, &deftypmod); - typenameTypeIdAndMod(NULL, newdef->typeName, &newTypeId, &newtypmod); + typenameTypeIdModColl(NULL, def->typeName, &defTypeId, &deftypmod, &defcollid); + typenameTypeIdModColl(NULL, newdef->typeName, &newTypeId, &newtypmod, &newcollid); if (defTypeId != newTypeId || deftypmod != newtypmod) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), @@ -1612,6 +1627,14 @@ MergeAttributes(List *schema, List *supers, char relpersistence, errdetail("%s versus %s", TypeNameToString(def->typeName), TypeNameToString(newdef->typeName)))); + if (defcollid != newcollid) + ereport(ERROR, + (errcode(ERRCODE_COLLATION_MISMATCH), + errmsg("column \"%s\" has a collation conflict", + attributeName), + errdetail("\"%s\" versus \"%s\"", + get_collation_name(defcollid), + get_collation_name(newcollid)))); /* Copy storage parameter */ if (def->storage == 0) @@ -2649,7 +2672,7 @@ AlterTableGetLockLevel(List *cmds) * though don't change the semantic results from normal data reads and writes. * Delaying an ALTER TABLE behind currently active writes only delays the point * where the new strategy begins to take effect, so there is no benefit in waiting. - * In thise case the minimum restriction applies: we don't currently allow + * In this case the minimum restriction applies: we don't currently allow * concurrent catalog updates. */ case AT_SetStatistics: @@ -2660,6 +2683,7 @@ AlterTableGetLockLevel(List *cmds) case AT_SetOptions: case AT_ResetOptions: case AT_SetStorage: + case AT_ValidateConstraint: cmd_lockmode = ShareUpdateExclusiveLock; break; @@ -2887,6 +2911,7 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, ATPrepAddInherit(rel); pass = AT_PASS_MISC; break; + case AT_ValidateConstraint: case AT_EnableTrig: /* ENABLE TRIGGER variants */ case AT_EnableAlwaysTrig: case AT_EnableReplicaTrig: @@ -3054,6 +3079,9 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */ ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def, lockmode); break; + case AT_ValidateConstraint: + ATExecValidateConstraint(rel, cmd->name); + break; case AT_DropConstraint: /* DROP CONSTRAINT */ ATExecDropConstraint(rel, cmd->name, cmd->behavior, false, false, @@ -3193,10 +3221,33 @@ ATRewriteTables(List **wqueue, LOCKMODE lockmode) continue; /* + * If we change column data types or add/remove OIDs, the operation + * has to be propagated to tables that use this table's rowtype as a + * column type. tab->newvals will also be non-NULL in the case where + * we're adding a column with a default. We choose to forbid that + * case as well, since composite types might eventually support + * defaults. + * + * (Eventually we'll probably need to check for composite type + * dependencies even when we're just scanning the table without a + * rewrite, but at the moment a composite type does not enforce any + * constraints, so it's not necessary/appropriate to enforce them + * just during ALTER.) + */ + if (tab->newvals != NIL || tab->rewrite) + { + Relation rel; + + rel = heap_open(tab->relid, NoLock); + find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL); + heap_close(rel, NoLock); + } + + /* * We only need to rewrite the table if at least one column needs to * be recomputed, or we are adding/removing the OID column. */ - if (tab->newvals != NIL || tab->new_changeoids) + if (tab->rewrite) { /* Build a temporary relation and copy data */ Relation OldHeap; @@ -3307,10 +3358,15 @@ ATRewriteTables(List **wqueue, LOCKMODE lockmode) */ refrel = heap_open(con->refrelid, ShareRowExclusiveLock); - validateForeignKeyConstraint(fkconstraint, rel, refrel, + validateForeignKeyConstraint(fkconstraint->conname, rel, refrel, con->refindid, con->conid); + /* + * No need to mark the constraint row as validated, + * we did that when we inserted the row earlier. + */ + heap_close(refrel, NoLock); } } @@ -3378,23 +3434,6 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) } /* - * If we change column data types or add/remove OIDs, the operation has to - * be propagated to tables that use this table's rowtype as a column type. - * newrel will also be non-NULL in the case where we're adding a column - * with a default. We choose to forbid that case as well, since composite - * types might eventually support defaults. - * - * (Eventually we'll probably need to check for composite type - * dependencies even when we're just scanning the table without a rewrite, - * but at the moment a composite type does not enforce any constraints, - * so it's not necessary/appropriate to enforce them just during ALTER.) - */ - if (newrel) - find_composite_type_dependencies(oldrel->rd_rel->reltype, - oldrel->rd_rel->relkind, - RelationGetRelationName(oldrel)); - - /* * Generate the constraint and default execution states */ @@ -3460,6 +3499,15 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) List *dropped_attrs = NIL; ListCell *lc; + if (newrel) + ereport(DEBUG1, + (errmsg("rewriting table \"%s\"", + RelationGetRelationName(oldrel)))); + else + ereport(DEBUG1, + (errmsg("verifying table \"%s\"", + RelationGetRelationName(oldrel)))); + econtext = GetPerTupleExprContext(estate); /* @@ -3502,7 +3550,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { - if (newrel) + if (tab->rewrite) { Oid tupOid = InvalidOid; @@ -3860,8 +3908,8 @@ ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, * to reject the ALTER. (How safe is this really?) */ void -find_composite_type_dependencies(Oid typeOid, char origRelkind, - const char *origRelname) +find_composite_type_dependencies(Oid typeOid, Relation origRelation, + const char *origTypeName) { Relation depRel; ScanKeyData key[2]; @@ -3905,16 +3953,20 @@ find_composite_type_dependencies(Oid typeOid, char origRelkind, if (rel->rd_rel->relkind == RELKIND_RELATION) { const char *msg; - if (origRelkind == RELKIND_COMPOSITE_TYPE) + + if (origTypeName + || origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE) msg = gettext_noop("cannot alter type \"%s\" because column \"%s\".\"%s\" uses it"); - else if (origRelkind == RELKIND_FOREIGN_TABLE) + else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE) msg = gettext_noop("cannot alter foreign table \"%s\" because column \"%s\".\"%s\" uses its rowtype"); else msg = gettext_noop("cannot alter table \"%s\" because column \"%s\".\"%s\" uses its rowtype"); + ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg(msg, - origRelname, + origTypeName ? origTypeName + : RelationGetRelationName(origRelation), RelationGetRelationName(rel), NameStr(att->attname)))); } @@ -3925,7 +3977,7 @@ find_composite_type_dependencies(Oid typeOid, char origRelkind, * recursively check for indirect dependencies via its rowtype. */ find_composite_type_dependencies(rel->rd_rel->reltype, - origRelkind, origRelname); + origRelation, origTypeName); } relation_close(rel, AccessShareLock); @@ -3941,7 +3993,7 @@ find_composite_type_dependencies(Oid typeOid, char origRelkind, */ arrayOid = get_array_type(typeOid); if (OidIsValid(arrayOid)) - find_composite_type_dependencies(arrayOid, origRelkind, origRelname); + find_composite_type_dependencies(arrayOid, origRelation, origTypeName); } @@ -4054,6 +4106,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, HeapTuple typeTuple; Oid typeOid; int32 typmod; + Oid collOid; Form_pg_type tform; Expr *defval; @@ -4074,15 +4127,24 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple); Oid ctypeId; int32 ctypmod; + Oid ccollid; /* Child column must match by type */ - typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod); + typenameTypeIdModColl(NULL, colDef->typeName, &ctypeId, &ctypmod, &ccollid); if (ctypeId != childatt->atttypid || ctypmod != childatt->atttypmod) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("child table \"%s\" has different type for column \"%s\"", RelationGetRelationName(rel), colDef->colname))); + if (ccollid != childatt->attcollation) + ereport(ERROR, + (errcode(ERRCODE_COLLATION_MISMATCH), + errmsg("child table \"%s\" has different collation for column \"%s\"", + RelationGetRelationName(rel), colDef->colname), + errdetail("\"%s\" versus \"%s\"", + get_collation_name(ccollid), + get_collation_name(childatt->attcollation)))); /* If it's OID, child column must actually be OID */ if (isOid && childatt->attnum != ObjectIdAttributeNumber) @@ -4140,7 +4202,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, MaxHeapAttributeNumber))); } - typeTuple = typenameType(NULL, colDef->typeName, &typmod); + typeTuple = typenameType(NULL, colDef->typeName, &typmod, &collOid); tform = (Form_pg_type) GETSTRUCT(typeTuple); typeOid = HeapTupleGetOid(typeTuple); @@ -4165,6 +4227,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, attribute.attisdropped = false; attribute.attislocal = colDef->is_local; attribute.attinhcount = colDef->inhcount; + attribute.attcollation = collOid; /* attribute.attacl is handled by InsertPgAttributeTuple */ ReleaseSysCache(typeTuple); @@ -4285,6 +4348,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, newval->expr = defval; tab->newvals = lappend(tab->newvals, newval); + tab->rewrite = true; } /* @@ -4301,19 +4365,19 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, * table to fix that. */ if (isOid) - tab->new_changeoids = true; + tab->rewrite = true; /* * Add needed dependency entries for the new column. */ - add_column_datatype_dependency(myrelid, newattnum, attribute.atttypid); + add_column_datatype_dependency(myrelid, newattnum, attribute.atttypid, attribute.attcollation); } /* * Install a column's dependency on its datatype. */ static void -add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid) +add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid, Oid collid) { ObjectAddress myself, referenced; @@ -4325,6 +4389,14 @@ add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid) referenced.objectId = typid; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + + if (collid) + { + referenced.classId = CollationRelationId; + referenced.objectId = collid; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } } /* @@ -4342,7 +4414,7 @@ ATPrepAddOids(List **wqueue, Relation rel, bool recurse, AlterTableCmd *cmd, LOC ColumnDef *cdef = makeNode(ColumnDef); cdef->colname = pstrdup("oid"); - cdef->typeName = makeTypeNameFromOid(OIDOID, -1); + cdef->typeName = makeTypeNameFromOid(OIDOID, -1, InvalidOid); cdef->inhcount = 0; cdef->is_local = true; cdef->is_not_null = true; @@ -4974,7 +5046,7 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName, tab = ATGetQueueEntry(wqueue, rel); /* Tell Phase 3 to physically remove the OID column */ - tab->new_changeoids = true; + tab->rewrite = true; } } @@ -4998,7 +5070,7 @@ ATExecAddIndex(AlteredTableInfo *tab, Relation rel, /* suppress schema rights check when rebuilding existing index */ check_rights = !is_rebuild; /* skip index build if phase 3 will have to rewrite table anyway */ - skip_build = (tab->newvals != NIL); + skip_build = tab->rewrite; /* suppress notices when rebuilding existing index */ quiet = is_rebuild; @@ -5509,6 +5581,7 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, CONSTRAINT_FOREIGN, fkconstraint->deferrable, fkconstraint->initdeferred, + !fkconstraint->skip_validation, RelationGetRelid(rel), fkattnum, numfks, @@ -5538,7 +5611,8 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, /* * Tell Phase 3 to check that the constraint is satisfied by existing rows - * (we can skip this during table creation). + * We can skip this during table creation or if requested explicitly + * by specifying NOT VALID on an alter table statement. */ if (!fkconstraint->skip_validation) { @@ -5561,6 +5635,85 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, heap_close(pkrel, NoLock); } +/* + * ALTER TABLE VALIDATE CONSTRAINT + */ +static void +ATExecValidateConstraint(Relation rel, const char *constrName) +{ + Relation conrel; + SysScanDesc scan; + ScanKeyData key; + HeapTuple tuple; + Form_pg_constraint con = NULL; + bool found = false; + + conrel = heap_open(ConstraintRelationId, RowExclusiveLock); + + /* + * Find and check the target constraint + */ + ScanKeyInit(&key, + Anum_pg_constraint_conrelid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(RelationGetRelid(rel))); + scan = systable_beginscan(conrel, ConstraintRelidIndexId, + true, SnapshotNow, 1, &key); + + while (HeapTupleIsValid(tuple = systable_getnext(scan))) + { + con = (Form_pg_constraint) GETSTRUCT(tuple); + if (con->contype == CONSTRAINT_FOREIGN && + strcmp(NameStr(con->conname), constrName) == 0) + { + found = true; + break; + } + } + + if (!found) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("foreign key constraint \"%s\" of relation \"%s\" does not exist", + constrName, RelationGetRelationName(rel)))); + + if (!con->convalidated) + { + Oid conid = HeapTupleGetOid(tuple); + HeapTuple copyTuple = heap_copytuple(tuple); + Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple); + Relation refrel; + + /* + * Triggers are already in place on both tables, so a + * concurrent write that alters the result here is not + * possible. Normally we can run a query here to do the + * validation, which would only require AccessShareLock. + * In some cases, it is possible that we might need to + * fire triggers to perform the check, so we take a lock + * at RowShareLock level just in case. + */ + refrel = heap_open(con->confrelid, RowShareLock); + + validateForeignKeyConstraint((char *)constrName, rel, refrel, + con->conindid, + conid); + + /* + * Now update the catalog, while we have the door open. + */ + copy_con->convalidated = true; + simple_heap_update(conrel, ©Tuple->t_self, copyTuple); + CatalogUpdateIndexes(conrel, copyTuple); + heap_freetuple(copyTuple); + + heap_close(refrel, NoLock); + } + + systable_endscan(scan); + + heap_close(conrel, RowExclusiveLock); +} /* * transformColumnNameList - transform list of column names @@ -5866,7 +6019,7 @@ checkFkeyPermissions(Relation rel, int16 *attnums, int natts) * Caller must have opened and locked both relations appropriately. */ static void -validateForeignKeyConstraint(Constraint *fkconstraint, +validateForeignKeyConstraint(char *conname, Relation rel, Relation pkrel, Oid pkindOid, @@ -5876,12 +6029,15 @@ validateForeignKeyConstraint(Constraint *fkconstraint, HeapTuple tuple; Trigger trig; + ereport(DEBUG1, + (errmsg("validating foreign key constraint \"%s\"", conname))); + /* * Build a trigger call structure; we'll need it either way. */ MemSet(&trig, 0, sizeof(trig)); trig.tgoid = InvalidOid; - trig.tgname = fkconstraint->conname; + trig.tgname = conname; trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN; trig.tgisinternal = TRUE; trig.tgconstrrelid = RelationGetRelid(pkrel); @@ -6322,6 +6478,7 @@ ATPrepAlterColumnType(List **wqueue, AttrNumber attnum; Oid targettype; int32 targettypmod; + Oid targetcollid; Node *transform; NewColumnValue *newval; ParseState *pstate = make_parsestate(NULL); @@ -6356,7 +6513,7 @@ ATPrepAlterColumnType(List **wqueue, colName))); /* Look up the target type */ - typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod); + typenameTypeIdModColl(NULL, typeName, &targettype, &targettypmod, &targetcollid); /* make sure datatype is legal for a column */ CheckAttributeType(colName, targettype, false); @@ -6408,7 +6565,7 @@ ATPrepAlterColumnType(List **wqueue, else { transform = (Node *) makeVar(1, attnum, - attTup->atttypid, attTup->atttypmod, + attTup->atttypid, attTup->atttypmod, attTup->attcollation, 0); } @@ -6433,6 +6590,8 @@ ATPrepAlterColumnType(List **wqueue, newval->expr = (Expr *) transform; tab->newvals = lappend(tab->newvals, newval); + if (ATColumnChangeRequiresRewrite(transform, attnum)) + tab->rewrite = true; } else if (tab->relkind == RELKIND_FOREIGN_TABLE) { @@ -6449,9 +6608,7 @@ ATPrepAlterColumnType(List **wqueue, * For composite types, do this check now. Tables will check * it later when the table is being rewritten. */ - find_composite_type_dependencies(rel->rd_rel->reltype, - rel->rd_rel->relkind, - RelationGetRelationName(rel)); + find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL); } ReleaseSysCache(tuple); @@ -6474,6 +6631,41 @@ ATPrepAlterColumnType(List **wqueue, ATTypedTableRecursion(wqueue, rel, cmd, lockmode); } +/* + * When the data type of a column is changed, a rewrite might not be required + * if the new type is sufficiently identical to the old one, and the USING + * clause isn't trying to insert some other value. It's safe to skip the + * rewrite if the old type is binary coercible to the new type, or if the + * new type is an unconstrained domain over the old type. In the case of a + * constrained domain, we could get by with scanning the table and checking + * the constraint rather than actually rewriting it, but we don't currently + * try to do that. + */ +static bool +ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno) +{ + Assert(expr != NULL); + + for (;;) + { + /* only one varno, so no need to check that */ + if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno) + return false; + else if (IsA(expr, RelabelType)) + expr = (Node *) ((RelabelType *) expr)->arg; + else if (IsA(expr, CoerceToDomain)) + { + CoerceToDomain *d = (CoerceToDomain *) expr; + + if (GetDomainConstraints(d->resulttype) != NIL) + return true; + expr = (Node *) d->arg; + } + else + return true; + } +} + static void ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, const char *colName, TypeName *typeName, LOCKMODE lockmode) @@ -6485,6 +6677,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, Form_pg_type tform; Oid targettype; int32 targettypmod; + Oid targetcollid; Node *defaultexpr; Relation attrelation; Relation depRel; @@ -6513,7 +6706,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, colName))); /* Look up the target type (should not fail, since prep found it) */ - typeTuple = typenameType(NULL, typeName, &targettypmod); + typeTuple = typenameType(NULL, typeName, &targettypmod, &targetcollid); tform = (Form_pg_type) GETSTRUCT(typeTuple); targettype = HeapTupleGetOid(typeTuple); @@ -6705,6 +6898,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, case OCLASS_PROC: case OCLASS_TYPE: case OCLASS_CAST: + case OCLASS_COLLATION: case OCLASS_CONVERSION: case OCLASS_LANGUAGE: case OCLASS_LARGEOBJECT: @@ -6725,6 +6919,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, case OCLASS_FOREIGN_SERVER: case OCLASS_USER_MAPPING: case OCLASS_DEFACL: + case OCLASS_EXTENSION: /* * We don't expect any of these sorts of objects to depend on @@ -6745,7 +6940,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, /* * Now scan for dependencies of this column on other things. The only * thing we should find is the dependency on the column datatype, which we - * want to remove. + * want to remove, and possibly an associated collation. */ ScanKeyInit(&key[0], Anum_pg_depend_classid, @@ -6770,8 +6965,10 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, if (foundDep->deptype != DEPENDENCY_NORMAL) elog(ERROR, "found unexpected dependency type '%c'", foundDep->deptype); - if (foundDep->refclassid != TypeRelationId || - foundDep->refobjid != attTup->atttypid) + if (!(foundDep->refclassid == TypeRelationId && + foundDep->refobjid == attTup->atttypid) && + !(foundDep->refclassid == CollationRelationId && + foundDep->refobjid == attTup->attcollation)) elog(ERROR, "found unexpected dependency for column"); simple_heap_delete(depRel, &depTup->t_self); @@ -6787,6 +6984,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, */ attTup->atttypid = targettype; attTup->atttypmod = targettypmod; + attTup->attcollation = targetcollid; attTup->attndims = list_length(typeName->arrayBounds); attTup->attlen = tform->typlen; attTup->attbyval = tform->typbyval; @@ -6803,7 +7001,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, heap_close(attrelation, RowExclusiveLock); /* Install dependency on new datatype */ - add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype); + add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype, targetcollid); /* * Drop any pg_statistic entry for the column, since it's now wrong type diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 48021388fc..8d996a87c7 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -422,6 +422,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, CONSTRAINT_TRIGGER, stmt->deferrable, stmt->initdeferred, + true, RelationGetRelid(rel), NULL, /* no conkey */ 0, diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c index 7afd2f896e..81f129dff6 100644 --- a/src/backend/commands/tsearchcmds.c +++ b/src/backend/commands/tsearchcmds.c @@ -135,6 +135,9 @@ makeParserDependencies(HeapTuple tuple) referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + /* dependency on extension */ + recordDependencyOnCurrentExtension(&myself); + /* dependencies on functions */ referenced.classId = ProcedureRelationId; referenced.objectSubId = 0; @@ -414,12 +417,33 @@ AlterTSParserNamespace(List *name, const char *newschema) /* get schema OID */ nspOid = LookupCreationNamespace(newschema); - AlterObjectNamespace(rel, TSPARSEROID, TSParserRelationId, prsId, nspOid, + AlterObjectNamespace(rel, TSPARSEROID, TSPARSERNAMENSP, + prsId, nspOid, Anum_pg_ts_parser_prsname, Anum_pg_ts_parser_prsnamespace, - -1, -1, true); + -1, -1); - heap_close(rel, NoLock); + heap_close(rel, RowExclusiveLock); +} + +Oid +AlterTSParserNamespace_oid(Oid prsId, Oid newNspOid) +{ + Oid oldNspOid; + Relation rel; + + rel = heap_open(TSParserRelationId, RowExclusiveLock); + + oldNspOid = + AlterObjectNamespace(rel, TSPARSEROID, TSPARSERNAMENSP, + prsId, newNspOid, + Anum_pg_ts_parser_prsname, + Anum_pg_ts_parser_prsnamespace, + -1, -1); + + heap_close(rel, RowExclusiveLock); + + return oldNspOid; } /* ---------------------- TS Dictionary commands -----------------------*/ @@ -447,6 +471,9 @@ makeDictionaryDependencies(HeapTuple tuple) /* dependency on owner */ recordDependencyOnOwner(myself.classId, myself.objectId, dict->dictowner); + /* dependency on extension */ + recordDependencyOnCurrentExtension(&myself); + /* dependency on template */ referenced.classId = TSTemplateRelationId; referenced.objectId = dict->dicttemplate; @@ -668,14 +695,35 @@ AlterTSDictionaryNamespace(List *name, const char *newschema) /* get schema OID */ nspOid = LookupCreationNamespace(newschema); - AlterObjectNamespace(rel, TSDICTOID, TSDictionaryRelationId, dictId, nspOid, + AlterObjectNamespace(rel, TSDICTOID, TSDICTNAMENSP, + dictId, nspOid, Anum_pg_ts_dict_dictname, Anum_pg_ts_dict_dictnamespace, Anum_pg_ts_dict_dictowner, - ACL_KIND_TSDICTIONARY, - true); + ACL_KIND_TSDICTIONARY); - heap_close(rel, NoLock); + heap_close(rel, RowExclusiveLock); +} + +Oid +AlterTSDictionaryNamespace_oid(Oid dictId, Oid newNspOid) +{ + Oid oldNspOid; + Relation rel; + + rel = heap_open(TSDictionaryRelationId, RowExclusiveLock); + + oldNspOid = + AlterObjectNamespace(rel, TSDICTOID, TSDICTNAMENSP, + dictId, newNspOid, + Anum_pg_ts_dict_dictname, + Anum_pg_ts_dict_dictnamespace, + Anum_pg_ts_dict_dictowner, + ACL_KIND_TSDICTIONARY); + + heap_close(rel, RowExclusiveLock); + + return oldNspOid; } /* @@ -1012,6 +1060,9 @@ makeTSTemplateDependencies(HeapTuple tuple) referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + /* dependency on extension */ + recordDependencyOnCurrentExtension(&myself); + /* dependencies on functions */ referenced.classId = ProcedureRelationId; referenced.objectSubId = 0; @@ -1177,13 +1228,33 @@ AlterTSTemplateNamespace(List *name, const char *newschema) /* get schema OID */ nspOid = LookupCreationNamespace(newschema); - AlterObjectNamespace(rel, TSTEMPLATEOID, TSTemplateRelationId, + AlterObjectNamespace(rel, TSTEMPLATEOID, TSTEMPLATENAMENSP, tmplId, nspOid, Anum_pg_ts_template_tmplname, Anum_pg_ts_template_tmplnamespace, - -1, -1, true); + -1, -1); - heap_close(rel, NoLock); + heap_close(rel, RowExclusiveLock); +} + +Oid +AlterTSTemplateNamespace_oid(Oid tmplId, Oid newNspOid) +{ + Oid oldNspOid; + Relation rel; + + rel = heap_open(TSTemplateRelationId, RowExclusiveLock); + + oldNspOid = + AlterObjectNamespace(rel, TSTEMPLATEOID, TSTEMPLATENAMENSP, + tmplId, newNspOid, + Anum_pg_ts_template_tmplname, + Anum_pg_ts_template_tmplnamespace, + -1, -1); + + heap_close(rel, RowExclusiveLock); + + return oldNspOid; } /* @@ -1313,10 +1384,10 @@ makeConfigurationDependencies(HeapTuple tuple, bool removeOld, myself.objectId = HeapTupleGetOid(tuple); myself.objectSubId = 0; - /* for ALTER case, first flush old dependencies */ + /* for ALTER case, first flush old dependencies, except extension deps */ if (removeOld) { - deleteDependencyRecordsFor(myself.classId, myself.objectId); + deleteDependencyRecordsFor(myself.classId, myself.objectId, true); deleteSharedDependencyRecordsFor(myself.classId, myself.objectId, 0); } @@ -1336,6 +1407,10 @@ makeConfigurationDependencies(HeapTuple tuple, bool removeOld, /* dependency on owner */ recordDependencyOnOwner(myself.classId, myself.objectId, cfg->cfgowner); + /* dependency on extension */ + if (!removeOld) + recordDependencyOnCurrentExtension(&myself); + /* dependency on parser */ referenced.classId = TSParserRelationId; referenced.objectId = cfg->cfgparser; @@ -1603,14 +1678,35 @@ AlterTSConfigurationNamespace(List *name, const char *newschema) /* get schema OID */ nspOid = LookupCreationNamespace(newschema); - AlterObjectNamespace(rel, TSCONFIGOID, TSConfigRelationId, cfgId, nspOid, + AlterObjectNamespace(rel, TSCONFIGOID, TSCONFIGNAMENSP, + cfgId, nspOid, Anum_pg_ts_config_cfgname, Anum_pg_ts_config_cfgnamespace, Anum_pg_ts_config_cfgowner, - ACL_KIND_TSCONFIGURATION, - false); + ACL_KIND_TSCONFIGURATION); - heap_close(rel, NoLock); + heap_close(rel, RowExclusiveLock); +} + +Oid +AlterTSConfigurationNamespace_oid(Oid cfgId, Oid newNspOid) +{ + Oid oldNspOid; + Relation rel; + + rel = heap_open(TSConfigRelationId, RowExclusiveLock); + + oldNspOid = + AlterObjectNamespace(rel, TSCONFIGOID, TSCONFIGNAMENSP, + cfgId, newNspOid, + Anum_pg_ts_config_cfgname, + Anum_pg_ts_config_cfgnamespace, + Anum_pg_ts_config_cfgowner, + ACL_KIND_TSCONFIGURATION); + + heap_close(rel, RowExclusiveLock); + + return oldNspOid; } /* diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 619929ffe5..be1f1d791f 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -38,6 +38,7 @@ #include "catalog/dependency.h" #include "catalog/heap.h" #include "catalog/indexing.h" +#include "catalog/pg_collation.h" #include "catalog/pg_constraint.h" #include "catalog/pg_depend.h" #include "catalog/pg_enum.h" @@ -118,6 +119,7 @@ DefineType(List *names, List *parameters) bool byValue = false; char alignment = 'i'; /* default alignment */ char storage = 'p'; /* default TOAST storage method */ + Oid collation = InvalidOid; DefElem *likeTypeEl = NULL; DefElem *internalLengthEl = NULL; DefElem *inputNameEl = NULL; @@ -135,6 +137,7 @@ DefineType(List *names, List *parameters) DefElem *byValueEl = NULL; DefElem *alignmentEl = NULL; DefElem *storageEl = NULL; + DefElem *collatableEl = NULL; Oid inputOid; Oid outputOid; Oid receiveOid = InvalidOid; @@ -261,6 +264,8 @@ DefineType(List *names, List *parameters) defelp = &alignmentEl; else if (pg_strcasecmp(defel->defname, "storage") == 0) defelp = &storageEl; + else if (pg_strcasecmp(defel->defname, "collatable") == 0) + defelp = &collatableEl; else { /* WARNING, not ERROR, for historical backwards-compatibility */ @@ -287,7 +292,7 @@ DefineType(List *names, List *parameters) Type likeType; Form_pg_type likeForm; - likeType = typenameType(NULL, defGetTypeName(likeTypeEl), NULL); + likeType = typenameType(NULL, defGetTypeName(likeTypeEl), NULL, NULL); likeForm = (Form_pg_type) GETSTRUCT(likeType); internalLength = likeForm->typlen; byValue = likeForm->typbyval; @@ -390,6 +395,8 @@ DefineType(List *names, List *parameters) (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("storage \"%s\" not recognized", a))); } + if (collatableEl) + collation = defGetBoolean(collatableEl) ? DEFAULT_COLLATION_OID : InvalidOid; /* * make sure we have our required definitions @@ -562,7 +569,8 @@ DefineType(List *names, List *parameters) storage, /* TOAST strategy */ -1, /* typMod (Domains only) */ 0, /* Array Dimensions of typbasetype */ - false); /* Type NOT NULL */ + false, /* Type NOT NULL */ + collation); /* * Create the array type that goes with it. @@ -601,7 +609,8 @@ DefineType(List *names, List *parameters) 'x', /* ARRAY is always toastable */ -1, /* typMod (Domains only) */ 0, /* Array dimensions of typbasetype */ - false); /* Type NOT NULL */ + false, /* Type NOT NULL */ + collation); pfree(array_type); } @@ -640,7 +649,7 @@ RemoveTypes(DropStmt *drop) typename = makeTypeNameFromNameList(names); /* Use LookupTypeName here so that shell types can be removed. */ - tup = LookupTypeName(NULL, typename, NULL); + tup = LookupTypeName(NULL, typename, NULL, NULL); if (tup == NULL) { if (!drop->missing_ok) @@ -767,6 +776,7 @@ DefineDomain(CreateDomainStmt *stmt) Oid old_type_oid; Form_pg_type baseType; int32 basetypeMod; + Oid baseColl; /* Convert list of names to a name and namespace */ domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname, @@ -797,7 +807,7 @@ DefineDomain(CreateDomainStmt *stmt) /* * Look up the base type. */ - typeTup = typenameType(NULL, stmt->typeName, &basetypeMod); + typeTup = typenameType(NULL, stmt->typeName, &basetypeMod, &baseColl); baseType = (Form_pg_type) GETSTRUCT(typeTup); basetypeoid = HeapTupleGetOid(typeTup); @@ -1040,7 +1050,8 @@ DefineDomain(CreateDomainStmt *stmt) storage, /* TOAST strategy */ basetypeMod, /* typeMod value */ typNDims, /* Array dimensions for base type */ - typNotNull); /* Type NOT NULL */ + typNotNull, /* Type NOT NULL */ + baseColl); /* * Process constraints which refer to the domain ID returned by TypeCreate @@ -1149,7 +1160,8 @@ DefineEnum(CreateEnumStmt *stmt) 'p', /* TOAST strategy always plain */ -1, /* typMod (Domains only) */ 0, /* Array dimensions of typbasetype */ - false); /* Type NOT NULL */ + false, /* Type NOT NULL */ + InvalidOid); /* typcollation */ /* Enter the enum's values into pg_enum */ EnumValuesCreate(enumTypeOid, stmt->vals); @@ -1188,7 +1200,8 @@ DefineEnum(CreateEnumStmt *stmt) 'x', /* ARRAY is always toastable */ -1, /* typMod (Domains only) */ 0, /* Array dimensions of typbasetype */ - false); /* Type NOT NULL */ + false, /* Type NOT NULL */ + InvalidOid); /* typcollation */ pfree(enumArrayName); } @@ -1723,6 +1736,7 @@ AlterDomainDefault(List *names, Node *defaultRaw) InvalidOid, false, /* a domain isn't an implicit array */ typTup->typbasetype, + typTup->typcollation, defaultExpr, true); /* Rebuild is true */ @@ -2183,7 +2197,7 @@ get_rels_with_domain(Oid domainOid, LOCKMODE lockmode) */ if (OidIsValid(rel->rd_rel->reltype)) find_composite_type_dependencies(rel->rd_rel->reltype, - RELKIND_COMPOSITE_TYPE, + NULL, format_type_be(domainOid)); /* Otherwise we can ignore views, composite types, etc */ @@ -2378,6 +2392,7 @@ domainAddConstraint(Oid domainOid, Oid domainNamespace, Oid baseTypeOid, CONSTRAINT_CHECK, /* Constraint Type */ false, /* Is Deferrable */ false, /* Is Deferred */ + true, /* Is Validated */ InvalidOid, /* not a relation constraint */ NULL, 0, @@ -2614,7 +2629,7 @@ AlterTypeOwner(List *names, Oid newOwnerId) typename = makeTypeNameFromNameList(names); /* Use LookupTypeName here so that shell types can be processed */ - tup = LookupTypeName(NULL, typename, NULL); + tup = LookupTypeName(NULL, typename, NULL, NULL); if (tup == NULL) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), @@ -2766,20 +2781,27 @@ AlterTypeNamespace(List *names, const char *newschema) TypeName *typename; Oid typeOid; Oid nspOid; - Oid elemOid; /* Make a TypeName so we can use standard type lookup machinery */ typename = makeTypeNameFromNameList(names); typeOid = typenameTypeId(NULL, typename); + /* get schema OID and check its permissions */ + nspOid = LookupCreationNamespace(newschema); + + AlterTypeNamespace_oid(typeOid, nspOid); +} + +Oid +AlterTypeNamespace_oid(Oid typeOid, Oid nspOid) +{ + Oid elemOid; + /* check permissions on type */ if (!pg_type_ownercheck(typeOid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, format_type_be(typeOid)); - /* get schema OID and check its permissions */ - nspOid = LookupCreationNamespace(newschema); - /* don't allow direct alteration of array types */ elemOid = get_element_type(typeOid); if (OidIsValid(elemOid) && get_array_type(elemOid) == typeOid) @@ -2791,7 +2813,7 @@ AlterTypeNamespace(List *names, const char *newschema) format_type_be(elemOid)))); /* and do the work */ - AlterTypeNamespaceInternal(typeOid, nspOid, false, true); + return AlterTypeNamespaceInternal(typeOid, nspOid, false, true); } /* @@ -2806,8 +2828,10 @@ AlterTypeNamespace(List *names, const char *newschema) * If errorOnTableType is TRUE, the function errors out if the type is * a table type. ALTER TABLE has to be used to move a table to a new * namespace. + * + * Returns the type's old namespace OID. */ -void +Oid AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, bool isImplicitArray, bool errorOnTableType) @@ -2914,4 +2938,6 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, /* Recursively alter the associated array type, if any */ if (OidIsValid(arrayOid)) AlterTypeNamespaceInternal(arrayOid, nspOid, true, true); + + return oldNspOid; } diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index a684172c8a..22dfc923cf 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -120,7 +120,8 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace) def->colname = pstrdup(tle->resname); def->typeName = makeTypeNameFromOid(exprType((Node *) tle->expr), - exprTypmod((Node *) tle->expr)); + exprTypmod((Node *) tle->expr), + exprCollation((Node *) tle->expr)); def->inhcount = 0; def->is_local = true; def->is_not_null = false; diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 600f7e0334..7df7f5cdf0 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -742,6 +742,7 @@ InitPlan(QueryDesc *queryDesc, int eflags) erm->relation = relation; erm->rti = rc->rti; erm->prti = rc->prti; + erm->rowmarkId = rc->rowmarkId; erm->markType = rc->markType; erm->noWait = rc->noWait; ItemPointerSetInvalid(&(erm->curCtid)); @@ -1425,23 +1426,29 @@ ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist) /* if child rel, need tableoid */ if (erm->rti != erm->prti) { - snprintf(resname, sizeof(resname), "tableoid%u", erm->prti); + snprintf(resname, sizeof(resname), "tableoid%u", erm->rowmarkId); aerm->toidAttNo = ExecFindJunkAttributeInTlist(targetlist, resname); + if (!AttributeNumberIsValid(aerm->toidAttNo)) + elog(ERROR, "could not find junk %s column", resname); } /* always need ctid for real relations */ - snprintf(resname, sizeof(resname), "ctid%u", erm->prti); + snprintf(resname, sizeof(resname), "ctid%u", erm->rowmarkId); aerm->ctidAttNo = ExecFindJunkAttributeInTlist(targetlist, resname); + if (!AttributeNumberIsValid(aerm->ctidAttNo)) + elog(ERROR, "could not find junk %s column", resname); } else { Assert(erm->markType == ROW_MARK_COPY); - snprintf(resname, sizeof(resname), "wholerow%u", erm->prti); + snprintf(resname, sizeof(resname), "wholerow%u", erm->rowmarkId); aerm->wholeAttNo = ExecFindJunkAttributeInTlist(targetlist, resname); + if (!AttributeNumberIsValid(aerm->wholeAttNo)) + elog(ERROR, "could not find junk %s column", resname); } return aerm; diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 5e38c20ca6..2b5dd2dbf8 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -166,6 +166,9 @@ static Datum ExecEvalFieldStore(FieldStoreState *fstate, static Datum ExecEvalRelabelType(GenericExprState *exprstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); +static Datum ExecEvalCollateClause(GenericExprState *exprstate, + ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalCoerceViaIO(CoerceViaIOState *iostate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); @@ -1202,7 +1205,7 @@ init_fcache(Oid foid, FuncExprState *fcache, /* Set up the primary fmgr lookup information */ fmgr_info_cxt(foid, &(fcache->func), fcacheCxt); - fcache->func.fn_expr = (Node *) fcache->xprstate.expr; + fmgr_info_expr((Node *) fcache->xprstate.expr, &(fcache->func)); /* Initialize the function call parameter struct as well */ InitFunctionCallInfoData(fcache->fcinfo_data, &(fcache->func), @@ -4026,6 +4029,20 @@ ExecEvalRelabelType(GenericExprState *exprstate, } /* ---------------------------------------------------------------- + * ExecEvalCollateClause + * + * Evaluate a CollateClause node. + * ---------------------------------------------------------------- + */ +static Datum +ExecEvalCollateClause(GenericExprState *exprstate, + ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone) +{ + return ExecEvalExpr(exprstate->arg, econtext, isNull, isDone); +} + +/* ---------------------------------------------------------------- * ExecEvalCoerceViaIO * * Evaluate a CoerceViaIO node. @@ -4114,7 +4131,7 @@ ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate, econtext->ecxt_per_query_memory); /* Initialize additional info */ - astate->elemfunc.fn_expr = (Node *) acoerce; + fmgr_info_expr((Node *) acoerce, &(astate->elemfunc)); } /* @@ -4484,6 +4501,16 @@ ExecInitExpr(Expr *node, PlanState *parent) state = (ExprState *) gstate; } break; + case T_CollateClause: + { + CollateClause *collate = (CollateClause *) node; + GenericExprState *gstate = makeNode(GenericExprState); + + gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCollateClause; + gstate->arg = ExecInitExpr(collate->arg, parent); + state = (ExprState *) gstate; + } + break; case T_CoerceViaIO: { CoerceViaIO *iocoerce = (CoerceViaIO *) node; @@ -4657,6 +4684,7 @@ ExecInitExpr(Expr *node, PlanState *parent) List *outlist; ListCell *l; ListCell *l2; + ListCell *l3; int i; rstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalRowCompare; @@ -4685,10 +4713,11 @@ ExecInitExpr(Expr *node, PlanState *parent) Assert(list_length(rcexpr->opfamilies) == nopers); rstate->funcs = (FmgrInfo *) palloc(nopers * sizeof(FmgrInfo)); i = 0; - forboth(l, rcexpr->opnos, l2, rcexpr->opfamilies) + forthree(l, rcexpr->opnos, l2, rcexpr->opfamilies, l3, rcexpr->collids) { Oid opno = lfirst_oid(l); Oid opfamily = lfirst_oid(l2); + Oid collid = lfirst_oid(l3); int strategy; Oid lefttype; Oid righttype; @@ -4710,6 +4739,7 @@ ExecInitExpr(Expr *node, PlanState *parent) * does this code. */ fmgr_info(proc, &(rstate->funcs[i])); + fmgr_info_collation(collid, &(rstate->funcs[i])); i++; } state = (ExprState *) rstate; @@ -4769,6 +4799,7 @@ ExecInitExpr(Expr *node, PlanState *parent) * code. */ fmgr_info(typentry->cmp_proc, &(mstate->cfunc)); + fmgr_info_collation(minmaxexpr->collid, &(mstate->cfunc)); state = (ExprState *) mstate; } break; diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 810ade23f6..3f44ef0186 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -937,11 +937,15 @@ ExecTypeFromTLInternal(List *targetList, bool hasoid, bool skipjunk) if (skipjunk && tle->resjunk) continue; TupleDescInitEntry(typeInfo, - cur_resno++, + cur_resno, tle->resname, exprType((Node *) tle->expr), exprTypmod((Node *) tle->expr), 0); + TupleDescInitEntryCollation(typeInfo, + cur_resno, + exprCollation((Node *) tle->expr)); + cur_resno++; } return typeInfo; @@ -969,11 +973,15 @@ ExecTypeFromExprList(List *exprList) sprintf(fldname, "f%d", cur_resno); TupleDescInitEntry(typeInfo, - cur_resno++, + cur_resno, fldname, exprType(e), exprTypmod(e), 0); + TupleDescInitEntryCollation(typeInfo, + cur_resno, + exprCollation(e)); + cur_resno++; } return typeInfo; diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index cb652862ed..d9bed220e4 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -140,6 +140,7 @@ typedef struct AggStatePerAggData /* deconstructed sorting information (arrays of length numSortCols) */ AttrNumber *sortColIdx; Oid *sortOperators; + Oid *sortCollations; bool *sortNullsFirst; /* @@ -315,12 +316,14 @@ initialize_aggregates(AggState *aggstate, (peraggstate->numInputs == 1) ? tuplesort_begin_datum(peraggstate->evaldesc->attrs[0]->atttypid, peraggstate->sortOperators[0], + peraggstate->sortCollations[0], peraggstate->sortNullsFirst[0], work_mem, false) : tuplesort_begin_heap(peraggstate->evaldesc, peraggstate->numSortCols, peraggstate->sortColIdx, peraggstate->sortOperators, + peraggstate->sortCollations, peraggstate->sortNullsFirst, work_mem, false); } @@ -1668,16 +1671,17 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) aggref->aggtype, transfn_oid, finalfn_oid, + aggref->collid, &transfnexpr, &finalfnexpr); fmgr_info(transfn_oid, &peraggstate->transfn); - peraggstate->transfn.fn_expr = (Node *) transfnexpr; + fmgr_info_expr((Node *) transfnexpr, &peraggstate->transfn); if (OidIsValid(finalfn_oid)) { fmgr_info(finalfn_oid, &peraggstate->finalfn); - peraggstate->finalfn.fn_expr = (Node *) finalfnexpr; + fmgr_info_expr((Node *) finalfnexpr, &peraggstate->finalfn); } get_typlenbyval(aggref->aggtype, @@ -1786,6 +1790,8 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) (AttrNumber *) palloc(numSortCols * sizeof(AttrNumber)); peraggstate->sortOperators = (Oid *) palloc(numSortCols * sizeof(Oid)); + peraggstate->sortCollations = + (Oid *) palloc(numSortCols * sizeof(Oid)); peraggstate->sortNullsFirst = (bool *) palloc(numSortCols * sizeof(bool)); @@ -1801,6 +1807,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) peraggstate->sortColIdx[i] = tle->resno; peraggstate->sortOperators[i] = sortcl->sortop; + peraggstate->sortCollations[i] = exprCollation((Node *) tle->expr); peraggstate->sortNullsFirst[i] = sortcl->nulls_first; i++; } diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c index 142f81767a..dedd255010 100644 --- a/src/backend/executor/nodeFunctionscan.c +++ b/src/backend/executor/nodeFunctionscan.c @@ -24,6 +24,7 @@ #include "executor/nodeFunctionscan.h" #include "funcapi.h" +#include "nodes/nodeFuncs.h" #include "utils/builtins.h" @@ -185,12 +186,16 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) funcrettype, -1, 0); + TupleDescInitEntryCollation(tupdesc, + (AttrNumber) 1, + exprCollation(node->funcexpr)); } else if (functypclass == TYPEFUNC_RECORD) { tupdesc = BuildDescFromLists(node->funccolnames, node->funccoltypes, - node->funccoltypmods); + node->funccoltypmods, + node->funccolcollations); } else { diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index bbf894e3ac..55fce94b32 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -732,6 +732,7 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid, int op_strategy; /* operator's strategy number */ Oid op_lefttype; /* operator's declared input types */ Oid op_righttype; + Oid collation; Expr *leftop; /* expr on lhs of operator */ Expr *rightop; /* expr on rhs ... */ AttrNumber varattno; /* att number used in scan */ @@ -831,6 +832,8 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid, op_righttype, /* strategy subtype */ opfuncid, /* reg proc to use */ scanvalue); /* constant */ + ScanKeyEntryInitializeCollation(this_scan_key, + ((OpExpr *) clause)->collid); } else if (IsA(clause, RowCompareExpr)) { @@ -839,6 +842,7 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid, ListCell *largs_cell = list_head(rc->largs); ListCell *rargs_cell = list_head(rc->rargs); ListCell *opnos_cell = list_head(rc->opnos); + ListCell *collids_cell = list_head(rc->collids); ScanKey first_sub_key; int n_sub_key; @@ -897,6 +901,9 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid, op_righttype, BTORDER_PROC); + collation = lfirst_oid(collids_cell); + collids_cell = lnext(collids_cell); + /* * rightop is the constant or variable comparison value */ @@ -952,6 +959,8 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid, op_righttype, /* strategy subtype */ opfuncid, /* reg proc to use */ scanvalue); /* constant */ + ScanKeyEntryInitializeCollation(this_sub_key, + collation); n_sub_key++; } @@ -1035,6 +1044,8 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid, op_righttype, /* strategy subtype */ opfuncid, /* reg proc to use */ (Datum) 0); /* constant */ + ScanKeyEntryInitializeCollation(this_scan_key, + saop->collid); } else if (IsA(clause, NullTest)) { diff --git a/src/backend/executor/nodeMergeAppend.c b/src/backend/executor/nodeMergeAppend.c index 24c5cd8a5b..e46af8cff9 100644 --- a/src/backend/executor/nodeMergeAppend.c +++ b/src/backend/executor/nodeMergeAppend.c @@ -150,6 +150,9 @@ ExecInitMergeAppend(MergeAppend *node, EState *estate, int eflags) sortFunction, (Datum) 0); + ScanKeyEntryInitializeCollation(&mergestate->ms_scankeys[i], + node->collations[i]); + /* However, we use btree's conventions for encoding directionality */ if (reverse) mergestate->ms_scankeys[i].sk_flags |= SK_BT_DESC; diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 6f6645687f..c0b9f23085 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -180,6 +180,7 @@ typedef enum static MergeJoinClause MJExamineQuals(List *mergeclauses, Oid *mergefamilies, + Oid *mergecollations, int *mergestrategies, bool *mergenullsfirst, PlanState *parent) @@ -197,6 +198,7 @@ MJExamineQuals(List *mergeclauses, OpExpr *qual = (OpExpr *) lfirst(cl); MergeJoinClause clause = &clauses[iClause]; Oid opfamily = mergefamilies[iClause]; + Oid collation = mergecollations[iClause]; StrategyNumber opstrategy = mergestrategies[iClause]; bool nulls_first = mergenullsfirst[iClause]; int op_strategy; @@ -240,6 +242,7 @@ MJExamineQuals(List *mergeclauses, /* Set up the fmgr lookup information */ fmgr_info(cmpproc, &(clause->cmpfinfo)); + fmgr_info_collation(collation, &(clause->cmpfinfo)); /* Fill the additional comparison-strategy flags */ if (opstrategy == BTLessStrategyNumber) @@ -1636,6 +1639,7 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) mergestate->mj_NumClauses = list_length(node->mergeclauses); mergestate->mj_Clauses = MJExamineQuals(node->mergeclauses, node->mergeFamilies, + node->mergeCollations, node->mergeStrategies, node->mergeNullsFirst, (PlanState *) mergestate); diff --git a/src/backend/executor/nodeSort.c b/src/backend/executor/nodeSort.c index 6a03d9ce82..e4b28c59b4 100644 --- a/src/backend/executor/nodeSort.c +++ b/src/backend/executor/nodeSort.c @@ -86,6 +86,7 @@ ExecSort(SortState *node) plannode->numCols, plannode->sortColIdx, plannode->sortOperators, + plannode->collations, plannode->nullsFirst, work_mem, node->randomAccess); diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index 8c263181fd..e9b3d76df1 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -831,7 +831,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) /* Lookup the equality function (potentially cross-type) */ fmgr_info(opexpr->opfuncid, &sstate->cur_eq_funcs[i - 1]); - sstate->cur_eq_funcs[i - 1].fn_expr = (Node *) opexpr; + fmgr_info_expr((Node *) opexpr, &sstate->cur_eq_funcs[i - 1]); /* Look up the equality function for the RHS type */ if (!get_compatible_hash_operators(opexpr->opno, diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c index f37ab39de0..372262ad7f 100644 --- a/src/backend/executor/nodeWindowAgg.c +++ b/src/backend/executor/nodeWindowAgg.c @@ -1561,7 +1561,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) fmgr_info_cxt(wfunc->winfnoid, &perfuncstate->flinfo, econtext->ecxt_per_query_memory); - perfuncstate->flinfo.fn_expr = (Node *) wfunc; + fmgr_info_expr((Node *) wfunc, &perfuncstate->flinfo); get_typlenbyval(wfunc->wintype, &perfuncstate->resulttypeLen, &perfuncstate->resulttypeByVal); @@ -1794,16 +1794,17 @@ initialize_peragg(WindowAggState *winstate, WindowFunc *wfunc, wfunc->wintype, transfn_oid, finalfn_oid, + wfunc->collid, &transfnexpr, &finalfnexpr); fmgr_info(transfn_oid, &peraggstate->transfn); - peraggstate->transfn.fn_expr = (Node *) transfnexpr; + fmgr_info_expr((Node *) transfnexpr, &peraggstate->transfn); if (OidIsValid(finalfn_oid)) { fmgr_info(finalfn_oid, &peraggstate->finalfn); - peraggstate->finalfn.fn_expr = (Node *) finalfnexpr; + fmgr_info_expr((Node *) finalfnexpr, &peraggstate->finalfn); } get_typlenbyval(wfunc->wintype, diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index b94905f2f0..8683589ab7 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -223,6 +223,7 @@ _copyMergeAppend(MergeAppend *from) COPY_SCALAR_FIELD(numCols); COPY_POINTER_FIELD(sortColIdx, from->numCols * sizeof(AttrNumber)); COPY_POINTER_FIELD(sortOperators, from->numCols * sizeof(Oid)); + COPY_POINTER_FIELD(collations, from->numCols * sizeof(Oid)); COPY_POINTER_FIELD(nullsFirst, from->numCols * sizeof(bool)); return newnode; @@ -479,6 +480,7 @@ _copyFunctionScan(FunctionScan *from) COPY_NODE_FIELD(funccolnames); COPY_NODE_FIELD(funccoltypes); COPY_NODE_FIELD(funccoltypmods); + COPY_NODE_FIELD(funccolcollations); return newnode; } @@ -658,6 +660,7 @@ _copyMergeJoin(MergeJoin *from) COPY_NODE_FIELD(mergeclauses); numCols = list_length(from->mergeclauses); COPY_POINTER_FIELD(mergeFamilies, numCols * sizeof(Oid)); + COPY_POINTER_FIELD(mergeCollations, numCols * sizeof(Oid)); COPY_POINTER_FIELD(mergeStrategies, numCols * sizeof(int)); COPY_POINTER_FIELD(mergeNullsFirst, numCols * sizeof(bool)); @@ -719,6 +722,7 @@ _copySort(Sort *from) COPY_SCALAR_FIELD(numCols); COPY_POINTER_FIELD(sortColIdx, from->numCols * sizeof(AttrNumber)); COPY_POINTER_FIELD(sortOperators, from->numCols * sizeof(Oid)); + COPY_POINTER_FIELD(collations, from->numCols * sizeof(Oid)); COPY_POINTER_FIELD(nullsFirst, from->numCols * sizeof(bool)); return newnode; @@ -938,6 +942,7 @@ _copyPlanRowMark(PlanRowMark *from) COPY_SCALAR_FIELD(rti); COPY_SCALAR_FIELD(prti); + COPY_SCALAR_FIELD(rowmarkId); COPY_SCALAR_FIELD(markType); COPY_SCALAR_FIELD(noWait); COPY_SCALAR_FIELD(isParent); @@ -1034,6 +1039,7 @@ _copyVar(Var *from) COPY_SCALAR_FIELD(varattno); COPY_SCALAR_FIELD(vartype); COPY_SCALAR_FIELD(vartypmod); + COPY_SCALAR_FIELD(varcollid); COPY_SCALAR_FIELD(varlevelsup); COPY_SCALAR_FIELD(varnoold); COPY_SCALAR_FIELD(varoattno); @@ -1052,6 +1058,7 @@ _copyConst(Const *from) COPY_SCALAR_FIELD(consttype); COPY_SCALAR_FIELD(consttypmod); + COPY_SCALAR_FIELD(constcollid); COPY_SCALAR_FIELD(constlen); if (from->constbyval || from->constisnull) @@ -1091,6 +1098,7 @@ _copyParam(Param *from) COPY_SCALAR_FIELD(paramid); COPY_SCALAR_FIELD(paramtype); COPY_SCALAR_FIELD(paramtypmod); + COPY_SCALAR_FIELD(paramcollation); COPY_LOCATION_FIELD(location); return newnode; @@ -1111,6 +1119,7 @@ _copyAggref(Aggref *from) COPY_NODE_FIELD(aggdistinct); COPY_SCALAR_FIELD(aggstar); COPY_SCALAR_FIELD(agglevelsup); + COPY_SCALAR_FIELD(collid); COPY_LOCATION_FIELD(location); return newnode; @@ -1130,6 +1139,7 @@ _copyWindowFunc(WindowFunc *from) COPY_SCALAR_FIELD(winref); COPY_SCALAR_FIELD(winstar); COPY_SCALAR_FIELD(winagg); + COPY_SCALAR_FIELD(collid); COPY_LOCATION_FIELD(location); return newnode; @@ -1146,6 +1156,7 @@ _copyArrayRef(ArrayRef *from) COPY_SCALAR_FIELD(refarraytype); COPY_SCALAR_FIELD(refelemtype); COPY_SCALAR_FIELD(reftypmod); + COPY_SCALAR_FIELD(refcollid); COPY_NODE_FIELD(refupperindexpr); COPY_NODE_FIELD(reflowerindexpr); COPY_NODE_FIELD(refexpr); @@ -1167,6 +1178,7 @@ _copyFuncExpr(FuncExpr *from) COPY_SCALAR_FIELD(funcretset); COPY_SCALAR_FIELD(funcformat); COPY_NODE_FIELD(args); + COPY_SCALAR_FIELD(collid); COPY_LOCATION_FIELD(location); return newnode; @@ -1201,6 +1213,7 @@ _copyOpExpr(OpExpr *from) COPY_SCALAR_FIELD(opresulttype); COPY_SCALAR_FIELD(opretset); COPY_NODE_FIELD(args); + COPY_SCALAR_FIELD(collid); COPY_LOCATION_FIELD(location); return newnode; @@ -1219,6 +1232,7 @@ _copyDistinctExpr(DistinctExpr *from) COPY_SCALAR_FIELD(opresulttype); COPY_SCALAR_FIELD(opretset); COPY_NODE_FIELD(args); + COPY_SCALAR_FIELD(collid); COPY_LOCATION_FIELD(location); return newnode; @@ -1236,6 +1250,7 @@ _copyScalarArrayOpExpr(ScalarArrayOpExpr *from) COPY_SCALAR_FIELD(opfuncid); COPY_SCALAR_FIELD(useOr); COPY_NODE_FIELD(args); + COPY_SCALAR_FIELD(collid); COPY_LOCATION_FIELD(location); return newnode; @@ -1288,6 +1303,7 @@ _copySubPlan(SubPlan *from) COPY_STRING_FIELD(plan_name); COPY_SCALAR_FIELD(firstColType); COPY_SCALAR_FIELD(firstColTypmod); + COPY_SCALAR_FIELD(firstColCollation); COPY_SCALAR_FIELD(useHashTable); COPY_SCALAR_FIELD(unknownEqFalse); COPY_NODE_FIELD(setParam); @@ -1324,6 +1340,7 @@ _copyFieldSelect(FieldSelect *from) COPY_SCALAR_FIELD(fieldnum); COPY_SCALAR_FIELD(resulttype); COPY_SCALAR_FIELD(resulttypmod); + COPY_SCALAR_FIELD(resultcollation); return newnode; } @@ -1421,6 +1438,7 @@ _copyCaseExpr(CaseExpr *from) CaseExpr *newnode = makeNode(CaseExpr); COPY_SCALAR_FIELD(casetype); + COPY_SCALAR_FIELD(casecollation); COPY_NODE_FIELD(arg); COPY_NODE_FIELD(args); COPY_NODE_FIELD(defresult); @@ -1454,6 +1472,7 @@ _copyCaseTestExpr(CaseTestExpr *from) COPY_SCALAR_FIELD(typeId); COPY_SCALAR_FIELD(typeMod); + COPY_SCALAR_FIELD(collation); return newnode; } @@ -1503,6 +1522,7 @@ _copyRowCompareExpr(RowCompareExpr *from) COPY_SCALAR_FIELD(rctype); COPY_NODE_FIELD(opnos); COPY_NODE_FIELD(opfamilies); + COPY_NODE_FIELD(collids); COPY_NODE_FIELD(largs); COPY_NODE_FIELD(rargs); @@ -1518,6 +1538,7 @@ _copyCoalesceExpr(CoalesceExpr *from) CoalesceExpr *newnode = makeNode(CoalesceExpr); COPY_SCALAR_FIELD(coalescetype); + COPY_SCALAR_FIELD(coalescecollation); COPY_NODE_FIELD(args); COPY_LOCATION_FIELD(location); @@ -1535,6 +1556,7 @@ _copyMinMaxExpr(MinMaxExpr *from) COPY_SCALAR_FIELD(minmaxtype); COPY_SCALAR_FIELD(op); COPY_NODE_FIELD(args); + COPY_SCALAR_FIELD(collid); COPY_LOCATION_FIELD(location); return newnode; @@ -1650,6 +1672,7 @@ _copySetToDefault(SetToDefault *from) COPY_SCALAR_FIELD(typeId); COPY_SCALAR_FIELD(typeMod); + COPY_SCALAR_FIELD(collid); COPY_LOCATION_FIELD(location); return newnode; @@ -1755,6 +1778,7 @@ _copyPathKey(PathKey *from) /* EquivalenceClasses are never moved, so just shallow-copy the pointer */ COPY_SCALAR_FIELD(pk_eclass); COPY_SCALAR_FIELD(pk_opfamily); + COPY_SCALAR_FIELD(pk_collation); COPY_SCALAR_FIELD(pk_strategy); COPY_SCALAR_FIELD(pk_nulls_first); @@ -1907,12 +1931,14 @@ _copyRangeTblEntry(RangeTblEntry *from) COPY_NODE_FIELD(funcexpr); COPY_NODE_FIELD(funccoltypes); COPY_NODE_FIELD(funccoltypmods); + COPY_NODE_FIELD(funccolcollations); COPY_NODE_FIELD(values_lists); COPY_STRING_FIELD(ctename); COPY_SCALAR_FIELD(ctelevelsup); COPY_SCALAR_FIELD(self_reference); COPY_NODE_FIELD(ctecoltypes); COPY_NODE_FIELD(ctecoltypmods); + COPY_NODE_FIELD(ctecolcollations); COPY_NODE_FIELD(alias); COPY_NODE_FIELD(eref); COPY_SCALAR_FIELD(inh); @@ -1996,6 +2022,7 @@ _copyCommonTableExpr(CommonTableExpr *from) COPY_NODE_FIELD(ctecolnames); COPY_NODE_FIELD(ctecoltypes); COPY_NODE_FIELD(ctecoltypmods); + COPY_NODE_FIELD(ctecolcollations); return newnode; } @@ -2150,6 +2177,8 @@ _copyTypeName(TypeName *from) COPY_NODE_FIELD(typmods); COPY_SCALAR_FIELD(typemod); COPY_NODE_FIELD(arrayBounds); + COPY_NODE_FIELD(collnames); + COPY_SCALAR_FIELD(collOid); COPY_LOCATION_FIELD(location); return newnode; @@ -2221,6 +2250,19 @@ _copyTypeCast(TypeCast *from) return newnode; } +static CollateClause * +_copyCollateClause(CollateClause *from) +{ + CollateClause *newnode = makeNode(CollateClause); + + COPY_NODE_FIELD(arg); + COPY_NODE_FIELD(collnames); + COPY_SCALAR_FIELD(collOid); + COPY_LOCATION_FIELD(location); + + return newnode; +} + static IndexElem * _copyIndexElem(IndexElem *from) { @@ -2229,6 +2271,7 @@ _copyIndexElem(IndexElem *from) COPY_STRING_FIELD(name); COPY_NODE_FIELD(expr); COPY_STRING_FIELD(indexcolname); + COPY_NODE_FIELD(collation); COPY_NODE_FIELD(opclass); COPY_SCALAR_FIELD(ordering); COPY_SCALAR_FIELD(nulls_ordering); @@ -2439,6 +2482,7 @@ _copySetOperationStmt(SetOperationStmt *from) COPY_NODE_FIELD(rarg); COPY_NODE_FIELD(colTypes); COPY_NODE_FIELD(colTypmods); + COPY_NODE_FIELD(colCollations); COPY_NODE_FIELD(groupClauses); return newnode; @@ -3232,6 +3276,42 @@ _copyAlterTableSpaceOptionsStmt(AlterTableSpaceOptionsStmt *from) return newnode; } +static CreateExtensionStmt * +_copyCreateExtensionStmt(CreateExtensionStmt *from) +{ + CreateExtensionStmt *newnode = makeNode(CreateExtensionStmt); + + COPY_STRING_FIELD(extname); + COPY_NODE_FIELD(options); + + return newnode; +} + +static AlterExtensionStmt * +_copyAlterExtensionStmt(AlterExtensionStmt *from) +{ + AlterExtensionStmt *newnode = makeNode(AlterExtensionStmt); + + COPY_STRING_FIELD(extname); + COPY_NODE_FIELD(options); + + return newnode; +} + +static AlterExtensionContentsStmt * +_copyAlterExtensionContentsStmt(AlterExtensionContentsStmt *from) +{ + AlterExtensionContentsStmt *newnode = makeNode(AlterExtensionContentsStmt); + + COPY_STRING_FIELD(extname); + COPY_SCALAR_FIELD(action); + COPY_SCALAR_FIELD(objtype); + COPY_NODE_FIELD(objname); + COPY_NODE_FIELD(objargs); + + return newnode; +} + static CreateFdwStmt * _copyCreateFdwStmt(CreateFdwStmt *from) { @@ -4236,6 +4316,15 @@ copyObject(void *from) case T_AlterTableSpaceOptionsStmt: retval = _copyAlterTableSpaceOptionsStmt(from); break; + case T_CreateExtensionStmt: + retval = _copyCreateExtensionStmt(from); + break; + case T_AlterExtensionStmt: + retval = _copyAlterExtensionStmt(from); + break; + case T_AlterExtensionContentsStmt: + retval = _copyAlterExtensionContentsStmt(from); + break; case T_CreateFdwStmt: retval = _copyCreateFdwStmt(from); break; @@ -4369,6 +4458,9 @@ copyObject(void *from) case T_TypeCast: retval = _copyTypeCast(from); break; + case T_CollateClause: + retval = _copyCollateClause(from); + break; case T_SortBy: retval = _copySortBy(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 26df9acd6b..dd332f19f0 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -137,6 +137,7 @@ _equalVar(Var *a, Var *b) COMPARE_SCALAR_FIELD(varattno); COMPARE_SCALAR_FIELD(vartype); COMPARE_SCALAR_FIELD(vartypmod); + COMPARE_SCALAR_FIELD(varcollid); COMPARE_SCALAR_FIELD(varlevelsup); COMPARE_SCALAR_FIELD(varnoold); COMPARE_SCALAR_FIELD(varoattno); @@ -150,6 +151,7 @@ _equalConst(Const *a, Const *b) { COMPARE_SCALAR_FIELD(consttype); COMPARE_SCALAR_FIELD(consttypmod); + COMPARE_SCALAR_FIELD(constcollid); COMPARE_SCALAR_FIELD(constlen); COMPARE_SCALAR_FIELD(constisnull); COMPARE_SCALAR_FIELD(constbyval); @@ -172,6 +174,7 @@ _equalParam(Param *a, Param *b) COMPARE_SCALAR_FIELD(paramid); COMPARE_SCALAR_FIELD(paramtype); COMPARE_SCALAR_FIELD(paramtypmod); + COMPARE_SCALAR_FIELD(paramcollation); COMPARE_LOCATION_FIELD(location); return true; @@ -187,6 +190,7 @@ _equalAggref(Aggref *a, Aggref *b) COMPARE_NODE_FIELD(aggdistinct); COMPARE_SCALAR_FIELD(aggstar); COMPARE_SCALAR_FIELD(agglevelsup); + COMPARE_SCALAR_FIELD(collid); COMPARE_LOCATION_FIELD(location); return true; @@ -201,6 +205,7 @@ _equalWindowFunc(WindowFunc *a, WindowFunc *b) COMPARE_SCALAR_FIELD(winref); COMPARE_SCALAR_FIELD(winstar); COMPARE_SCALAR_FIELD(winagg); + COMPARE_SCALAR_FIELD(collid); COMPARE_LOCATION_FIELD(location); return true; @@ -212,6 +217,7 @@ _equalArrayRef(ArrayRef *a, ArrayRef *b) COMPARE_SCALAR_FIELD(refarraytype); COMPARE_SCALAR_FIELD(refelemtype); COMPARE_SCALAR_FIELD(reftypmod); + COMPARE_SCALAR_FIELD(refcollid); COMPARE_NODE_FIELD(refupperindexpr); COMPARE_NODE_FIELD(reflowerindexpr); COMPARE_NODE_FIELD(refexpr); @@ -237,6 +243,7 @@ _equalFuncExpr(FuncExpr *a, FuncExpr *b) return false; COMPARE_NODE_FIELD(args); + COMPARE_SCALAR_FIELD(collid); COMPARE_LOCATION_FIELD(location); return true; @@ -272,6 +279,7 @@ _equalOpExpr(OpExpr *a, OpExpr *b) COMPARE_SCALAR_FIELD(opresulttype); COMPARE_SCALAR_FIELD(opretset); COMPARE_NODE_FIELD(args); + COMPARE_SCALAR_FIELD(collid); COMPARE_LOCATION_FIELD(location); return true; @@ -296,6 +304,7 @@ _equalDistinctExpr(DistinctExpr *a, DistinctExpr *b) COMPARE_SCALAR_FIELD(opresulttype); COMPARE_SCALAR_FIELD(opretset); COMPARE_NODE_FIELD(args); + COMPARE_SCALAR_FIELD(collid); COMPARE_LOCATION_FIELD(location); return true; @@ -319,6 +328,7 @@ _equalScalarArrayOpExpr(ScalarArrayOpExpr *a, ScalarArrayOpExpr *b) COMPARE_SCALAR_FIELD(useOr); COMPARE_NODE_FIELD(args); + COMPARE_SCALAR_FIELD(collid); COMPARE_LOCATION_FIELD(location); return true; @@ -356,6 +366,7 @@ _equalSubPlan(SubPlan *a, SubPlan *b) COMPARE_STRING_FIELD(plan_name); COMPARE_SCALAR_FIELD(firstColType); COMPARE_SCALAR_FIELD(firstColTypmod); + COMPARE_SCALAR_FIELD(firstColCollation); COMPARE_SCALAR_FIELD(useHashTable); COMPARE_SCALAR_FIELD(unknownEqFalse); COMPARE_NODE_FIELD(setParam); @@ -382,6 +393,7 @@ _equalFieldSelect(FieldSelect *a, FieldSelect *b) COMPARE_SCALAR_FIELD(fieldnum); COMPARE_SCALAR_FIELD(resulttype); COMPARE_SCALAR_FIELD(resulttypmod); + COMPARE_SCALAR_FIELD(resultcollation); return true; } @@ -485,6 +497,7 @@ static bool _equalCaseExpr(CaseExpr *a, CaseExpr *b) { COMPARE_SCALAR_FIELD(casetype); + COMPARE_SCALAR_FIELD(casecollation); COMPARE_NODE_FIELD(arg); COMPARE_NODE_FIELD(args); COMPARE_NODE_FIELD(defresult); @@ -508,6 +521,7 @@ _equalCaseTestExpr(CaseTestExpr *a, CaseTestExpr *b) { COMPARE_SCALAR_FIELD(typeId); COMPARE_SCALAR_FIELD(typeMod); + COMPARE_SCALAR_FIELD(collation); return true; } @@ -551,6 +565,7 @@ _equalRowCompareExpr(RowCompareExpr *a, RowCompareExpr *b) COMPARE_SCALAR_FIELD(rctype); COMPARE_NODE_FIELD(opnos); COMPARE_NODE_FIELD(opfamilies); + COMPARE_NODE_FIELD(collids); COMPARE_NODE_FIELD(largs); COMPARE_NODE_FIELD(rargs); @@ -561,6 +576,7 @@ static bool _equalCoalesceExpr(CoalesceExpr *a, CoalesceExpr *b) { COMPARE_SCALAR_FIELD(coalescetype); + COMPARE_SCALAR_FIELD(coalescecollation); COMPARE_NODE_FIELD(args); COMPARE_LOCATION_FIELD(location); @@ -573,6 +589,7 @@ _equalMinMaxExpr(MinMaxExpr *a, MinMaxExpr *b) COMPARE_SCALAR_FIELD(minmaxtype); COMPARE_SCALAR_FIELD(op); COMPARE_NODE_FIELD(args); + COMPARE_SCALAR_FIELD(collid); COMPARE_LOCATION_FIELD(location); return true; @@ -673,6 +690,7 @@ _equalSetToDefault(SetToDefault *a, SetToDefault *b) { COMPARE_SCALAR_FIELD(typeId); COMPARE_SCALAR_FIELD(typeMod); + COMPARE_SCALAR_FIELD(collid); COMPARE_LOCATION_FIELD(location); return true; @@ -759,6 +777,7 @@ _equalPathKey(PathKey *a, PathKey *b) if (a_eclass != b_eclass) return false; COMPARE_SCALAR_FIELD(pk_opfamily); + COMPARE_SCALAR_FIELD(pk_collation); COMPARE_SCALAR_FIELD(pk_strategy); COMPARE_SCALAR_FIELD(pk_nulls_first); @@ -965,6 +984,7 @@ _equalSetOperationStmt(SetOperationStmt *a, SetOperationStmt *b) COMPARE_NODE_FIELD(rarg); COMPARE_NODE_FIELD(colTypes); COMPARE_NODE_FIELD(colTypmods); + COMPARE_NODE_FIELD(colCollations); COMPARE_NODE_FIELD(groupClauses); return true; @@ -1626,6 +1646,36 @@ _equalAlterTableSpaceOptionsStmt(AlterTableSpaceOptionsStmt *a, } static bool +_equalCreateExtensionStmt(CreateExtensionStmt *a, CreateExtensionStmt *b) +{ + COMPARE_STRING_FIELD(extname); + COMPARE_NODE_FIELD(options); + + return true; +} + +static bool +_equalAlterExtensionStmt(AlterExtensionStmt *a, AlterExtensionStmt *b) +{ + COMPARE_STRING_FIELD(extname); + COMPARE_NODE_FIELD(options); + + return true; +} + +static bool +_equalAlterExtensionContentsStmt(AlterExtensionContentsStmt *a, AlterExtensionContentsStmt *b) +{ + COMPARE_STRING_FIELD(extname); + COMPARE_SCALAR_FIELD(action); + COMPARE_SCALAR_FIELD(objtype); + COMPARE_NODE_FIELD(objname); + COMPARE_NODE_FIELD(objargs); + + return true; +} + +static bool _equalCreateFdwStmt(CreateFdwStmt *a, CreateFdwStmt *b) { COMPARE_STRING_FIELD(fdwname); @@ -2078,6 +2128,8 @@ _equalTypeName(TypeName *a, TypeName *b) COMPARE_NODE_FIELD(typmods); COMPARE_SCALAR_FIELD(typemod); COMPARE_NODE_FIELD(arrayBounds); + COMPARE_NODE_FIELD(collnames); + COMPARE_SCALAR_FIELD(collOid); COMPARE_LOCATION_FIELD(location); return true; @@ -2094,6 +2146,17 @@ _equalTypeCast(TypeCast *a, TypeCast *b) } static bool +_equalCollateClause(CollateClause *a, CollateClause *b) +{ + COMPARE_NODE_FIELD(arg); + COMPARE_NODE_FIELD(collnames); + COMPARE_SCALAR_FIELD(collOid); + COMPARE_LOCATION_FIELD(location); + + return true; +} + +static bool _equalSortBy(SortBy *a, SortBy *b) { COMPARE_NODE_FIELD(node); @@ -2145,6 +2208,7 @@ _equalIndexElem(IndexElem *a, IndexElem *b) COMPARE_STRING_FIELD(name); COMPARE_NODE_FIELD(expr); COMPARE_STRING_FIELD(indexcolname); + COMPARE_NODE_FIELD(collation); COMPARE_NODE_FIELD(opclass); COMPARE_SCALAR_FIELD(ordering); COMPARE_SCALAR_FIELD(nulls_ordering); @@ -2228,12 +2292,14 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b) COMPARE_NODE_FIELD(funcexpr); COMPARE_NODE_FIELD(funccoltypes); COMPARE_NODE_FIELD(funccoltypmods); + COMPARE_NODE_FIELD(funccolcollations); COMPARE_NODE_FIELD(values_lists); COMPARE_STRING_FIELD(ctename); COMPARE_SCALAR_FIELD(ctelevelsup); COMPARE_SCALAR_FIELD(self_reference); COMPARE_NODE_FIELD(ctecoltypes); COMPARE_NODE_FIELD(ctecoltypmods); + COMPARE_NODE_FIELD(ctecolcollations); COMPARE_NODE_FIELD(alias); COMPARE_NODE_FIELD(eref); COMPARE_SCALAR_FIELD(inh); @@ -2307,6 +2373,7 @@ _equalCommonTableExpr(CommonTableExpr *a, CommonTableExpr *b) COMPARE_NODE_FIELD(ctecolnames); COMPARE_NODE_FIELD(ctecoltypes); COMPARE_NODE_FIELD(ctecoltypmods); + COMPARE_NODE_FIELD(ctecolcollations); return true; } @@ -2807,6 +2874,15 @@ equal(void *a, void *b) case T_AlterTableSpaceOptionsStmt: retval = _equalAlterTableSpaceOptionsStmt(a, b); break; + case T_CreateExtensionStmt: + retval = _equalCreateExtensionStmt(a, b); + break; + case T_AlterExtensionStmt: + retval = _equalAlterExtensionStmt(a, b); + break; + case T_AlterExtensionContentsStmt: + retval = _equalAlterExtensionContentsStmt(a, b); + break; case T_CreateFdwStmt: retval = _equalCreateFdwStmt(a, b); break; @@ -2940,6 +3016,9 @@ equal(void *a, void *b) case T_TypeCast: retval = _equalTypeCast(a, b); break; + case T_CollateClause: + retval = _equalCollateClause(a, b); + break; case T_SortBy: retval = _equalSortBy(a, b); break; diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c index 79da1853c3..0225f19382 100644 --- a/src/backend/nodes/makefuncs.c +++ b/src/backend/nodes/makefuncs.c @@ -67,6 +67,7 @@ makeVar(Index varno, AttrNumber varattno, Oid vartype, int32 vartypmod, + Oid varcollid, Index varlevelsup) { Var *var = makeNode(Var); @@ -75,6 +76,7 @@ makeVar(Index varno, var->varattno = varattno; var->vartype = vartype; var->vartypmod = vartypmod; + var->varcollid = varcollid; var->varlevelsup = varlevelsup; /* @@ -105,6 +107,7 @@ makeVarFromTargetEntry(Index varno, tle->resno, exprType((Node *) tle->expr), exprTypmod((Node *) tle->expr), + exprCollation((Node *) tle->expr), 0); } @@ -139,6 +142,7 @@ makeWholeRowVar(RangeTblEntry *rte, InvalidAttrNumber, toid, -1, + InvalidOid, varlevelsup); break; case RTE_FUNCTION: @@ -150,6 +154,7 @@ makeWholeRowVar(RangeTblEntry *rte, InvalidAttrNumber, toid, -1, + InvalidOid, varlevelsup); } else @@ -164,6 +169,7 @@ makeWholeRowVar(RangeTblEntry *rte, 1, toid, -1, + InvalidOid, varlevelsup); } break; @@ -174,6 +180,7 @@ makeWholeRowVar(RangeTblEntry *rte, InvalidAttrNumber, toid, -1, + InvalidOid, varlevelsup); break; default: @@ -188,6 +195,7 @@ makeWholeRowVar(RangeTblEntry *rte, InvalidAttrNumber, RECORDOID, -1, + InvalidOid, varlevelsup); break; } @@ -272,6 +280,7 @@ makeConst(Oid consttype, cnst->consttype = consttype; cnst->consttypmod = consttypmod; + cnst->constcollid = get_typcollation(consttype); cnst->constlen = constlen; cnst->constvalue = constvalue; cnst->constisnull = constisnull; @@ -418,15 +427,16 @@ makeTypeNameFromNameList(List *names) /* * makeTypeNameFromOid - - * build a TypeName node to represent a type already known by OID/typmod. + * build a TypeName node to represent a type already known by OID/typmod/collation. */ TypeName * -makeTypeNameFromOid(Oid typeOid, int32 typmod) +makeTypeNameFromOid(Oid typeOid, int32 typmod, Oid collOid) { TypeName *n = makeNode(TypeName); n->typeOid = typeOid; n->typemod = typmod; + n->collOid = collOid; n->location = -1; return n; } @@ -438,7 +448,7 @@ makeTypeNameFromOid(Oid typeOid, int32 typmod) * The argument expressions must have been transformed already. */ FuncExpr * -makeFuncExpr(Oid funcid, Oid rettype, List *args, CoercionForm fformat) +makeFuncExpr(Oid funcid, Oid rettype, List *args, Oid collid, CoercionForm fformat) { FuncExpr *funcexpr; @@ -448,6 +458,7 @@ makeFuncExpr(Oid funcid, Oid rettype, List *args, CoercionForm fformat) funcexpr->funcretset = false; /* only allowed case here */ funcexpr->funcformat = fformat; funcexpr->args = args; + funcexpr->collid = collid; funcexpr->location = -1; return funcexpr; diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c index d17b347e45..8a23047d38 100644 --- a/src/backend/nodes/nodeFuncs.c +++ b/src/backend/nodes/nodeFuncs.c @@ -14,6 +14,7 @@ */ #include "postgres.h" +#include "catalog/pg_collation.h" #include "catalog/pg_type.h" #include "miscadmin.h" #include "nodes/nodeFuncs.h" @@ -161,6 +162,9 @@ exprType(Node *expr) case T_RelabelType: type = ((RelabelType *) expr)->resulttype; break; + case T_CollateClause: + type = exprType((Node *) ((CollateClause *) expr)->arg); + break; case T_CoerceViaIO: type = ((CoerceViaIO *) expr)->resulttype; break; @@ -460,6 +464,215 @@ exprTypmod(Node *expr) } /* + * exprCollation - + * returns the Oid of the collation of the expression's result. + */ +Oid +exprCollation(Node *expr) +{ + Oid coll; + + if (!expr) + return InvalidOid; + + switch (nodeTag(expr)) + { + case T_Var: + coll = ((Var *) expr)->varcollid; + break; + case T_Const: + coll = ((Const *) expr)->constcollid; + break; + case T_Param: + coll = ((Param *) expr)->paramcollation; + break; + case T_Aggref: + coll = ((Aggref *) expr)->collid; + break; + case T_WindowFunc: + coll = ((WindowFunc *) expr)->collid; + break; + case T_ArrayRef: + coll = ((ArrayRef *) expr)->refcollid; + break; + case T_FuncExpr: + coll = ((FuncExpr *) expr)->collid; + break; + case T_NamedArgExpr: + coll = exprCollation((Node *) ((NamedArgExpr *) expr)->arg); + break; + case T_OpExpr: + coll = ((OpExpr *) expr)->collid; + break; + case T_DistinctExpr: + coll = ((DistinctExpr *) expr)->collid; + break; + case T_ScalarArrayOpExpr: + coll = ((ScalarArrayOpExpr *) expr)->collid; + break; + case T_BoolExpr: + coll = InvalidOid; /* not applicable */ + break; + case T_SubLink: + { + SubLink *sublink = (SubLink *) expr; + + if (sublink->subLinkType == EXPR_SUBLINK || + sublink->subLinkType == ARRAY_SUBLINK) + { + /* get the collation of the subselect's first target column */ + Query *qtree = (Query *) sublink->subselect; + TargetEntry *tent; + + if (!qtree || !IsA(qtree, Query)) + elog(ERROR, "cannot get collation for untransformed sublink"); + tent = (TargetEntry *) linitial(qtree->targetList); + Assert(IsA(tent, TargetEntry)); + Assert(!tent->resjunk); + coll = exprCollation((Node *) tent->expr); + /* note we don't need to care if it's an array */ + } + else + coll = InvalidOid; + } + break; + case T_SubPlan: + { + SubPlan *subplan = (SubPlan *) expr; + + if (subplan->subLinkType == EXPR_SUBLINK || + subplan->subLinkType == ARRAY_SUBLINK) + { + /* get the collation of the subselect's first target column */ + /* note we don't need to care if it's an array */ + coll = subplan->firstColCollation; + } + else + { + /* for all other subplan types, result is boolean */ + coll = InvalidOid; + } + } + break; + case T_AlternativeSubPlan: + { + AlternativeSubPlan *asplan = (AlternativeSubPlan *) expr; + + /* subplans should all return the same thing */ + coll = exprCollation((Node *) linitial(asplan->subplans)); + } + break; + case T_FieldSelect: + coll = ((FieldSelect *) expr)->resultcollation; + break; + case T_FieldStore: + coll = InvalidOid; /* not applicable */ + break; + case T_RelabelType: + coll = exprCollation((Node *) ((RelabelType *) expr)->arg); + break; + case T_CollateClause: + coll = ((CollateClause *) expr)->collOid; + break; + case T_CoerceViaIO: + { + CoerceViaIO *cvio = (CoerceViaIO *) expr; + coll = coercion_expression_result_collation(cvio->resulttype, (Node *) cvio->arg); + break; + } + case T_ArrayCoerceExpr: + { + ArrayCoerceExpr *ace = (ArrayCoerceExpr *) expr; + coll = coercion_expression_result_collation(ace->resulttype, (Node *) ace->arg); + break; + } + case T_ConvertRowtypeExpr: + { + ConvertRowtypeExpr *cre = (ConvertRowtypeExpr *) expr; + coll = coercion_expression_result_collation(cre->resulttype, (Node *) cre->arg); + break; + } + case T_CaseExpr: + coll = ((CaseExpr *) expr)->casecollation; + break; + case T_CaseTestExpr: + coll = ((CaseTestExpr *) expr)->collation; + break; + case T_ArrayExpr: + coll = get_typcollation(((ArrayExpr *) expr)->array_typeid); + break; + case T_RowExpr: + coll = InvalidOid; /* not applicable */ + break; + case T_RowCompareExpr: + coll = InvalidOid; /* not applicable */ + break; + case T_CoalesceExpr: + coll = ((CoalesceExpr *) expr)->coalescecollation; + break; + case T_MinMaxExpr: + coll = ((MinMaxExpr *) expr)->collid; + break; + case T_XmlExpr: + if (((XmlExpr *) expr)->op == IS_XMLSERIALIZE) + coll = DEFAULT_COLLATION_OID; + else + coll = InvalidOid; + break; + case T_NullIfExpr: + coll = exprCollation((Node *) linitial(((NullIfExpr *) expr)->args)); + break; + case T_NullTest: + coll = InvalidOid; /* not applicable */ + break; + case T_BooleanTest: + coll = InvalidOid; /* not applicable */ + break; + case T_CoerceToDomain: + coll = get_typcollation(((CoerceToDomain *) expr)->resulttype); + if (coll == DEFAULT_COLLATION_OID) + coll = exprCollation((Node *) ((CoerceToDomain *) expr)->arg); + break; + case T_CoerceToDomainValue: + coll = get_typcollation(((CoerceToDomainValue *) expr)->typeId); + break; + case T_SetToDefault: + coll = ((SetToDefault *) expr)->collid; + break; + case T_CurrentOfExpr: + coll = InvalidOid; /* not applicable */ + break; + case T_PlaceHolderVar: + coll = exprCollation((Node *) ((PlaceHolderVar *) expr)->phexpr); + break; + default: + elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr)); + coll = InvalidOid; /* keep compiler quiet */ + break; + } + + return coll; +} + +/* + * Compute the result collation of a coercion-like expression that + * converts arg to resulttype. + */ +Oid +coercion_expression_result_collation(Oid resulttype, Node *arg) +{ + if (type_is_collatable(resulttype)) + { + if (type_is_collatable(exprType(arg))) + return exprCollation(arg); + else + return DEFAULT_COLLATION_OID; + } + else + return InvalidOid; +} + +/* * exprIsLengthCoercion * Detect whether an expression tree is an application of a datatype's * typmod-coercion function. Optionally extract the result's typmod. @@ -908,6 +1121,9 @@ exprLocation(Node *expr) loc = leftmostLoc(loc, tc->location); } break; + case T_CollateClause: + loc = ((CollateClause *) expr)->location; + break; case T_SortBy: /* just use argument's location (ignore operator, if any) */ loc = exprLocation(((SortBy *) expr)->node); @@ -1220,6 +1436,8 @@ expression_tree_walker(Node *node, break; case T_RelabelType: return walker(((RelabelType *) node)->arg, context); + case T_CollateClause: + return walker(((CollateClause *) node)->arg, context); case T_CoerceViaIO: return walker(((CoerceViaIO *) node)->arg, context); case T_ArrayCoerceExpr: @@ -1776,6 +1994,16 @@ expression_tree_mutator(Node *node, return (Node *) newnode; } break; + case T_CollateClause: + { + CollateClause *collate = (CollateClause *) node; + CollateClause *newnode; + + FLATCOPY(newnode, collate, CollateClause); + MUTATE(newnode->arg, collate->arg, Expr *); + return (Node *) newnode; + } + break; case T_CoerceViaIO: { CoerceViaIO *iocoerce = (CoerceViaIO *) node; @@ -2471,6 +2699,8 @@ bool return true; } break; + case T_CollateClause: + return walker(((CollateClause *) node)->arg, context); case T_SortBy: return walker(((SortBy *) node)->node, context); case T_WindowDef: diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index b8e4c1ff71..52a9d2a33d 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -365,6 +365,10 @@ _outMergeAppend(StringInfo str, MergeAppend *node) for (i = 0; i < node->numCols; i++) appendStringInfo(str, " %u", node->sortOperators[i]); + appendStringInfo(str, " :collations"); + for (i = 0; i < node->numCols; i++) + appendStringInfo(str, " %u", node->collations[i]); + appendStringInfo(str, " :nullsFirst"); for (i = 0; i < node->numCols; i++) appendStringInfo(str, " %s", booltostr(node->nullsFirst[i])); @@ -499,6 +503,7 @@ _outFunctionScan(StringInfo str, FunctionScan *node) WRITE_NODE_FIELD(funccolnames); WRITE_NODE_FIELD(funccoltypes); WRITE_NODE_FIELD(funccoltypmods); + WRITE_NODE_FIELD(funccolcollations); } static void @@ -587,6 +592,10 @@ _outMergeJoin(StringInfo str, MergeJoin *node) for (i = 0; i < numCols; i++) appendStringInfo(str, " %u", node->mergeFamilies[i]); + appendStringInfo(str, " :mergeCollations"); + for (i = 0; i < numCols; i++) + appendStringInfo(str, " %u", node->mergeCollations[i]); + appendStringInfo(str, " :mergeStrategies"); for (i = 0; i < numCols; i++) appendStringInfo(str, " %d", node->mergeStrategies[i]); @@ -711,6 +720,10 @@ _outSort(StringInfo str, Sort *node) for (i = 0; i < node->numCols; i++) appendStringInfo(str, " %u", node->sortOperators[i]); + appendStringInfo(str, " :collations"); + for (i = 0; i < node->numCols; i++) + appendStringInfo(str, " %u", node->collations[i]); + appendStringInfo(str, " :nullsFirst"); for (i = 0; i < node->numCols; i++) appendStringInfo(str, " %s", booltostr(node->nullsFirst[i])); @@ -814,6 +827,7 @@ _outPlanRowMark(StringInfo str, PlanRowMark *node) WRITE_UINT_FIELD(rti); WRITE_UINT_FIELD(prti); + WRITE_UINT_FIELD(rowmarkId); WRITE_ENUM_FIELD(markType, RowMarkType); WRITE_BOOL_FIELD(noWait); WRITE_BOOL_FIELD(isParent); @@ -883,6 +897,7 @@ _outVar(StringInfo str, Var *node) WRITE_INT_FIELD(varattno); WRITE_OID_FIELD(vartype); WRITE_INT_FIELD(vartypmod); + WRITE_OID_FIELD(varcollid); WRITE_UINT_FIELD(varlevelsup); WRITE_UINT_FIELD(varnoold); WRITE_INT_FIELD(varoattno); @@ -896,6 +911,7 @@ _outConst(StringInfo str, Const *node) WRITE_OID_FIELD(consttype); WRITE_INT_FIELD(consttypmod); + WRITE_OID_FIELD(constcollid); WRITE_INT_FIELD(constlen); WRITE_BOOL_FIELD(constbyval); WRITE_BOOL_FIELD(constisnull); @@ -917,6 +933,7 @@ _outParam(StringInfo str, Param *node) WRITE_INT_FIELD(paramid); WRITE_OID_FIELD(paramtype); WRITE_INT_FIELD(paramtypmod); + WRITE_OID_FIELD(paramcollation); WRITE_LOCATION_FIELD(location); } @@ -932,6 +949,7 @@ _outAggref(StringInfo str, Aggref *node) WRITE_NODE_FIELD(aggdistinct); WRITE_BOOL_FIELD(aggstar); WRITE_UINT_FIELD(agglevelsup); + WRITE_OID_FIELD(collid); WRITE_LOCATION_FIELD(location); } @@ -946,6 +964,7 @@ _outWindowFunc(StringInfo str, WindowFunc *node) WRITE_UINT_FIELD(winref); WRITE_BOOL_FIELD(winstar); WRITE_BOOL_FIELD(winagg); + WRITE_OID_FIELD(collid); WRITE_LOCATION_FIELD(location); } @@ -957,6 +976,7 @@ _outArrayRef(StringInfo str, ArrayRef *node) WRITE_OID_FIELD(refarraytype); WRITE_OID_FIELD(refelemtype); WRITE_INT_FIELD(reftypmod); + WRITE_INT_FIELD(refcollid); WRITE_NODE_FIELD(refupperindexpr); WRITE_NODE_FIELD(reflowerindexpr); WRITE_NODE_FIELD(refexpr); @@ -973,6 +993,7 @@ _outFuncExpr(StringInfo str, FuncExpr *node) WRITE_BOOL_FIELD(funcretset); WRITE_ENUM_FIELD(funcformat, CoercionForm); WRITE_NODE_FIELD(args); + WRITE_OID_FIELD(collid); WRITE_LOCATION_FIELD(location); } @@ -997,6 +1018,7 @@ _outOpExpr(StringInfo str, OpExpr *node) WRITE_OID_FIELD(opresulttype); WRITE_BOOL_FIELD(opretset); WRITE_NODE_FIELD(args); + WRITE_OID_FIELD(collid); WRITE_LOCATION_FIELD(location); } @@ -1010,6 +1032,7 @@ _outDistinctExpr(StringInfo str, DistinctExpr *node) WRITE_OID_FIELD(opresulttype); WRITE_BOOL_FIELD(opretset); WRITE_NODE_FIELD(args); + WRITE_OID_FIELD(collid); WRITE_LOCATION_FIELD(location); } @@ -1022,6 +1045,7 @@ _outScalarArrayOpExpr(StringInfo str, ScalarArrayOpExpr *node) WRITE_OID_FIELD(opfuncid); WRITE_BOOL_FIELD(useOr); WRITE_NODE_FIELD(args); + WRITE_OID_FIELD(collid); WRITE_LOCATION_FIELD(location); } @@ -1076,6 +1100,7 @@ _outSubPlan(StringInfo str, SubPlan *node) WRITE_STRING_FIELD(plan_name); WRITE_OID_FIELD(firstColType); WRITE_INT_FIELD(firstColTypmod); + WRITE_OID_FIELD(firstColCollation); WRITE_BOOL_FIELD(useHashTable); WRITE_BOOL_FIELD(unknownEqFalse); WRITE_NODE_FIELD(setParam); @@ -1102,6 +1127,7 @@ _outFieldSelect(StringInfo str, FieldSelect *node) WRITE_INT_FIELD(fieldnum); WRITE_OID_FIELD(resulttype); WRITE_INT_FIELD(resulttypmod); + WRITE_OID_FIELD(resultcollation); } static void @@ -1169,6 +1195,7 @@ _outCaseExpr(StringInfo str, CaseExpr *node) WRITE_NODE_TYPE("CASE"); WRITE_OID_FIELD(casetype); + WRITE_OID_FIELD(casecollation); WRITE_NODE_FIELD(arg); WRITE_NODE_FIELD(args); WRITE_NODE_FIELD(defresult); @@ -1192,6 +1219,7 @@ _outCaseTestExpr(StringInfo str, CaseTestExpr *node) WRITE_OID_FIELD(typeId); WRITE_INT_FIELD(typeMod); + WRITE_OID_FIELD(collation); } static void @@ -1226,6 +1254,7 @@ _outRowCompareExpr(StringInfo str, RowCompareExpr *node) WRITE_ENUM_FIELD(rctype, RowCompareType); WRITE_NODE_FIELD(opnos); WRITE_NODE_FIELD(opfamilies); + WRITE_NODE_FIELD(collids); WRITE_NODE_FIELD(largs); WRITE_NODE_FIELD(rargs); } @@ -1236,6 +1265,7 @@ _outCoalesceExpr(StringInfo str, CoalesceExpr *node) WRITE_NODE_TYPE("COALESCE"); WRITE_OID_FIELD(coalescetype); + WRITE_OID_FIELD(coalescecollation); WRITE_NODE_FIELD(args); WRITE_LOCATION_FIELD(location); } @@ -1248,6 +1278,7 @@ _outMinMaxExpr(StringInfo str, MinMaxExpr *node) WRITE_OID_FIELD(minmaxtype); WRITE_ENUM_FIELD(op, MinMaxOp); WRITE_NODE_FIELD(args); + WRITE_OID_FIELD(collid); WRITE_LOCATION_FIELD(location); } @@ -1328,6 +1359,7 @@ _outSetToDefault(StringInfo str, SetToDefault *node) WRITE_OID_FIELD(typeId); WRITE_INT_FIELD(typeMod); + WRITE_OID_FIELD(collid); WRITE_LOCATION_FIELD(location); } @@ -1605,6 +1637,7 @@ _outPlannerGlobal(StringInfo str, PlannerGlobal *node) WRITE_NODE_FIELD(relationOids); WRITE_NODE_FIELD(invalItems); WRITE_UINT_FIELD(lastPHId); + WRITE_UINT_FIELD(lastRowMarkId); WRITE_BOOL_FIELD(transientPlan); } @@ -1695,10 +1728,12 @@ _outIndexOptInfo(StringInfo str, IndexOptInfo *node) WRITE_UINT_FIELD(pages); WRITE_FLOAT_FIELD(tuples, "%.0f"); WRITE_INT_FIELD(ncolumns); + WRITE_OID_FIELD(relam); WRITE_NODE_FIELD(indexprs); WRITE_NODE_FIELD(indpred); WRITE_BOOL_FIELD(predOK); WRITE_BOOL_FIELD(unique); + WRITE_BOOL_FIELD(hypothetical); } static void @@ -1744,6 +1779,7 @@ _outPathKey(StringInfo str, PathKey *node) WRITE_NODE_FIELD(pk_eclass); WRITE_OID_FIELD(pk_opfamily); + WRITE_OID_FIELD(pk_collation); WRITE_INT_FIELD(pk_strategy); WRITE_BOOL_FIELD(pk_nulls_first); } @@ -2042,6 +2078,8 @@ _outTypeName(StringInfo str, TypeName *node) WRITE_NODE_FIELD(typmods); WRITE_INT_FIELD(typemod); WRITE_NODE_FIELD(arrayBounds); + WRITE_NODE_FIELD(collnames); + WRITE_OID_FIELD(collOid); WRITE_LOCATION_FIELD(location); } @@ -2056,6 +2094,17 @@ _outTypeCast(StringInfo str, TypeCast *node) } static void +_outCollateClause(StringInfo str, CollateClause *node) +{ + WRITE_NODE_TYPE("COLLATE"); + + WRITE_NODE_FIELD(arg); + WRITE_NODE_FIELD(collnames); + WRITE_OID_FIELD(collOid); + WRITE_LOCATION_FIELD(location); +} + +static void _outIndexElem(StringInfo str, IndexElem *node) { WRITE_NODE_TYPE("INDEXELEM"); @@ -2063,6 +2112,7 @@ _outIndexElem(StringInfo str, IndexElem *node) WRITE_STRING_FIELD(name); WRITE_NODE_FIELD(expr); WRITE_STRING_FIELD(indexcolname); + WRITE_NODE_FIELD(collation); WRITE_NODE_FIELD(opclass); WRITE_ENUM_FIELD(ordering, SortByDir); WRITE_ENUM_FIELD(nulls_ordering, SortByNulls); @@ -2190,6 +2240,7 @@ _outCommonTableExpr(StringInfo str, CommonTableExpr *node) WRITE_NODE_FIELD(ctecolnames); WRITE_NODE_FIELD(ctecoltypes); WRITE_NODE_FIELD(ctecoltypmods); + WRITE_NODE_FIELD(ctecolcollations); } static void @@ -2203,6 +2254,7 @@ _outSetOperationStmt(StringInfo str, SetOperationStmt *node) WRITE_NODE_FIELD(rarg); WRITE_NODE_FIELD(colTypes); WRITE_NODE_FIELD(colTypmods); + WRITE_NODE_FIELD(colCollations); WRITE_NODE_FIELD(groupClauses); } @@ -2233,6 +2285,7 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node) WRITE_NODE_FIELD(funcexpr); WRITE_NODE_FIELD(funccoltypes); WRITE_NODE_FIELD(funccoltypmods); + WRITE_NODE_FIELD(funccolcollations); break; case RTE_VALUES: WRITE_NODE_FIELD(values_lists); @@ -2243,6 +2296,7 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node) WRITE_BOOL_FIELD(self_reference); WRITE_NODE_FIELD(ctecoltypes); WRITE_NODE_FIELD(ctecoltypmods); + WRITE_NODE_FIELD(ctecolcollations); break; default: elog(ERROR, "unrecognized RTE kind: %d", (int) node->rtekind); @@ -2766,6 +2820,9 @@ _outNode(StringInfo str, void *obj) case T_RelabelType: _outRelabelType(str, obj); break; + case T_CollateClause: + _outCollateClause(str, obj); + break; case T_CoerceViaIO: _outCoerceViaIO(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 99d0576e5e..b007caeee3 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -323,6 +323,7 @@ _readCommonTableExpr(void) READ_NODE_FIELD(ctecolnames); READ_NODE_FIELD(ctecoltypes); READ_NODE_FIELD(ctecoltypmods); + READ_NODE_FIELD(ctecolcollations); READ_DONE(); } @@ -341,6 +342,7 @@ _readSetOperationStmt(void) READ_NODE_FIELD(rarg); READ_NODE_FIELD(colTypes); READ_NODE_FIELD(colTypmods); + READ_NODE_FIELD(colCollations); READ_NODE_FIELD(groupClauses); READ_DONE(); @@ -406,6 +408,7 @@ _readVar(void) READ_INT_FIELD(varattno); READ_OID_FIELD(vartype); READ_INT_FIELD(vartypmod); + READ_OID_FIELD(varcollid); READ_UINT_FIELD(varlevelsup); READ_UINT_FIELD(varnoold); READ_INT_FIELD(varoattno); @@ -424,6 +427,7 @@ _readConst(void) READ_OID_FIELD(consttype); READ_INT_FIELD(consttypmod); + READ_OID_FIELD(constcollid); READ_INT_FIELD(constlen); READ_BOOL_FIELD(constbyval); READ_BOOL_FIELD(constisnull); @@ -450,6 +454,7 @@ _readParam(void) READ_INT_FIELD(paramid); READ_OID_FIELD(paramtype); READ_INT_FIELD(paramtypmod); + READ_OID_FIELD(paramcollation); READ_LOCATION_FIELD(location); READ_DONE(); @@ -470,6 +475,7 @@ _readAggref(void) READ_NODE_FIELD(aggdistinct); READ_BOOL_FIELD(aggstar); READ_UINT_FIELD(agglevelsup); + READ_OID_FIELD(collid); READ_LOCATION_FIELD(location); READ_DONE(); @@ -489,6 +495,7 @@ _readWindowFunc(void) READ_UINT_FIELD(winref); READ_BOOL_FIELD(winstar); READ_BOOL_FIELD(winagg); + READ_OID_FIELD(collid); READ_LOCATION_FIELD(location); READ_DONE(); @@ -505,6 +512,7 @@ _readArrayRef(void) READ_OID_FIELD(refarraytype); READ_OID_FIELD(refelemtype); READ_INT_FIELD(reftypmod); + READ_INT_FIELD(refcollid); READ_NODE_FIELD(refupperindexpr); READ_NODE_FIELD(reflowerindexpr); READ_NODE_FIELD(refexpr); @@ -526,6 +534,7 @@ _readFuncExpr(void) READ_BOOL_FIELD(funcretset); READ_ENUM_FIELD(funcformat, CoercionForm); READ_NODE_FIELD(args); + READ_OID_FIELD(collid); READ_LOCATION_FIELD(location); READ_DONE(); @@ -571,6 +580,7 @@ _readOpExpr(void) READ_OID_FIELD(opresulttype); READ_BOOL_FIELD(opretset); READ_NODE_FIELD(args); + READ_OID_FIELD(collid); READ_LOCATION_FIELD(location); READ_DONE(); @@ -600,6 +610,7 @@ _readDistinctExpr(void) READ_OID_FIELD(opresulttype); READ_BOOL_FIELD(opretset); READ_NODE_FIELD(args); + READ_OID_FIELD(collid); READ_LOCATION_FIELD(location); READ_DONE(); @@ -628,6 +639,7 @@ _readScalarArrayOpExpr(void) READ_BOOL_FIELD(useOr); READ_NODE_FIELD(args); + READ_OID_FIELD(collid); READ_LOCATION_FIELD(location); READ_DONE(); @@ -692,6 +704,7 @@ _readFieldSelect(void) READ_INT_FIELD(fieldnum); READ_OID_FIELD(resulttype); READ_INT_FIELD(resulttypmod); + READ_OID_FIELD(resultcollation); READ_DONE(); } @@ -730,6 +743,22 @@ _readRelabelType(void) } /* + * _readCollateClause + */ +static CollateClause * +_readCollateClause(void) +{ + READ_LOCALS(CollateClause); + + READ_NODE_FIELD(arg); + READ_NODE_FIELD(collnames); + READ_OID_FIELD(collOid); + READ_LOCATION_FIELD(location); + + READ_DONE(); +} + +/* * _readCoerceViaIO */ static CoerceViaIO * @@ -789,6 +818,7 @@ _readCaseExpr(void) READ_LOCALS(CaseExpr); READ_OID_FIELD(casetype); + READ_OID_FIELD(casecollation); READ_NODE_FIELD(arg); READ_NODE_FIELD(args); READ_NODE_FIELD(defresult); @@ -822,6 +852,7 @@ _readCaseTestExpr(void) READ_OID_FIELD(typeId); READ_INT_FIELD(typeMod); + READ_OID_FIELD(collation); READ_DONE(); } @@ -871,6 +902,7 @@ _readRowCompareExpr(void) READ_ENUM_FIELD(rctype, RowCompareType); READ_NODE_FIELD(opnos); READ_NODE_FIELD(opfamilies); + READ_NODE_FIELD(collids); READ_NODE_FIELD(largs); READ_NODE_FIELD(rargs); @@ -886,6 +918,7 @@ _readCoalesceExpr(void) READ_LOCALS(CoalesceExpr); READ_OID_FIELD(coalescetype); + READ_OID_FIELD(coalescecollation); READ_NODE_FIELD(args); READ_LOCATION_FIELD(location); @@ -903,6 +936,7 @@ _readMinMaxExpr(void) READ_OID_FIELD(minmaxtype); READ_ENUM_FIELD(op, MinMaxOp); READ_NODE_FIELD(args); + READ_OID_FIELD(collid); READ_LOCATION_FIELD(location); READ_DONE(); @@ -1029,6 +1063,7 @@ _readSetToDefault(void) READ_OID_FIELD(typeId); READ_INT_FIELD(typeMod); + READ_OID_FIELD(collid); READ_LOCATION_FIELD(location); READ_DONE(); @@ -1150,6 +1185,7 @@ _readRangeTblEntry(void) READ_NODE_FIELD(funcexpr); READ_NODE_FIELD(funccoltypes); READ_NODE_FIELD(funccoltypmods); + READ_NODE_FIELD(funccolcollations); break; case RTE_VALUES: READ_NODE_FIELD(values_lists); @@ -1160,6 +1196,7 @@ _readRangeTblEntry(void) READ_BOOL_FIELD(self_reference); READ_NODE_FIELD(ctecoltypes); READ_NODE_FIELD(ctecoltypmods); + READ_NODE_FIELD(ctecolcollations); break; default: elog(ERROR, "unrecognized RTE kind: %d", @@ -1248,6 +1285,8 @@ parseNodeString(void) return_value = _readFieldStore(); else if (MATCH("RELABELTYPE", 11)) return_value = _readRelabelType(); + else if (MATCH("COLLATE", 7)) + return_value = _readCollateClause(); else if (MATCH("COERCEVIAIO", 11)) return_value = _readCoerceViaIO(); else if (MATCH("ARRAYCOERCEEXPR", 15)) diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index 6c98c49bff..ffb066283f 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -1795,6 +1795,7 @@ cost_mergejoin(MergePath *path, PlannerInfo *root, SpecialJoinInfo *sjinfo) ipathkey = (PathKey *) linitial(ipathkeys); /* debugging check */ if (opathkey->pk_opfamily != ipathkey->pk_opfamily || + opathkey->pk_collation != ipathkey->pk_collation || opathkey->pk_strategy != ipathkey->pk_strategy || opathkey->pk_nulls_first != ipathkey->pk_nulls_first) elog(ERROR, "left and right pathkeys do not match in mergejoin"); @@ -2045,6 +2046,7 @@ cached_scansel(PlannerInfo *root, RestrictInfo *rinfo, PathKey *pathkey) { cache = (MergeScanSelCache *) lfirst(lc); if (cache->opfamily == pathkey->pk_opfamily && + cache->collation == pathkey->pk_collation && cache->strategy == pathkey->pk_strategy && cache->nulls_first == pathkey->pk_nulls_first) return cache; @@ -2054,6 +2056,7 @@ cached_scansel(PlannerInfo *root, RestrictInfo *rinfo, PathKey *pathkey) mergejoinscansel(root, (Node *) rinfo->clause, pathkey->pk_opfamily, + pathkey->pk_collation, pathkey->pk_strategy, pathkey->pk_nulls_first, &leftstartsel, @@ -2066,6 +2069,7 @@ cached_scansel(PlannerInfo *root, RestrictInfo *rinfo, PathKey *pathkey) cache = (MergeScanSelCache *) palloc(sizeof(MergeScanSelCache)); cache->opfamily = pathkey->pk_opfamily; + cache->collation = pathkey->pk_collation; cache->strategy = pathkey->pk_strategy; cache->nulls_first = pathkey->pk_nulls_first; cache->leftstartsel = leftstartsel; diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index a3101d7ea7..65bc9be8da 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -23,6 +23,7 @@ #include "catalog/pg_opfamily.h" #include "catalog/pg_type.h" #include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" #include "optimizer/pathnode.h" @@ -99,15 +100,15 @@ static List *find_clauses_for_join(PlannerInfo *root, RelOptInfo *rel, Relids outer_relids, bool isouterjoin); static bool match_boolean_index_clause(Node *clause, int indexcol, IndexOptInfo *index); -static bool match_special_index_operator(Expr *clause, Oid opfamily, +static bool match_special_index_operator(Expr *clause, Oid idxcolcollation, Oid opfamily, bool indexkey_on_left); static Expr *expand_boolean_index_clause(Node *clause, int indexcol, IndexOptInfo *index); -static List *expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily); +static List *expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily, Oid collation); static RestrictInfo *expand_indexqual_rowcompare(RestrictInfo *rinfo, IndexOptInfo *index, int indexcol); -static List *prefix_quals(Node *leftop, Oid opfamily, +static List *prefix_quals(Node *leftop, Oid opfamily, Oid collation, Const *prefix, Pattern_Prefix_Status pstatus); static List *network_prefix_quals(Node *leftop, Oid expr_op, Oid opfamily, Datum rightop); @@ -1142,7 +1143,9 @@ group_clauses_by_indexkey(IndexOptInfo *index, * and * (2) must contain an operator which is in the same family as the index * operator for this column, or is a "special" operator as recognized - * by match_special_index_operator(). + * by match_special_index_operator(); + * and + * (3) must match the collation of the index. * * Our definition of "const" is pretty liberal: we allow Vars belonging * to the caller-specified outer_relids relations (which had better not @@ -1198,6 +1201,7 @@ match_clause_to_indexcol(IndexOptInfo *index, SaOpControl saop_control) { Expr *clause = rinfo->clause; + Oid collation = index->indexcollations[indexcol]; Oid opfamily = index->opfamily[indexcol]; Node *leftop, *rightop; @@ -1280,7 +1284,8 @@ match_clause_to_indexcol(IndexOptInfo *index, bms_is_subset(right_relids, outer_relids) && !contain_volatile_functions(rightop)) { - if (is_indexable_operator(expr_op, opfamily, true)) + if (is_indexable_operator(expr_op, opfamily, true) && + (!collation || collation == exprCollation((Node *) clause))) return true; /* @@ -1288,7 +1293,7 @@ match_clause_to_indexcol(IndexOptInfo *index, * is a "special" indexable operator. */ if (plain_op && - match_special_index_operator(clause, opfamily, true)) + match_special_index_operator(clause, collation, opfamily, true)) return true; return false; } @@ -1298,14 +1303,15 @@ match_clause_to_indexcol(IndexOptInfo *index, bms_is_subset(left_relids, outer_relids) && !contain_volatile_functions(leftop)) { - if (is_indexable_operator(expr_op, opfamily, false)) + if (is_indexable_operator(expr_op, opfamily, false) && + (!collation || collation == exprCollation((Node *) clause))) return true; /* * If we didn't find a member of the index's opfamily, see whether it * is a "special" indexable operator. */ - if (match_special_index_operator(clause, opfamily, false)) + if (match_special_index_operator(clause, collation, opfamily, false)) return true; return false; } @@ -1391,6 +1397,9 @@ match_rowcompare_to_indexcol(IndexOptInfo *index, else return false; + if (index->indexcollations[indexcol] != linitial_oid(clause->collids)) + return false; + /* We're good if the operator is the right type of opfamily member */ switch (get_op_opfamily_strategy(expr_op, opfamily)) { @@ -2380,7 +2389,7 @@ match_boolean_index_clause(Node *clause, * Return 'true' if we can do something with it anyway. */ static bool -match_special_index_operator(Expr *clause, Oid opfamily, +match_special_index_operator(Expr *clause, Oid idxcolcollation, Oid opfamily, bool indexkey_on_left) { bool isIndexable = false; @@ -2495,7 +2504,7 @@ match_special_index_operator(Expr *clause, Oid opfamily, isIndexable = (opfamily == TEXT_PATTERN_BTREE_FAM_OID) || (opfamily == TEXT_BTREE_FAM_OID && - (pstatus == Pattern_Prefix_Exact || lc_collate_is_c())); + (pstatus == Pattern_Prefix_Exact || lc_collate_is_c(idxcolcollation))); break; case OID_BPCHAR_LIKE_OP: @@ -2505,7 +2514,7 @@ match_special_index_operator(Expr *clause, Oid opfamily, isIndexable = (opfamily == BPCHAR_PATTERN_BTREE_FAM_OID) || (opfamily == BPCHAR_BTREE_FAM_OID && - (pstatus == Pattern_Prefix_Exact || lc_collate_is_c())); + (pstatus == Pattern_Prefix_Exact || lc_collate_is_c(idxcolcollation))); break; case OID_NAME_LIKE_OP: @@ -2526,6 +2535,25 @@ match_special_index_operator(Expr *clause, Oid opfamily, break; } + if (!isIndexable) + return false; + + /* + * For case-insensitive matching, we also need to check that the + * collations match. + */ + switch (expr_op) + { + case OID_TEXT_ICLIKE_OP: + case OID_TEXT_ICREGEXEQ_OP: + case OID_BPCHAR_ICLIKE_OP: + case OID_BPCHAR_ICREGEXEQ_OP: + case OID_NAME_ICLIKE_OP: + case OID_NAME_ICREGEXEQ_OP: + isIndexable = (idxcolcollation == exprCollation((Node *) clause)); + break; + } + return isIndexable; } @@ -2561,6 +2589,7 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups) { List *clausegroup = (List *) lfirst(lc); Oid curFamily = index->opfamily[indexcol]; + Oid curCollation = index->indexcollations[indexcol]; ListCell *lc2; foreach(lc2, clausegroup) @@ -2592,7 +2621,8 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups) { resultquals = list_concat(resultquals, expand_indexqual_opclause(rinfo, - curFamily)); + curFamily, + curCollation)); } else if (IsA(clause, ScalarArrayOpExpr)) { @@ -2693,7 +2723,7 @@ expand_boolean_index_clause(Node *clause, * expand special cases that were accepted by match_special_index_operator(). */ static List * -expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily) +expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily, Oid collation) { Expr *clause = rinfo->clause; @@ -2724,7 +2754,7 @@ expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily) { pstatus = pattern_fixed_prefix(patt, Pattern_Type_Like, &prefix, &rest); - return prefix_quals(leftop, opfamily, prefix, pstatus); + return prefix_quals(leftop, opfamily, collation, prefix, pstatus); } break; @@ -2736,7 +2766,7 @@ expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily) /* the right-hand const is type text for all of these */ pstatus = pattern_fixed_prefix(patt, Pattern_Type_Like_IC, &prefix, &rest); - return prefix_quals(leftop, opfamily, prefix, pstatus); + return prefix_quals(leftop, opfamily, collation, prefix, pstatus); } break; @@ -2748,7 +2778,7 @@ expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily) /* the right-hand const is type text for all of these */ pstatus = pattern_fixed_prefix(patt, Pattern_Type_Regex, &prefix, &rest); - return prefix_quals(leftop, opfamily, prefix, pstatus); + return prefix_quals(leftop, opfamily, collation, prefix, pstatus); } break; @@ -2760,7 +2790,7 @@ expand_indexqual_opclause(RestrictInfo *rinfo, Oid opfamily) /* the right-hand const is type text for all of these */ pstatus = pattern_fixed_prefix(patt, Pattern_Type_Regex_IC, &prefix, &rest); - return prefix_quals(leftop, opfamily, prefix, pstatus); + return prefix_quals(leftop, opfamily, collation, prefix, pstatus); } break; @@ -2814,6 +2844,7 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo, ListCell *largs_cell; ListCell *rargs_cell; ListCell *opnos_cell; + ListCell *collids_cell; /* We have to figure out (again) how the first col matches */ var_on_left = match_index_to_operand((Node *) linitial(clause->largs), @@ -2845,6 +2876,7 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo, largs_cell = lnext(list_head(clause->largs)); rargs_cell = lnext(list_head(clause->rargs)); opnos_cell = lnext(list_head(clause->opnos)); + collids_cell = lnext(list_head(clause->collids)); while (largs_cell != NULL) { @@ -2891,6 +2923,10 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo, != op_strategy) break; + /* Does collation match? */ + if (lfirst_oid(collids_cell) != index->indexcollations[i]) + break; + /* Add opfamily and datatypes to lists */ get_op_opfamily_properties(expr_op, index->opfamily[i], false, &op_strategy, @@ -2974,6 +3010,8 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo, rc->opnos = new_ops; rc->opfamilies = list_truncate(list_copy(clause->opfamilies), matching_cols); + rc->collids = list_truncate(list_copy(clause->collids), + matching_cols); rc->largs = list_truncate((List *) copyObject(clause->largs), matching_cols); rc->rargs = list_truncate((List *) copyObject(clause->rargs), @@ -2998,7 +3036,7 @@ expand_indexqual_rowcompare(RestrictInfo *rinfo, * operators and operand datatypes. */ static List * -prefix_quals(Node *leftop, Oid opfamily, +prefix_quals(Node *leftop, Oid opfamily, Oid collation, Const *prefix_const, Pattern_Prefix_Status pstatus) { List *result; @@ -3100,6 +3138,7 @@ prefix_quals(Node *leftop, Oid opfamily, if (oproid == InvalidOid) elog(ERROR, "no < operator for opfamily %u", opfamily); fmgr_info(get_opcode(oproid), <proc); + fmgr_info_collation(collation, <proc); greaterstr = make_greater_string(prefix_const, <proc); if (greaterstr) { diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c index d5536fc2b3..fd759281ed 100644 --- a/src/backend/optimizer/path/pathkeys.c +++ b/src/backend/optimizer/path/pathkeys.c @@ -18,6 +18,7 @@ #include "postgres.h" #include "access/skey.h" +#include "catalog/pg_collation.h" #include "catalog/pg_type.h" #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" @@ -30,10 +31,10 @@ #include "utils/lsyscache.h" -static PathKey *makePathKey(EquivalenceClass *eclass, Oid opfamily, +static PathKey *makePathKey(EquivalenceClass *eclass, Oid opfamily, Oid collation, int strategy, bool nulls_first); static PathKey *make_canonical_pathkey(PlannerInfo *root, - EquivalenceClass *eclass, Oid opfamily, + EquivalenceClass *eclass, Oid opfamily, Oid collation, int strategy, bool nulls_first); static bool pathkey_is_redundant(PathKey *new_pathkey, List *pathkeys); static Var *find_indexkey_var(PlannerInfo *root, RelOptInfo *rel, @@ -53,13 +54,14 @@ static bool right_merge_direction(PlannerInfo *root, PathKey *pathkey); * convenience routine to build the specified node. */ static PathKey * -makePathKey(EquivalenceClass *eclass, Oid opfamily, +makePathKey(EquivalenceClass *eclass, Oid opfamily, Oid collation, int strategy, bool nulls_first) { PathKey *pk = makeNode(PathKey); pk->pk_eclass = eclass; pk->pk_opfamily = opfamily; + pk->pk_collation = collation; pk->pk_strategy = strategy; pk->pk_nulls_first = nulls_first; @@ -77,7 +79,7 @@ makePathKey(EquivalenceClass *eclass, Oid opfamily, */ static PathKey * make_canonical_pathkey(PlannerInfo *root, - EquivalenceClass *eclass, Oid opfamily, + EquivalenceClass *eclass, Oid opfamily, Oid collation, int strategy, bool nulls_first) { PathKey *pk; @@ -93,6 +95,7 @@ make_canonical_pathkey(PlannerInfo *root, pk = (PathKey *) lfirst(lc); if (eclass == pk->pk_eclass && opfamily == pk->pk_opfamily && + collation == pk->pk_collation && strategy == pk->pk_strategy && nulls_first == pk->pk_nulls_first) return pk; @@ -104,7 +107,7 @@ make_canonical_pathkey(PlannerInfo *root, */ oldcontext = MemoryContextSwitchTo(root->planner_cxt); - pk = makePathKey(eclass, opfamily, strategy, nulls_first); + pk = makePathKey(eclass, opfamily, collation, strategy, nulls_first); root->canon_pathkeys = lappend(root->canon_pathkeys, pk); MemoryContextSwitchTo(oldcontext); @@ -206,6 +209,7 @@ canonicalize_pathkeys(PlannerInfo *root, List *pathkeys) cpathkey = make_canonical_pathkey(root, eclass, pathkey->pk_opfamily, + pathkey->pk_collation, pathkey->pk_strategy, pathkey->pk_nulls_first); @@ -247,6 +251,7 @@ make_pathkey_from_sortinfo(PlannerInfo *root, Oid equality_op; List *opfamilies; EquivalenceClass *eclass; + Oid collation; strategy = reverse_sort ? BTGreaterStrategyNumber : BTLessStrategyNumber; @@ -301,12 +306,14 @@ make_pathkey_from_sortinfo(PlannerInfo *root, if (!eclass) return NULL; + collation = exprCollation((Node *) expr); + /* And finally we can find or create a PathKey node */ if (canonicalize) - return make_canonical_pathkey(root, eclass, opfamily, + return make_canonical_pathkey(root, eclass, opfamily, collation, strategy, nulls_first); else - return makePathKey(eclass, opfamily, strategy, nulls_first); + return makePathKey(eclass, opfamily, collation, strategy, nulls_first); } /* @@ -605,7 +612,8 @@ find_indexkey_var(PlannerInfo *root, RelOptInfo *rel, AttrNumber varattno) ListCell *temp; Index relid; Oid reloid, - vartypeid; + vartypeid, + varcollid; int32 type_mod; foreach(temp, rel->reltargetlist) @@ -620,8 +628,9 @@ find_indexkey_var(PlannerInfo *root, RelOptInfo *rel, AttrNumber varattno) relid = rel->relid; reloid = getrelid(relid, root->parse->rtable); get_atttypetypmod(reloid, varattno, &vartypeid, &type_mod); + varcollid = get_attcollation(reloid, varattno); - return makeVar(relid, varattno, vartypeid, type_mod, 0); + return makeVar(relid, varattno, vartypeid, type_mod, varcollid, 0); } /* @@ -703,6 +712,7 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel, make_canonical_pathkey(root, outer_ec, sub_pathkey->pk_opfamily, + sub_pathkey->pk_collation, sub_pathkey->pk_strategy, sub_pathkey->pk_nulls_first); } @@ -805,6 +815,7 @@ convert_subquery_pathkeys(PlannerInfo *root, RelOptInfo *rel, outer_pk = make_canonical_pathkey(root, outer_ec, sub_pathkey->pk_opfamily, + sub_pathkey->pk_collation, sub_pathkey->pk_strategy, sub_pathkey->pk_nulls_first); /* score = # of equivalence peers */ @@ -1326,6 +1337,7 @@ select_outer_pathkeys_for_merge(PlannerInfo *root, pathkey = make_canonical_pathkey(root, ec, linitial_oid(ec->ec_opfamilies), + DEFAULT_COLLATION_OID, BTLessStrategyNumber, false); /* can't be redundant because no duplicate ECs */ @@ -1419,6 +1431,7 @@ make_inner_pathkeys_for_merge(PlannerInfo *root, pathkey = make_canonical_pathkey(root, ieclass, opathkey->pk_opfamily, + opathkey->pk_collation, opathkey->pk_strategy, opathkey->pk_nulls_first); @@ -1539,6 +1552,7 @@ right_merge_direction(PlannerInfo *root, PathKey *pathkey) PathKey *query_pathkey = (PathKey *) lfirst(l); if (pathkey->pk_eclass == query_pathkey->pk_eclass && + pathkey->pk_collation == query_pathkey->pk_collation && pathkey->pk_opfamily == query_pathkey->pk_opfamily) { /* diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 938b8af847..2fd63a144f 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -108,7 +108,7 @@ static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid, List *tidquals); static FunctionScan *make_functionscan(List *qptlist, List *qpqual, Index scanrelid, Node *funcexpr, List *funccolnames, - List *funccoltypes, List *funccoltypmods); + List *funccoltypes, List *funccoltypmods, List *funccolcollations); static ValuesScan *make_valuesscan(List *qptlist, List *qpqual, Index scanrelid, List *values_lists); static CteScan *make_ctescan(List *qptlist, List *qpqual, @@ -138,12 +138,13 @@ static MergeJoin *make_mergejoin(List *tlist, List *joinclauses, List *otherclauses, List *mergeclauses, Oid *mergefamilies, + Oid *mergecollations, int *mergestrategies, bool *mergenullsfirst, Plan *lefttree, Plan *righttree, JoinType jointype); static Sort *make_sort(PlannerInfo *root, Plan *lefttree, int numCols, - AttrNumber *sortColIdx, Oid *sortOperators, bool *nullsFirst, + AttrNumber *sortColIdx, Oid *sortOperators, Oid *collations, bool *nullsFirst, double limit_tuples); static Plan *prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, @@ -151,6 +152,7 @@ static Plan *prepare_sort_from_pathkeys(PlannerInfo *root, int *p_numsortkeys, AttrNumber **p_sortColIdx, Oid **p_sortOperators, + Oid **p_collations, bool **p_nullsFirst); static Material *make_material(Plan *lefttree); @@ -685,6 +687,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path) &node->numCols, &node->sortColIdx, &node->sortOperators, + &node->collations, &node->nullsFirst); /* @@ -699,6 +702,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path) int numsortkeys; AttrNumber *sortColIdx; Oid *sortOperators; + Oid *collations; bool *nullsFirst; /* Build the child plan */ @@ -710,6 +714,7 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path) &numsortkeys, &sortColIdx, &sortOperators, + &collations, &nullsFirst); /* @@ -724,13 +729,15 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path) elog(ERROR, "MergeAppend child's targetlist doesn't match MergeAppend"); Assert(memcmp(sortOperators, node->sortOperators, numsortkeys * sizeof(Oid)) == 0); + Assert(memcmp(collations, node->collations, + numsortkeys * sizeof(Oid)) == 0); Assert(memcmp(nullsFirst, node->nullsFirst, numsortkeys * sizeof(bool)) == 0); /* Now, insert a Sort node if subplan isn't sufficiently ordered */ if (!pathkeys_contained_in(pathkeys, subpath->pathkeys)) subplan = (Plan *) make_sort(root, subplan, numsortkeys, - sortColIdx, sortOperators, nullsFirst, + sortColIdx, sortOperators, collations, nullsFirst, best_path->limit_tuples); subplans = lappend(subplans, subplan); @@ -1583,7 +1590,8 @@ create_functionscan_plan(PlannerInfo *root, Path *best_path, rte->funcexpr, rte->eref->colnames, rte->funccoltypes, - rte->funccoltypmods); + rte->funccoltypmods, + rte->funccolcollations); copy_path_costsize(&scan_plan->scan.plan, best_path); @@ -1898,6 +1906,7 @@ create_mergejoin_plan(PlannerInfo *root, List *innerpathkeys; int nClauses; Oid *mergefamilies; + Oid *mergecollations; int *mergestrategies; bool *mergenullsfirst; MergeJoin *join_plan; @@ -1997,6 +2006,7 @@ create_mergejoin_plan(PlannerInfo *root, nClauses = list_length(mergeclauses); Assert(nClauses == list_length(best_path->path_mergeclauses)); mergefamilies = (Oid *) palloc(nClauses * sizeof(Oid)); + mergecollations = (Oid *) palloc(nClauses * sizeof(Oid)); mergestrategies = (int *) palloc(nClauses * sizeof(int)); mergenullsfirst = (bool *) palloc(nClauses * sizeof(bool)); @@ -2125,12 +2135,14 @@ create_mergejoin_plan(PlannerInfo *root, /* pathkeys should match each other too (more debugging) */ if (opathkey->pk_opfamily != ipathkey->pk_opfamily || + opathkey->pk_collation != ipathkey->pk_collation || opathkey->pk_strategy != ipathkey->pk_strategy || opathkey->pk_nulls_first != ipathkey->pk_nulls_first) elog(ERROR, "left and right pathkeys do not match in mergejoin"); /* OK, save info for executor */ mergefamilies[i] = opathkey->pk_opfamily; + mergecollations[i] = opathkey->pk_collation; mergestrategies[i] = opathkey->pk_strategy; mergenullsfirst[i] = opathkey->pk_nulls_first; i++; @@ -2150,6 +2162,7 @@ create_mergejoin_plan(PlannerInfo *root, otherclauses, mergeclauses, mergefamilies, + mergecollations, mergestrategies, mergenullsfirst, outer_plan, @@ -2579,6 +2592,7 @@ fix_indexqual_operand(Node *node, IndexOptInfo *index) /* Found a match */ result = makeVar(index->rel->relid, pos + 1, exprType(lfirst(indexpr_item)), -1, + exprCollation(lfirst(indexpr_item)), 0); return (Node *) result; } @@ -2932,7 +2946,8 @@ make_functionscan(List *qptlist, Node *funcexpr, List *funccolnames, List *funccoltypes, - List *funccoltypmods) + List *funccoltypmods, + List *funccolcollations) { FunctionScan *node = makeNode(FunctionScan); Plan *plan = &node->scan.plan; @@ -2947,6 +2962,7 @@ make_functionscan(List *qptlist, node->funccolnames = funccolnames; node->funccoltypes = funccoltypes; node->funccoltypmods = funccoltypmods; + node->funccolcollations = funccolcollations; return node; } @@ -3253,6 +3269,7 @@ make_mergejoin(List *tlist, List *otherclauses, List *mergeclauses, Oid *mergefamilies, + Oid *mergecollations, int *mergestrategies, bool *mergenullsfirst, Plan *lefttree, @@ -3269,6 +3286,7 @@ make_mergejoin(List *tlist, plan->righttree = righttree; node->mergeclauses = mergeclauses; node->mergeFamilies = mergefamilies; + node->mergeCollations = mergecollations; node->mergeStrategies = mergestrategies; node->mergeNullsFirst = mergenullsfirst; node->join.jointype = jointype; @@ -3286,7 +3304,7 @@ make_mergejoin(List *tlist, */ static Sort * make_sort(PlannerInfo *root, Plan *lefttree, int numCols, - AttrNumber *sortColIdx, Oid *sortOperators, bool *nullsFirst, + AttrNumber *sortColIdx, Oid *sortOperators, Oid *collations, bool *nullsFirst, double limit_tuples) { Sort *node = makeNode(Sort); @@ -3310,6 +3328,7 @@ make_sort(PlannerInfo *root, Plan *lefttree, int numCols, node->numCols = numCols; node->sortColIdx = sortColIdx; node->sortOperators = sortOperators; + node->collations = collations; node->nullsFirst = nullsFirst; return node; @@ -3325,9 +3344,9 @@ make_sort(PlannerInfo *root, Plan *lefttree, int numCols, * max possible number of columns. Return value is the new column count. */ static int -add_sort_column(AttrNumber colIdx, Oid sortOp, bool nulls_first, +add_sort_column(AttrNumber colIdx, Oid sortOp, Oid coll, bool nulls_first, int numCols, AttrNumber *sortColIdx, - Oid *sortOperators, bool *nullsFirst) + Oid *sortOperators, Oid *collations, bool *nullsFirst) { int i; @@ -3343,7 +3362,8 @@ add_sort_column(AttrNumber colIdx, Oid sortOp, bool nulls_first, * opposite nulls direction is redundant. */ if (sortColIdx[i] == colIdx && - sortOperators[numCols] == sortOp) + sortOperators[numCols] == sortOp && + collations[numCols] == coll) { /* Already sorting by this col, so extra sort key is useless */ return numCols; @@ -3353,6 +3373,7 @@ add_sort_column(AttrNumber colIdx, Oid sortOp, bool nulls_first, /* Add the column */ sortColIdx[numCols] = colIdx; sortOperators[numCols] = sortOp; + collations[numCols] = coll; nullsFirst[numCols] = nulls_first; return numCols + 1; } @@ -3392,6 +3413,7 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, int *p_numsortkeys, AttrNumber **p_sortColIdx, Oid **p_sortOperators, + Oid **p_collations, bool **p_nullsFirst) { List *tlist = lefttree->targetlist; @@ -3399,6 +3421,7 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, int numsortkeys; AttrNumber *sortColIdx; Oid *sortOperators; + Oid *collations; bool *nullsFirst; /* @@ -3407,6 +3430,7 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, numsortkeys = list_length(pathkeys); sortColIdx = (AttrNumber *) palloc(numsortkeys * sizeof(AttrNumber)); sortOperators = (Oid *) palloc(numsortkeys * sizeof(Oid)); + collations = (Oid *) palloc(numsortkeys * sizeof(Oid)); nullsFirst = (bool *) palloc(numsortkeys * sizeof(bool)); numsortkeys = 0; @@ -3565,9 +3589,10 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, */ numsortkeys = add_sort_column(tle->resno, sortop, + pathkey->pk_collation, pathkey->pk_nulls_first, numsortkeys, - sortColIdx, sortOperators, nullsFirst); + sortColIdx, sortOperators, collations, nullsFirst); } Assert(numsortkeys > 0); @@ -3576,6 +3601,7 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, *p_numsortkeys = numsortkeys; *p_sortColIdx = sortColIdx; *p_sortOperators = sortOperators; + *p_collations = collations; *p_nullsFirst = nullsFirst; return lefttree; @@ -3597,6 +3623,7 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, int numsortkeys; AttrNumber *sortColIdx; Oid *sortOperators; + Oid *collations; bool *nullsFirst; /* Compute sort column info, and adjust lefttree as needed */ @@ -3605,11 +3632,12 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, &numsortkeys, &sortColIdx, &sortOperators, + &collations, &nullsFirst); /* Now build the Sort node */ return make_sort(root, lefttree, numsortkeys, - sortColIdx, sortOperators, nullsFirst, limit_tuples); + sortColIdx, sortOperators, collations, nullsFirst, limit_tuples); } /* @@ -3627,6 +3655,7 @@ make_sort_from_sortclauses(PlannerInfo *root, List *sortcls, Plan *lefttree) int numsortkeys; AttrNumber *sortColIdx; Oid *sortOperators; + Oid *collations; bool *nullsFirst; /* @@ -3635,6 +3664,7 @@ make_sort_from_sortclauses(PlannerInfo *root, List *sortcls, Plan *lefttree) numsortkeys = list_length(sortcls); sortColIdx = (AttrNumber *) palloc(numsortkeys * sizeof(AttrNumber)); sortOperators = (Oid *) palloc(numsortkeys * sizeof(Oid)); + collations = (Oid *) palloc(numsortkeys * sizeof(Oid)); nullsFirst = (bool *) palloc(numsortkeys * sizeof(bool)); numsortkeys = 0; @@ -3650,15 +3680,16 @@ make_sort_from_sortclauses(PlannerInfo *root, List *sortcls, Plan *lefttree) * redundantly. */ numsortkeys = add_sort_column(tle->resno, sortcl->sortop, + exprCollation((Node *) tle->expr), sortcl->nulls_first, numsortkeys, - sortColIdx, sortOperators, nullsFirst); + sortColIdx, sortOperators, collations, nullsFirst); } Assert(numsortkeys > 0); return make_sort(root, lefttree, numsortkeys, - sortColIdx, sortOperators, nullsFirst, -1.0); + sortColIdx, sortOperators, collations, nullsFirst, -1.0); } /* @@ -3686,6 +3717,7 @@ make_sort_from_groupcols(PlannerInfo *root, int numsortkeys; AttrNumber *sortColIdx; Oid *sortOperators; + Oid *collations; bool *nullsFirst; /* @@ -3694,6 +3726,7 @@ make_sort_from_groupcols(PlannerInfo *root, numsortkeys = list_length(groupcls); sortColIdx = (AttrNumber *) palloc(numsortkeys * sizeof(AttrNumber)); sortOperators = (Oid *) palloc(numsortkeys * sizeof(Oid)); + collations = (Oid *) palloc(numsortkeys * sizeof(Oid)); nullsFirst = (bool *) palloc(numsortkeys * sizeof(bool)); numsortkeys = 0; @@ -3709,16 +3742,17 @@ make_sort_from_groupcols(PlannerInfo *root, * redundantly. */ numsortkeys = add_sort_column(tle->resno, grpcl->sortop, + exprCollation((Node *) tle->expr), grpcl->nulls_first, numsortkeys, - sortColIdx, sortOperators, nullsFirst); + sortColIdx, sortOperators, collations, nullsFirst); grpno++; } Assert(numsortkeys > 0); return make_sort(root, lefttree, numsortkeys, - sortColIdx, sortOperators, nullsFirst, -1.0); + sortColIdx, sortOperators, collations, nullsFirst, -1.0); } static Material * diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c index dfbc624aa8..f885385296 100644 --- a/src/backend/optimizer/plan/planagg.c +++ b/src/backend/optimizer/plan/planagg.c @@ -561,7 +561,8 @@ make_agg_subplan(PlannerInfo *root, RelOptInfo *rel, PrivateMMAggInfo *info) */ info->param = SS_make_initplan_from_plan(&subroot, plan, exprType((Node *) tle->expr), - -1); + -1, + exprCollation((Node *) tle->expr)); /* * Put the updated list of InitPlans back into the outer PlannerInfo. diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index b52eebe2aa..01b90383cf 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -166,6 +166,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) glob->relationOids = NIL; glob->invalItems = NIL; glob->lastPHId = 0; + glob->lastRowMarkId = 0; glob->transientPlan = false; /* Determine what fraction of the plan is likely to be scanned */ @@ -1885,6 +1886,7 @@ preprocess_rowmarks(PlannerInfo *root) newrc = makeNode(PlanRowMark); newrc->rti = newrc->prti = rc->rti; + newrc->rowmarkId = ++(root->glob->lastRowMarkId); if (rc->forUpdate) newrc->markType = ROW_MARK_EXCLUSIVE; else @@ -1910,6 +1912,7 @@ preprocess_rowmarks(PlannerInfo *root) newrc = makeNode(PlanRowMark); newrc->rti = newrc->prti = i; + newrc->rowmarkId = ++(root->glob->lastRowMarkId); /* real tables support REFERENCE, anything else needs COPY */ if (rte->rtekind == RTE_RELATION) newrc->markType = ROW_MARK_REFERENCE; diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index ef2833aa2c..5830b0be41 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -213,9 +213,11 @@ set_plan_references(PlannerGlobal *glob, Plan *plan, newrte->funcexpr = NULL; newrte->funccoltypes = NIL; newrte->funccoltypmods = NIL; + newrte->funccolcollations = NIL; newrte->values_lists = NIL; newrte->ctecoltypes = NIL; newrte->ctecoltypmods = NIL; + newrte->ctecolcollations = NIL; glob->finalrtable = lappend(glob->finalrtable, newrte); @@ -250,7 +252,7 @@ set_plan_references(PlannerGlobal *glob, Plan *plan, newrc = (PlanRowMark *) palloc(sizeof(PlanRowMark)); memcpy(newrc, rc, sizeof(PlanRowMark)); - /* adjust indexes */ + /* adjust indexes ... but *not* the rowmarkId */ newrc->rti += rtoffset; newrc->prti += rtoffset; @@ -1130,6 +1132,7 @@ set_dummy_tlist_references(Plan *plan, int rtoffset) tle->resno, exprType((Node *) oldvar), exprTypmod((Node *) oldvar), + exprCollation((Node *) oldvar), 0); if (IsA(oldvar, Var)) { diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 54846daa1f..96a257f6af 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -157,6 +157,7 @@ replace_outer_var(PlannerInfo *root, Var *var) retval->paramid = i; retval->paramtype = var->vartype; retval->paramtypmod = var->vartypmod; + retval->paramcollation = var->varcollid; retval->location = -1; return retval; @@ -185,6 +186,7 @@ assign_nestloop_param(PlannerInfo *root, Var *var) retval->paramid = i; retval->paramtype = var->vartype; retval->paramtypmod = var->vartypmod; + retval->paramcollation = var->varcollid; retval->location = -1; return retval; @@ -225,6 +227,7 @@ replace_outer_agg(PlannerInfo *root, Aggref *agg) retval->paramid = i; retval->paramtype = agg->aggtype; retval->paramtypmod = -1; + retval->paramcollation = agg->collid; retval->location = -1; return retval; @@ -236,7 +239,7 @@ replace_outer_agg(PlannerInfo *root, Aggref *agg) * This is used to allocate PARAM_EXEC slots for subplan outputs. */ static Param * -generate_new_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod) +generate_new_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod, Oid paramcollation) { Param *retval; PlannerParamItem *pitem; @@ -246,6 +249,7 @@ generate_new_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod) retval->paramid = list_length(root->glob->paramlist); retval->paramtype = paramtype; retval->paramtypmod = paramtypmod; + retval->paramcollation = paramcollation; retval->location = -1; pitem = makeNode(PlannerParamItem); @@ -270,7 +274,7 @@ SS_assign_special_param(PlannerInfo *root) Param *param; /* We generate a Param of datatype INTERNAL */ - param = generate_new_param(root, INTERNALOID, -1); + param = generate_new_param(root, INTERNALOID, -1, InvalidOid); /* ... but the caller only cares about its ID */ return param->paramid; } @@ -278,13 +282,13 @@ SS_assign_special_param(PlannerInfo *root) /* * Get the datatype of the first column of the plan's output. * - * This is stored for ARRAY_SUBLINK execution and for exprType()/exprTypmod(), + * This is stored for ARRAY_SUBLINK execution and for exprType()/exprTypmod()/exprCollation(), * which have no way to get at the plan associated with a SubPlan node. * We really only need the info for EXPR_SUBLINK and ARRAY_SUBLINK subplans, * but for consistency we save it always. */ static void -get_first_col_type(Plan *plan, Oid *coltype, int32 *coltypmod) +get_first_col_type(Plan *plan, Oid *coltype, int32 *coltypmod, Oid *colcollation) { /* In cases such as EXISTS, tlist might be empty; arbitrarily use VOID */ if (plan->targetlist) @@ -296,11 +300,13 @@ get_first_col_type(Plan *plan, Oid *coltype, int32 *coltypmod) { *coltype = exprType((Node *) tent->expr); *coltypmod = exprTypmod((Node *) tent->expr); + *colcollation = exprCollation((Node *) tent->expr); return; } } *coltype = VOIDOID; *coltypmod = -1; + *colcollation = InvalidOid; } /* @@ -470,7 +476,7 @@ build_subplan(PlannerInfo *root, Plan *plan, List *rtable, List *rowmarks, splan->subLinkType = subLinkType; splan->testexpr = NULL; splan->paramIds = NIL; - get_first_col_type(plan, &splan->firstColType, &splan->firstColTypmod); + get_first_col_type(plan, &splan->firstColType, &splan->firstColTypmod, &splan->firstColCollation); splan->useHashTable = false; splan->unknownEqFalse = unknownEqFalse; splan->setParam = NIL; @@ -523,7 +529,7 @@ build_subplan(PlannerInfo *root, Plan *plan, List *rtable, List *rowmarks, Param *prm; Assert(testexpr == NULL); - prm = generate_new_param(root, BOOLOID, -1); + prm = generate_new_param(root, BOOLOID, -1, InvalidOid); splan->setParam = list_make1_int(prm->paramid); isInitPlan = true; result = (Node *) prm; @@ -537,7 +543,8 @@ build_subplan(PlannerInfo *root, Plan *plan, List *rtable, List *rowmarks, Assert(testexpr == NULL); prm = generate_new_param(root, exprType((Node *) te->expr), - exprTypmod((Node *) te->expr)); + exprTypmod((Node *) te->expr), + exprCollation((Node *) te->expr)); splan->setParam = list_make1_int(prm->paramid); isInitPlan = true; result = (Node *) prm; @@ -556,7 +563,8 @@ build_subplan(PlannerInfo *root, Plan *plan, List *rtable, List *rowmarks, format_type_be(exprType((Node *) te->expr))); prm = generate_new_param(root, arraytype, - exprTypmod((Node *) te->expr)); + exprTypmod((Node *) te->expr), + exprCollation((Node *) te->expr)); splan->setParam = list_make1_int(prm->paramid); isInitPlan = true; result = (Node *) prm; @@ -708,7 +716,8 @@ generate_subquery_params(PlannerInfo *root, List *tlist, List **paramIds) param = generate_new_param(root, exprType((Node *) tent->expr), - exprTypmod((Node *) tent->expr)); + exprTypmod((Node *) tent->expr), + exprCollation((Node *) tent->expr)); result = lappend(result, param); ids = lappend_int(ids, param->paramid); } @@ -964,7 +973,7 @@ SS_process_ctes(PlannerInfo *root) splan->subLinkType = CTE_SUBLINK; splan->testexpr = NULL; splan->paramIds = NIL; - get_first_col_type(plan, &splan->firstColType, &splan->firstColTypmod); + get_first_col_type(plan, &splan->firstColType, &splan->firstColTypmod, &splan->firstColCollation); splan->useHashTable = false; splan->unknownEqFalse = false; splan->setParam = NIL; @@ -999,7 +1008,7 @@ SS_process_ctes(PlannerInfo *root) * Assign a param to represent the query output. We only really care * about reserving a parameter ID number. */ - prm = generate_new_param(root, INTERNALOID, -1); + prm = generate_new_param(root, INTERNALOID, -1, InvalidOid); splan->setParam = list_make1_int(prm->paramid); /* @@ -1565,7 +1574,8 @@ convert_EXISTS_to_ANY(PlannerInfo *root, Query *subselect, oc = lnext(oc); param = generate_new_param(root, exprType(rightarg), - exprTypmod(rightarg)); + exprTypmod(rightarg), + exprCollation(rightarg)); tlist = lappend(tlist, makeTargetEntry((Expr *) rightarg, resno++, @@ -2356,7 +2366,7 @@ finalize_primnode(Node *node, finalize_primnode_context *context) */ Param * SS_make_initplan_from_plan(PlannerInfo *root, Plan *plan, - Oid resulttype, int32 resulttypmod) + Oid resulttype, int32 resulttypmod, Oid resultcollation) { SubPlan *node; Param *prm; @@ -2392,7 +2402,7 @@ SS_make_initplan_from_plan(PlannerInfo *root, Plan *plan, */ node = makeNode(SubPlan); node->subLinkType = EXPR_SUBLINK; - get_first_col_type(plan, &node->firstColType, &node->firstColTypmod); + get_first_col_type(plan, &node->firstColType, &node->firstColTypmod, &node->firstColCollation); node->plan_id = list_length(root->glob->subplans); root->init_plans = lappend(root->init_plans, node); @@ -2407,7 +2417,7 @@ SS_make_initplan_from_plan(PlannerInfo *root, Plan *plan, /* * Make a Param that will be the subplan's output. */ - prm = generate_new_param(root, resulttype, resulttypmod); + prm = generate_new_param(root, resulttype, resulttypmod, resultcollation); node->setParam = list_make1_int(prm->paramid); /* Label the subplan for EXPLAIN purposes */ diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index f92bcd41b1..bd678ac7ed 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -445,6 +445,7 @@ inline_set_returning_functions(PlannerInfo *root) rte->funcexpr = NULL; rte->funccoltypes = NIL; rte->funccoltypmods = NIL; + rte->funccolcollations = NIL; } } } diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c index 36c19438c0..63447aa536 100644 --- a/src/backend/optimizer/prep/preptlist.c +++ b/src/backend/optimizer/prep/preptlist.c @@ -100,8 +100,9 @@ preprocess_targetlist(PlannerInfo *root, List *tlist) SelfItemPointerAttributeNumber, TIDOID, -1, + InvalidOid, 0); - snprintf(resname, sizeof(resname), "ctid%u", rc->rti); + snprintf(resname, sizeof(resname), "ctid%u", rc->rowmarkId); tle = makeTargetEntry((Expr *) var, list_length(tlist) + 1, pstrdup(resname), @@ -115,8 +116,9 @@ preprocess_targetlist(PlannerInfo *root, List *tlist) TableOidAttributeNumber, OIDOID, -1, + InvalidOid, 0); - snprintf(resname, sizeof(resname), "tableoid%u", rc->rti); + snprintf(resname, sizeof(resname), "tableoid%u", rc->rowmarkId); tle = makeTargetEntry((Expr *) var, list_length(tlist) + 1, pstrdup(resname), @@ -130,7 +132,7 @@ preprocess_targetlist(PlannerInfo *root, List *tlist) var = makeWholeRowVar(rt_fetch(rc->rti, range_table), rc->rti, 0); - snprintf(resname, sizeof(resname), "wholerow%u", rc->rti); + snprintf(resname, sizeof(resname), "wholerow%u", rc->rowmarkId); tle = makeTargetEntry((Expr *) var, list_length(tlist) + 1, pstrdup(resname), @@ -257,6 +259,7 @@ expand_targetlist(List *tlist, int command_type, */ Oid atttype = att_tup->atttypid; int32 atttypmod = att_tup->atttypmod; + Oid attcollation = att_tup->attcollation; Node *new_expr; switch (command_type) @@ -296,6 +299,7 @@ expand_targetlist(List *tlist, int command_type, attrno, atttype, atttypmod, + attcollation, 0); } else diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 449c8dab50..606e5fe60a 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -86,7 +86,7 @@ static List *generate_setop_tlist(List *colTypes, int flag, bool hack_constants, List *input_tlist, List *refnames_tlist); -static List *generate_append_tlist(List *colTypes, bool flag, +static List *generate_append_tlist(List *colTypes, List *colCollations, bool flag, List *input_plans, List *refnames_tlist); static List *generate_setop_grouplist(SetOperationStmt *op, List *targetlist); @@ -348,7 +348,7 @@ generate_recursion_plan(SetOperationStmt *setOp, PlannerInfo *root, /* * Generate tlist for RecursiveUnion plan node --- same as in Append cases */ - tlist = generate_append_tlist(setOp->colTypes, false, + tlist = generate_append_tlist(setOp->colTypes, setOp->colCollations, false, list_make2(lplan, rplan), refnames_tlist); @@ -443,7 +443,7 @@ generate_union_plan(SetOperationStmt *op, PlannerInfo *root, * concerned, but we must make it look real anyway for the benefit of the * next plan level up. */ - tlist = generate_append_tlist(op->colTypes, false, + tlist = generate_append_tlist(op->colTypes, op->colCollations, false, planlist, refnames_tlist); /* @@ -534,7 +534,7 @@ generate_nonunion_plan(SetOperationStmt *op, PlannerInfo *root, * column is shown as a variable not a constant, else setrefs.c will get * confused. */ - tlist = generate_append_tlist(op->colTypes, true, + tlist = generate_append_tlist(op->colTypes, op->colCollations, true, planlist, refnames_tlist); /* @@ -885,6 +885,7 @@ generate_setop_tlist(List *colTypes, int flag, inputtle->resno, exprType((Node *) inputtle->expr), exprTypmod((Node *) inputtle->expr), + exprCollation((Node *) inputtle->expr), 0); if (exprType(expr) != colType) { @@ -936,13 +937,14 @@ generate_setop_tlist(List *colTypes, int flag, * The Vars are always generated with varno 0. */ static List * -generate_append_tlist(List *colTypes, bool flag, +generate_append_tlist(List *colTypes, List*colCollations, bool flag, List *input_plans, List *refnames_tlist) { List *tlist = NIL; int resno = 1; ListCell *curColType; + ListCell *curColCollation; ListCell *ref_tl_item; int colindex; TargetEntry *tle; @@ -997,10 +999,11 @@ generate_append_tlist(List *colTypes, bool flag, * Now we can build the tlist for the Append. */ colindex = 0; - forboth(curColType, colTypes, ref_tl_item, refnames_tlist) + forthree(curColType, colTypes, curColCollation, colCollations, ref_tl_item, refnames_tlist) { Oid colType = lfirst_oid(curColType); int32 colTypmod = colTypmods[colindex++]; + Oid colColl = lfirst_oid(curColCollation); TargetEntry *reftle = (TargetEntry *) lfirst(ref_tl_item); Assert(reftle->resno == resno); @@ -1009,6 +1012,7 @@ generate_append_tlist(List *colTypes, bool flag, resno, colType, colTypmod, + colColl, 0); tle = makeTargetEntry((Expr *) expr, (AttrNumber) resno++, @@ -1025,6 +1029,7 @@ generate_append_tlist(List *colTypes, bool flag, resno, INT4OID, -1, + InvalidOid, 0); tle = makeTargetEntry((Expr *) expr, (AttrNumber) resno++, @@ -1289,6 +1294,7 @@ expand_inherited_rtentry(PlannerInfo *root, RangeTblEntry *rte, Index rti) newrc->rti = childRTindex; newrc->prti = rti; + newrc->rowmarkId = oldrc->rowmarkId; newrc->markType = oldrc->markType; newrc->noWait = oldrc->noWait; newrc->isParent = false; @@ -1344,6 +1350,7 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation, char *attname; Oid atttypid; int32 atttypmod; + Oid attcollation; int new_attno; att = old_tupdesc->attrs[old_attno]; @@ -1356,6 +1363,7 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation, attname = NameStr(att->attname); atttypid = att->atttypid; atttypmod = att->atttypmod; + attcollation = att->attcollation; /* * When we are generating the "translation list" for the parent table @@ -1367,6 +1375,7 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation, (AttrNumber) (old_attno + 1), atttypid, atttypmod, + attcollation, 0)); continue; } @@ -1409,6 +1418,7 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation, (AttrNumber) (new_attno + 1), atttypid, atttypmod, + attcollation, 0)); } diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 86990c8725..fa0952618b 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -100,7 +100,7 @@ static List *simplify_and_arguments(List *args, bool *haveNull, bool *forceFalse); static Node *simplify_boolean_equality(Oid opno, List *args); static Expr *simplify_function(Oid funcid, - Oid result_type, int32 result_typmod, List **args, + Oid result_type, int32 result_typmod, Oid collid, List **args, bool has_named_args, bool allow_inline, eval_const_expressions_context *context); @@ -114,7 +114,7 @@ static List *fetch_function_defaults(HeapTuple func_tuple); static void recheck_cast_function_args(List *args, Oid result_type, HeapTuple func_tuple); static Expr *evaluate_function(Oid funcid, - Oid result_type, int32 result_typmod, List *args, + Oid result_type, int32 result_typmod, Oid collid, List *args, HeapTuple func_tuple, eval_const_expressions_context *context); static Expr *inline_function(Oid funcid, Oid result_type, List *args, @@ -156,6 +156,7 @@ make_opclause(Oid opno, Oid opresulttype, bool opretset, expr->args = list_make2(leftop, rightop); else expr->args = list_make1(leftop); + expr->collid = select_common_collation(NULL, expr->args, false); expr->location = -1; return (Expr *) expr; } @@ -1973,7 +1974,7 @@ set_coercionform_dontcare_walker(Node *node, void *context) */ static bool rowtype_field_matches(Oid rowtypeid, int fieldnum, - Oid expectedtype, int32 expectedtypmod) + Oid expectedtype, int32 expectedtypmod, Oid expectedcollation) { TupleDesc tupdesc; Form_pg_attribute attr; @@ -1990,7 +1991,8 @@ rowtype_field_matches(Oid rowtypeid, int fieldnum, attr = tupdesc->attrs[fieldnum - 1]; if (attr->attisdropped || attr->atttypid != expectedtype || - attr->atttypmod != expectedtypmod) + attr->atttypmod != expectedtypmod || + attr->attcollation != expectedcollation) { ReleaseTupleDesc(tupdesc); return false; @@ -2121,6 +2123,7 @@ eval_const_expressions_mutator(Node *node, int16 typLen; bool typByVal; Datum pval; + Const *cnst; Assert(prm->ptype == param->paramtype); get_typlenbyval(param->paramtype, &typLen, &typByVal); @@ -2128,12 +2131,14 @@ eval_const_expressions_mutator(Node *node, pval = prm->value; else pval = datumCopy(prm->value, typByVal, typLen); - return (Node *) makeConst(param->paramtype, + cnst = makeConst(param->paramtype, param->paramtypmod, (int) typLen, pval, prm->isnull, typByVal); + cnst->constcollid = param->paramcollation; + return (Node *) cnst; } } } @@ -2173,6 +2178,7 @@ eval_const_expressions_mutator(Node *node, */ simple = simplify_function(expr->funcid, expr->funcresulttype, exprTypmod(node), + expr->collid, &args, has_named_args, true, context); if (simple) /* successfully simplified it */ @@ -2190,6 +2196,7 @@ eval_const_expressions_mutator(Node *node, newexpr->funcretset = expr->funcretset; newexpr->funcformat = expr->funcformat; newexpr->args = args; + newexpr->collid = expr->collid; newexpr->location = expr->location; return (Node *) newexpr; } @@ -2221,6 +2228,7 @@ eval_const_expressions_mutator(Node *node, */ simple = simplify_function(expr->opfuncid, expr->opresulttype, -1, + expr->collid, &args, false, true, context); if (simple) /* successfully simplified it */ @@ -2250,6 +2258,7 @@ eval_const_expressions_mutator(Node *node, newexpr->opresulttype = expr->opresulttype; newexpr->opretset = expr->opretset; newexpr->args = args; + newexpr->collid = expr->collid; newexpr->location = expr->location; return (Node *) newexpr; } @@ -2314,6 +2323,7 @@ eval_const_expressions_mutator(Node *node, */ simple = simplify_function(expr->opfuncid, expr->opresulttype, -1, + expr->collid, &args, false, false, context); if (simple) /* successfully simplified it */ @@ -2342,6 +2352,7 @@ eval_const_expressions_mutator(Node *node, newexpr->opresulttype = expr->opresulttype; newexpr->opretset = expr->opretset; newexpr->args = args; + newexpr->collid = expr->collid; newexpr->location = expr->location; return (Node *) newexpr; } @@ -2493,7 +2504,7 @@ eval_const_expressions_mutator(Node *node, getTypeInputInfo(expr->resulttype, &infunc, &intypioparam); simple = simplify_function(outfunc, - CSTRINGOID, -1, + CSTRINGOID, -1, InvalidOid, &args, false, true, context); if (simple) /* successfully simplified output fn */ @@ -2510,8 +2521,11 @@ eval_const_expressions_mutator(Node *node, Int32GetDatum(-1), false, true)); + /* preserve collation of input expression */ simple = simplify_function(infunc, - expr->resulttype, -1, + expr->resulttype, + -1, + exprCollation((Node *) arg), &args, false, true, context); if (simple) /* successfully simplified input fn */ @@ -2690,6 +2704,7 @@ eval_const_expressions_mutator(Node *node, /* Otherwise we need a new CASE node */ newcase = makeNode(CaseExpr); newcase->casetype = caseexpr->casetype; + newcase->casecollation = caseexpr->casecollation; newcase->arg = (Expr *) newarg; newcase->args = newargs; newcase->defresult = (Expr *) defresult; @@ -2782,6 +2797,7 @@ eval_const_expressions_mutator(Node *node, newcoalesce = makeNode(CoalesceExpr); newcoalesce->coalescetype = coalesceexpr->coalescetype; + newcoalesce->coalescecollation = coalesceexpr->coalescecollation; newcoalesce->args = newargs; newcoalesce->location = coalesceexpr->location; return (Node *) newcoalesce; @@ -2811,11 +2827,13 @@ eval_const_expressions_mutator(Node *node, if (rowtype_field_matches(((Var *) arg)->vartype, fselect->fieldnum, fselect->resulttype, - fselect->resulttypmod)) + fselect->resulttypmod, + fselect->resultcollation)) return (Node *) makeVar(((Var *) arg)->varno, fselect->fieldnum, fselect->resulttype, fselect->resulttypmod, + fselect->resultcollation, ((Var *) arg)->varlevelsup); } if (arg && IsA(arg, RowExpr)) @@ -2831,9 +2849,11 @@ eval_const_expressions_mutator(Node *node, if (rowtype_field_matches(rowexpr->row_typeid, fselect->fieldnum, fselect->resulttype, - fselect->resulttypmod) && + fselect->resulttypmod, + fselect->resultcollation) && fselect->resulttype == exprType(fld) && - fselect->resulttypmod == exprTypmod(fld)) + fselect->resulttypmod == exprTypmod(fld) && + fselect->resultcollation == exprCollation(fld)) return fld; } } @@ -2842,6 +2862,7 @@ eval_const_expressions_mutator(Node *node, newfselect->fieldnum = fselect->fieldnum; newfselect->resulttype = fselect->resulttype; newfselect->resulttypmod = fselect->resulttypmod; + newfselect->resultcollation = fselect->resultcollation; return (Node *) newfselect; } if (IsA(node, NullTest)) @@ -3299,7 +3320,7 @@ simplify_boolean_equality(Oid opno, List *args) * pass-by-reference, and it may get modified even if simplification fails. */ static Expr * -simplify_function(Oid funcid, Oid result_type, int32 result_typmod, +simplify_function(Oid funcid, Oid result_type, int32 result_typmod, Oid collid, List **args, bool has_named_args, bool allow_inline, @@ -3330,7 +3351,7 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod, else if (((Form_pg_proc) GETSTRUCT(func_tuple))->pronargs > list_length(*args)) *args = add_function_defaults(*args, result_type, func_tuple, context); - newexpr = evaluate_function(funcid, result_type, result_typmod, *args, + newexpr = evaluate_function(funcid, result_type, result_typmod, collid, *args, func_tuple, context); if (!newexpr && allow_inline) @@ -3581,7 +3602,8 @@ recheck_cast_function_args(List *args, Oid result_type, HeapTuple func_tuple) * simplify the function. */ static Expr * -evaluate_function(Oid funcid, Oid result_type, int32 result_typmod, List *args, +evaluate_function(Oid funcid, Oid result_type, int32 result_typmod, Oid collid, + List *args, HeapTuple func_tuple, eval_const_expressions_context *context) { @@ -3664,6 +3686,7 @@ evaluate_function(Oid funcid, Oid result_type, int32 result_typmod, List *args, newexpr->funcretset = false; newexpr->funcformat = COERCE_DONTCARE; /* doesn't matter */ newexpr->args = args; + newexpr->collid = collid; newexpr->location = -1; return evaluate_expr((Expr *) newexpr, result_type, result_typmod); diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index f86bc5fab2..408eff68d8 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -192,10 +192,12 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, info->indexkeys = (int *) palloc(sizeof(int) * ncolumns); info->opfamily = (Oid *) palloc(sizeof(Oid) * ncolumns); info->opcintype = (Oid *) palloc(sizeof(Oid) * ncolumns); + info->indexcollations = (Oid *) palloc(sizeof(Oid) * ncolumns); for (i = 0; i < ncolumns; i++) { info->indexkeys[i] = index->indkey.values[i]; + info->indexcollations[i] = indexRelation->rd_indcollation[i]; info->opfamily[i] = indexRelation->rd_opfamily[i]; info->opcintype[i] = indexRelation->rd_opcintype[i]; } @@ -308,6 +310,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, ChangeVarNodes((Node *) info->indpred, 1, varno, 0); info->predOK = false; /* set later in indxpath.c */ info->unique = index->indisunique; + info->hypothetical = false; /* * Estimate the index size. If it's not a partial index, we lock @@ -640,6 +643,7 @@ get_relation_constraints(PlannerInfo *root, i, att->atttypid, att->atttypmod, + att->attcollation, 0); ntest->nulltesttype = IS_NOT_NULL; ntest->argisrow = type_is_rowtype(att->atttypid); @@ -804,6 +808,7 @@ build_physical_tlist(PlannerInfo *root, RelOptInfo *rel) attrno, att_tup->atttypid, att_tup->atttypmod, + att_tup->attcollation, 0); tlist = lappend(tlist, diff --git a/src/backend/optimizer/util/predtest.c b/src/backend/optimizer/util/predtest.c index 6e80b4c0c7..4561e8e92f 100644 --- a/src/backend/optimizer/util/predtest.c +++ b/src/backend/optimizer/util/predtest.c @@ -912,6 +912,7 @@ arrayconst_startup_fn(Node *clause, PredIterInfo info) state->constexpr.xpr.type = T_Const; state->constexpr.consttype = ARR_ELEMTYPE(arrayval); state->constexpr.consttypmod = -1; + state->constexpr.constcollid = arrayconst->constcollid; state->constexpr.constlen = elmlen; state->constexpr.constbyval = elmbyval; lsecond(state->opexpr.args) = &state->constexpr; diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 070e4c177a..22447f92a2 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -1203,6 +1203,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) ListCell *left_tlist, *lct, *lcm, + *lcc, *l; List *targetvars, *targetnames, @@ -1296,10 +1297,11 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) targetnames = NIL; left_tlist = list_head(leftmostQuery->targetList); - forboth(lct, sostmt->colTypes, lcm, sostmt->colTypmods) + forthree(lct, sostmt->colTypes, lcm, sostmt->colTypmods, lcc, sostmt->colCollations) { Oid colType = lfirst_oid(lct); int32 colTypmod = lfirst_int(lcm); + Oid colCollation = lfirst_oid(lcc); TargetEntry *lefttle = (TargetEntry *) lfirst(left_tlist); char *colName; TargetEntry *tle; @@ -1311,6 +1313,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) lefttle->resno, colType, colTypmod, + colCollation, 0); var->location = exprLocation((Node *) lefttle->expr); tle = makeTargetEntry((Expr *) var, @@ -1418,7 +1421,7 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) * Recursively transform leaves and internal nodes of a set-op tree * * In addition to returning the transformed node, we return a list of - * expression nodes showing the type, typmod, and location (for error messages) + * expression nodes showing the type, typmod, collation, and location (for error messages) * of each output column of the set-op node. This is used only during the * internal recursion of this function. At the upper levels we use * SetToDefault nodes for this purpose, since they carry exactly the fields @@ -1591,6 +1594,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, *colInfo = NIL; op->colTypes = NIL; op->colTypmods = NIL; + op->colCollations = NIL; op->groupClauses = NIL; forboth(lci, lcolinfo, rci, rcolinfo) { @@ -1604,6 +1608,7 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, SetToDefault *rescolnode; Oid rescoltype; int32 rescoltypmod; + Oid rescolcoll; /* select common type, same as CASE et al */ rescoltype = select_common_type(pstate, @@ -1615,6 +1620,12 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, rescoltypmod = lcoltypmod; else rescoltypmod = -1; + /* Select common collation. A common collation is + * required for all set operators except UNION ALL; see + * SQL:2008-2 7.13 SR 15c. */ + rescolcoll = select_common_collation(pstate, + list_make2(lcolnode, rcolnode), + (op->op == SETOP_UNION && op->all)); /* * Verify the coercions are actually possible. If not, we'd fail @@ -1643,11 +1654,13 @@ transformSetOperationTree(ParseState *pstate, SelectStmt *stmt, rescolnode = makeNode(SetToDefault); rescolnode->typeId = rescoltype; rescolnode->typeMod = rescoltypmod; + rescolnode->collid = rescolcoll; rescolnode->location = exprLocation(bestexpr); *colInfo = lappend(*colInfo, rescolnode); op->colTypes = lappend_oid(op->colTypes, rescoltype); op->colTypmods = lappend_int(op->colTypmods, rescoltypmod); + op->colCollations = lappend_oid(op->colCollations, rescolcoll); /* * For all cases except UNION ALL, identify the grouping operators diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 9e0c148ad4..8703e77b31 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -185,13 +185,13 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_ AlterDatabaseStmt AlterDatabaseSetStmt AlterDomainStmt AlterEnumStmt AlterFdwStmt AlterForeignServerStmt AlterGroupStmt AlterObjectSchemaStmt AlterOwnerStmt AlterSeqStmt AlterTableStmt - AlterForeignTableStmt + AlterExtensionStmt AlterExtensionContentsStmt AlterForeignTableStmt AlterCompositeTypeStmt AlterUserStmt AlterUserMappingStmt AlterUserSetStmt AlterRoleStmt AlterRoleSetStmt AlterDefaultPrivilegesStmt DefACLAction AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt - CreateDomainStmt CreateGroupStmt CreateOpClassStmt + CreateDomainStmt CreateExtensionStmt CreateGroupStmt CreateOpClassStmt CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt CreateSchemaStmt CreateSeqStmt CreateStmt CreateTableSpaceStmt CreateFdwStmt CreateForeignServerStmt CreateForeignTableStmt @@ -228,8 +228,10 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_ %type <list> createdb_opt_list alterdb_opt_list copy_opt_list transaction_mode_list + create_extension_opt_list alter_extension_opt_list %type <defelt> createdb_opt_item alterdb_opt_item copy_opt_item transaction_mode_item + create_extension_opt_item alter_extension_opt_item %type <ival> opt_lock lock_type cast_context %type <ival> vacuum_option_list vacuum_option_elem @@ -261,6 +263,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_ %type <list> func_name handler_name qual_Op qual_all_Op subquery_Op opt_class opt_inline_handler opt_validator validator_clause + opt_collate %type <range> qualified_name OptConstrFromTable @@ -397,7 +400,8 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_ %type <list> copy_generic_opt_list copy_generic_opt_arg_list %type <list> copy_options -%type <typnam> Typename SimpleTypename ConstTypename +%type <typnam> Typename SimpleTypename SimpleTypenameWithoutCollation + ConstTypename GenericType Numeric opt_float Character ConstCharacter CharacterWithLength CharacterWithoutLength @@ -481,7 +485,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_ CACHE CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE - CLUSTER COALESCE COLLATE COLUMN COMMENT COMMENTS COMMIT + CLUSTER COALESCE COLLATE COLLATION COLUMN COMMENT COMMENTS COMMIT COMMITTED CONCURRENTLY CONFIGURATION CONNECTION CONSTRAINT CONSTRAINTS CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE CREATEDB CREATEROLE CREATEUSER CROSS CSV CURRENT_P @@ -493,7 +497,8 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_ DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P DOUBLE_P DROP EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EXCEPT - EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTERNAL EXTRACT + EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN + EXTENSION EXTERNAL EXTRACT FALSE_P FAMILY FETCH FIRST_P FLOAT_P FOLLOWING FOR FORCE FOREIGN FORWARD FREEZE FROM FULL FUNCTION FUNCTIONS @@ -549,7 +554,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_ UNBOUNDED UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNLOGGED UNTIL UPDATE USER USING - VACUUM VALID VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING + VACUUM VALID VALIDATE VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING VERBOSE VERSION_P VIEW VOLATILE WHEN WHERE WHITESPACE_P WINDOW WITH WITHOUT WORK WRAPPER WRITE @@ -664,6 +669,8 @@ stmt : | AlterDefaultPrivilegesStmt | AlterDomainStmt | AlterEnumStmt + | AlterExtensionStmt + | AlterExtensionContentsStmt | AlterFdwStmt | AlterForeignServerStmt | AlterForeignTableStmt @@ -693,6 +700,7 @@ stmt : | CreateCastStmt | CreateConversionStmt | CreateDomainStmt + | CreateExtensionStmt | CreateFdwStmt | CreateForeignServerStmt | CreateForeignTableStmt @@ -1755,6 +1763,14 @@ alter_table_cmd: n->def = $2; $$ = (Node *)n; } + /* ALTER TABLE <name> VALIDATE CONSTRAINT ... */ + | VALIDATE CONSTRAINT name + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_ValidateConstraint; + n->name = $3; + $$ = (Node *)n; + } /* ALTER TABLE <name> DROP CONSTRAINT IF EXISTS <name> [RESTRICT|CASCADE] */ | DROP CONSTRAINT IF_P EXISTS name opt_drop_behavior { @@ -2746,9 +2762,25 @@ ConstraintElem: n->fk_matchtype = $9; n->fk_upd_action = (char) ($10 >> 8); n->fk_del_action = (char) ($10 & 0xFF); - n->skip_validation = FALSE; n->deferrable = ($11 & 1) != 0; n->initdeferred = ($11 & 2) != 0; + n->skip_validation = false; + $$ = (Node *)n; + } + | FOREIGN KEY '(' columnList ')' REFERENCES qualified_name + opt_column_list key_match key_actions + NOT VALID + { + Constraint *n = makeNode(Constraint); + n->contype = CONSTR_FOREIGN; + n->location = @1; + n->pktable = $7; + n->fk_attrs = $4; + n->pk_attrs = $8; + n->fk_matchtype = $9; + n->fk_upd_action = (char) ($10 >> 8); + n->fk_del_action = (char) ($10 & 0xFF); + n->skip_validation = true; $$ = (Node *)n; } ; @@ -3195,6 +3227,287 @@ DropTableSpaceStmt: DROP TABLESPACE name /***************************************************************************** * * QUERY: + * CREATE EXTENSION extension + * [ WITH ] [ SCHEMA schema ] [ VERSION version ] [ FROM oldversion ] + * + *****************************************************************************/ + +CreateExtensionStmt: CREATE EXTENSION name opt_with create_extension_opt_list + { + CreateExtensionStmt *n = makeNode(CreateExtensionStmt); + n->extname = $3; + n->options = $5; + $$ = (Node *) n; + } + ; + +create_extension_opt_list: + create_extension_opt_list create_extension_opt_item + { $$ = lappend($1, $2); } + | /* EMPTY */ + { $$ = NIL; } + ; + +create_extension_opt_item: + SCHEMA name + { + $$ = makeDefElem("schema", (Node *)makeString($2)); + } + | VERSION_P ColId_or_Sconst + { + $$ = makeDefElem("new_version", (Node *)makeString($2)); + } + | FROM ColId_or_Sconst + { + $$ = makeDefElem("old_version", (Node *)makeString($2)); + } + ; + +/***************************************************************************** + * + * ALTER EXTENSION name UPDATE [ TO version ] + * + *****************************************************************************/ + +AlterExtensionStmt: ALTER EXTENSION name UPDATE alter_extension_opt_list + { + AlterExtensionStmt *n = makeNode(AlterExtensionStmt); + n->extname = $3; + n->options = $5; + $$ = (Node *) n; + } + ; + +alter_extension_opt_list: + alter_extension_opt_list alter_extension_opt_item + { $$ = lappend($1, $2); } + | /* EMPTY */ + { $$ = NIL; } + ; + +alter_extension_opt_item: + TO ColId_or_Sconst + { + $$ = makeDefElem("new_version", (Node *)makeString($2)); + } + ; + +/***************************************************************************** + * + * ALTER EXTENSION name ADD/DROP object-identifier + * + *****************************************************************************/ + +AlterExtensionContentsStmt: + ALTER EXTENSION name add_drop AGGREGATE func_name aggr_args + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_AGGREGATE; + n->objname = $6; + n->objargs = $7; + $$ = (Node *)n; + } + | ALTER EXTENSION name add_drop CAST '(' Typename AS Typename ')' + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_CAST; + n->objname = list_make1($7); + n->objargs = list_make1($9); + $$ = (Node *) n; + } + | ALTER EXTENSION name add_drop COLLATION any_name + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_COLLATION; + n->objname = $6; + $$ = (Node *)n; + } + | ALTER EXTENSION name add_drop CONVERSION_P any_name + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_CONVERSION; + n->objname = $6; + $$ = (Node *)n; + } + | ALTER EXTENSION name add_drop DOMAIN_P any_name + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_DOMAIN; + n->objname = $6; + $$ = (Node *)n; + } + | ALTER EXTENSION name add_drop FUNCTION function_with_argtypes + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_FUNCTION; + n->objname = $6->funcname; + n->objargs = $6->funcargs; + $$ = (Node *)n; + } + | ALTER EXTENSION name add_drop opt_procedural LANGUAGE name + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_LANGUAGE; + n->objname = list_make1(makeString($7)); + $$ = (Node *)n; + } + | ALTER EXTENSION name add_drop OPERATOR any_operator oper_argtypes + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_OPERATOR; + n->objname = $6; + n->objargs = $7; + $$ = (Node *)n; + } + | ALTER EXTENSION name add_drop OPERATOR CLASS any_name USING access_method + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_OPCLASS; + n->objname = $7; + n->objargs = list_make1(makeString($9)); + $$ = (Node *)n; + } + | ALTER EXTENSION name add_drop OPERATOR FAMILY any_name USING access_method + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_OPFAMILY; + n->objname = $7; + n->objargs = list_make1(makeString($9)); + $$ = (Node *)n; + } + | ALTER EXTENSION name add_drop SCHEMA name + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_SCHEMA; + n->objname = list_make1(makeString($6)); + $$ = (Node *)n; + } + | ALTER EXTENSION name add_drop TABLE any_name + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_TABLE; + n->objname = $6; + $$ = (Node *)n; + } + | ALTER EXTENSION name add_drop TEXT_P SEARCH PARSER any_name + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_TSPARSER; + n->objname = $8; + $$ = (Node *)n; + } + | ALTER EXTENSION name add_drop TEXT_P SEARCH DICTIONARY any_name + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_TSDICTIONARY; + n->objname = $8; + $$ = (Node *)n; + } + | ALTER EXTENSION name add_drop TEXT_P SEARCH TEMPLATE any_name + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_TSTEMPLATE; + n->objname = $8; + $$ = (Node *)n; + } + | ALTER EXTENSION name add_drop TEXT_P SEARCH CONFIGURATION any_name + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_TSCONFIGURATION; + n->objname = $8; + $$ = (Node *)n; + } + | ALTER EXTENSION name add_drop SEQUENCE any_name + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_SEQUENCE; + n->objname = $6; + $$ = (Node *)n; + } + | ALTER EXTENSION name add_drop VIEW any_name + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_VIEW; + n->objname = $6; + $$ = (Node *)n; + } + | ALTER EXTENSION name add_drop FOREIGN TABLE any_name + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_FOREIGN_TABLE; + n->objname = $7; + $$ = (Node *)n; + } + | ALTER EXTENSION name add_drop FOREIGN DATA_P WRAPPER name + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_FDW; + n->objname = list_make1(makeString($8)); + $$ = (Node *)n; + } + | ALTER EXTENSION name add_drop SERVER name + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_FOREIGN_SERVER; + n->objname = list_make1(makeString($6)); + $$ = (Node *)n; + } + | ALTER EXTENSION name add_drop TYPE_P any_name + { + AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt); + n->extname = $3; + n->action = $4; + n->objtype = OBJECT_TYPE; + n->objname = $6; + $$ = (Node *)n; + } + ; + +/***************************************************************************** + * + * QUERY: * CREATE FOREIGN DATA WRAPPER name [ VALIDATOR name ] * *****************************************************************************/ @@ -3956,6 +4269,24 @@ DefineStmt: n->definition = $6; $$ = (Node *)n; } + | CREATE COLLATION any_name definition + { + DefineStmt *n = makeNode(DefineStmt); + n->kind = OBJECT_COLLATION; + n->args = NIL; + n->defnames = $3; + n->definition = $4; + $$ = (Node *)n; + } + | CREATE COLLATION any_name FROM any_name + { + DefineStmt *n = makeNode(DefineStmt); + n->kind = OBJECT_COLLATION; + n->args = NIL; + n->defnames = $3; + n->definition = list_make1(makeDefElem("from", (Node *) $5)); + $$ = (Node *)n; + } ; definition: '(' def_list ')' { $$ = $2; } @@ -4326,11 +4657,13 @@ drop_type: TABLE { $$ = OBJECT_TABLE; } | SEQUENCE { $$ = OBJECT_SEQUENCE; } | VIEW { $$ = OBJECT_VIEW; } | INDEX { $$ = OBJECT_INDEX; } - | TYPE_P { $$ = OBJECT_TYPE; } | FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; } + | TYPE_P { $$ = OBJECT_TYPE; } | DOMAIN_P { $$ = OBJECT_DOMAIN; } + | COLLATION { $$ = OBJECT_COLLATION; } | CONVERSION_P { $$ = OBJECT_CONVERSION; } | SCHEMA { $$ = OBJECT_SCHEMA; } + | EXTENSION { $$ = OBJECT_EXTENSION; } | TEXT_P SEARCH PARSER { $$ = OBJECT_TSPARSER; } | TEXT_P SEARCH DICTIONARY { $$ = OBJECT_TSDICTIONARY; } | TEXT_P SEARCH TEMPLATE { $$ = OBJECT_TSTEMPLATE; } @@ -4383,8 +4716,8 @@ opt_restart_seqs: * the object associated with the comment. The form of the statement is: * * COMMENT ON [ [ DATABASE | DOMAIN | INDEX | SEQUENCE | TABLE | TYPE | VIEW | - * CONVERSION | LANGUAGE | OPERATOR CLASS | LARGE OBJECT | - * CAST | COLUMN | SCHEMA | TABLESPACE | ROLE | + * COLLATION | CONVERSION | LANGUAGE | OPERATOR CLASS | LARGE OBJECT | + * CAST | COLUMN | SCHEMA | TABLESPACE | EXTENSION | ROLE | * TEXT SEARCH PARSER | TEXT SEARCH DICTIONARY | * TEXT SEARCH TEMPLATE | TEXT SEARCH CONFIGURATION | * FOREIGN TABLE ] <objname> | @@ -4558,11 +4891,13 @@ comment_type: | INDEX { $$ = OBJECT_INDEX; } | SEQUENCE { $$ = OBJECT_SEQUENCE; } | TABLE { $$ = OBJECT_TABLE; } - | DOMAIN_P { $$ = OBJECT_TYPE; } + | DOMAIN_P { $$ = OBJECT_DOMAIN; } | TYPE_P { $$ = OBJECT_TYPE; } | VIEW { $$ = OBJECT_VIEW; } + | COLLATION { $$ = OBJECT_COLLATION; } | CONVERSION_P { $$ = OBJECT_CONVERSION; } | TABLESPACE { $$ = OBJECT_TABLESPACE; } + | EXTENSION { $$ = OBJECT_EXTENSION; } | ROLE { $$ = OBJECT_ROLE; } | FOREIGN TABLE { $$ = OBJECT_FOREIGN_TABLE; } ; @@ -5311,38 +5646,45 @@ index_params: index_elem { $$ = list_make1($1); } * expressions in parens. For backwards-compatibility reasons, we allow * an expression that's just a function call to be written without parens. */ -index_elem: ColId opt_class opt_asc_desc opt_nulls_order +index_elem: ColId opt_collate opt_class opt_asc_desc opt_nulls_order { $$ = makeNode(IndexElem); $$->name = $1; $$->expr = NULL; $$->indexcolname = NULL; - $$->opclass = $2; - $$->ordering = $3; - $$->nulls_ordering = $4; + $$->collation = $2; + $$->opclass = $3; + $$->ordering = $4; + $$->nulls_ordering = $5; } - | func_expr opt_class opt_asc_desc opt_nulls_order + | func_expr opt_collate opt_class opt_asc_desc opt_nulls_order { $$ = makeNode(IndexElem); $$->name = NULL; $$->expr = $1; $$->indexcolname = NULL; - $$->opclass = $2; - $$->ordering = $3; - $$->nulls_ordering = $4; + $$->collation = $2; + $$->opclass = $3; + $$->ordering = $4; + $$->nulls_ordering = $5; } - | '(' a_expr ')' opt_class opt_asc_desc opt_nulls_order + | '(' a_expr ')' opt_collate opt_class opt_asc_desc opt_nulls_order { $$ = makeNode(IndexElem); $$->name = NULL; $$->expr = $2; $$->indexcolname = NULL; - $$->opclass = $4; - $$->ordering = $5; - $$->nulls_ordering = $6; + $$->collation = $4; + $$->opclass = $5; + $$->ordering = $6; + $$->nulls_ordering = $7; } ; +opt_collate: COLLATE any_name { $$ = $2; } + | /*EMPTY*/ { $$ = NIL; } + ; + opt_class: any_name { $$ = $1; } | USING any_name { $$ = $2; } | /*EMPTY*/ { $$ = NIL; } @@ -5974,6 +6316,14 @@ RenameStmt: ALTER AGGREGATE func_name aggr_args RENAME TO name n->newname = $7; $$ = (Node *)n; } + | ALTER COLLATION any_name RENAME TO name + { + RenameStmt *n = makeNode(RenameStmt); + n->renameType = OBJECT_COLLATION; + n->object = $3; + n->newname = $6; + $$ = (Node *)n; + } | ALTER CONVERSION_P any_name RENAME TO name { RenameStmt *n = makeNode(RenameStmt); @@ -6234,6 +6584,14 @@ AlterObjectSchemaStmt: n->newschema = $7; $$ = (Node *)n; } + | ALTER COLLATION any_name SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_COLLATION; + n->object = $3; + n->newschema = $6; + $$ = (Node *)n; + } | ALTER CONVERSION_P any_name SET SCHEMA name { AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); @@ -6250,6 +6608,14 @@ AlterObjectSchemaStmt: n->newschema = $6; $$ = (Node *)n; } + | ALTER EXTENSION any_name SET SCHEMA name + { + AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); + n->objectType = OBJECT_EXTENSION; + n->object = $3; + n->newschema = $6; + $$ = (Node *)n; + } | ALTER FUNCTION function_with_argtypes SET SCHEMA name { AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt); @@ -6375,6 +6741,14 @@ AlterOwnerStmt: ALTER AGGREGATE func_name aggr_args OWNER TO RoleId n->newowner = $7; $$ = (Node *)n; } + | ALTER COLLATION any_name OWNER TO RoleId + { + AlterOwnerStmt *n = makeNode(AlterOwnerStmt); + n->objectType = OBJECT_COLLATION; + n->object = $3; + n->newowner = $6; + $$ = (Node *)n; + } | ALTER CONVERSION_P any_name OWNER TO RoleId { AlterOwnerStmt *n = makeNode(AlterOwnerStmt); @@ -8764,6 +9138,13 @@ opt_array_bounds: ; SimpleTypename: + SimpleTypenameWithoutCollation opt_collate + { + $$ = $1; + $$->collnames = $2; + } + +SimpleTypenameWithoutCollation: GenericType { $$ = $1; } | Numeric { $$ = $1; } | Bit { $$ = $1; } @@ -9799,6 +10180,14 @@ c_expr: columnref { $$ = $1; } r->location = @1; $$ = (Node *)r; } + | c_expr COLLATE any_name + { + CollateClause *n = makeNode(CollateClause); + n->arg = (Expr *) $1; + n->collnames = $3; + n->location = @2; + $$ = (Node *)n; + } ; /* @@ -11340,9 +11729,9 @@ ColLabel: IDENT { $$ = $1; } * shift or reduce conflicts. The earlier lists define "less reserved" * categories of keywords. * - * Make sure that each keyword's category in keywords.c matches where + * Make sure that each keyword's category in kwlist.h matches where * it is listed here. (Someday we may be able to generate these lists and - * keywords.c's table from a common master list.) + * kwlist.h's table from a common master list.) */ /* "Unreserved" keywords --- available for use as any kind of name. @@ -11426,6 +11815,7 @@ unreserved_keyword: | EXCLUSIVE | EXECUTE | EXPLAIN + | EXTENSION | EXTERNAL | FAMILY | FIRST_P diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c index e43bc54b3f..8267627c42 100644 --- a/src/backend/parser/parse_agg.c +++ b/src/backend/parser/parse_agg.c @@ -723,6 +723,7 @@ build_aggregate_fnexprs(Oid *agg_input_types, Oid agg_result_type, Oid transfn_oid, Oid finalfn_oid, + Oid collation, Expr **transfnexpr, Expr **finalfnexpr) { @@ -741,6 +742,7 @@ build_aggregate_fnexprs(Oid *agg_input_types, argp->paramid = -1; argp->paramtype = agg_state_type; argp->paramtypmod = -1; + argp->paramcollation = collation; argp->location = -1; args = list_make1(argp); @@ -752,6 +754,7 @@ build_aggregate_fnexprs(Oid *agg_input_types, argp->paramid = -1; argp->paramtype = agg_input_types[i]; argp->paramtypmod = -1; + argp->paramcollation = collation; argp->location = -1; args = lappend(args, argp); } @@ -759,6 +762,7 @@ build_aggregate_fnexprs(Oid *agg_input_types, *transfnexpr = (Expr *) makeFuncExpr(transfn_oid, agg_state_type, args, + collation, COERCE_DONTCARE); /* see if we have a final function */ @@ -776,11 +780,13 @@ build_aggregate_fnexprs(Oid *agg_input_types, argp->paramid = -1; argp->paramtype = agg_state_type; argp->paramtypmod = -1; + argp->paramcollation = collation; argp->location = -1; args = list_make1(argp); *finalfnexpr = (Expr *) makeFuncExpr(finalfn_oid, agg_result_type, args, + collation, COERCE_DONTCARE); } diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index f9560d07b9..d250e0c859 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -613,7 +613,8 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r) tupdesc = BuildDescFromLists(rte->eref->colnames, rte->funccoltypes, - rte->funccoltypmods); + rte->funccoltypmods, + rte->funccolcollations); CheckAttributeNamesTypes(tupdesc, RELKIND_COMPOSITE_TYPE, false); } @@ -1935,6 +1936,7 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle, bool resolveUnknown) { Oid restype = exprType((Node *) tle->expr); + Oid rescollation = exprCollation((Node *) tle->expr); Oid sortop; Oid eqop; bool hashable; @@ -2018,6 +2020,12 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle, break; } + if (type_is_collatable(restype) && !OidIsValid(rescollation)) + ereport(ERROR, + (errcode(ERRCODE_INDETERMINATE_COLLATION), + errmsg("no collation was derived for the sort expression"), + errhint("Use the COLLATE clause to set the collation explicitly."))); + cancel_parser_errposition_callback(&pcbstate); /* avoid making duplicate sortlist entries */ diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 5b0dc1420d..2fd808d26b 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -16,6 +16,7 @@ #include "catalog/pg_cast.h" #include "catalog/pg_class.h" +#include "catalog/pg_collation.h" #include "catalog/pg_inherits_fn.h" #include "catalog/pg_proc.h" #include "catalog/pg_type.h" @@ -216,6 +217,7 @@ coerce_type(ParseState *pstate, Node *node, newcon->consttype = baseTypeId; newcon->consttypmod = inputTypeMod; + newcon->constcollid = get_typcollation(newcon->consttype); newcon->constlen = typeLen(targetType); newcon->constbyval = typeByVal(targetType); newcon->constisnull = con->constisnull; @@ -277,6 +279,14 @@ coerce_type(ParseState *pstate, Node *node, if (result) return result; } + if (IsA(node, CollateClause)) + { + CollateClause *cc = (CollateClause *) node; + + cc->arg = (Expr *) coerce_type(pstate, (Node *) cc->arg, inputTypeId, targetTypeId, targetTypeMod, + ccontext, cformat, location); + return (Node *) cc; + } pathtype = find_coercion_pathway(targetTypeId, inputTypeId, ccontext, &funcId); if (pathtype != COERCION_PATH_NONE) @@ -718,6 +728,7 @@ build_coercion_expression(Node *node, FuncExpr *fexpr; List *args; Const *cons; + Oid collation; Assert(OidIsValid(funcId)); @@ -749,7 +760,9 @@ build_coercion_expression(Node *node, args = lappend(args, cons); } - fexpr = makeFuncExpr(funcId, targetTypeId, args, cformat); + collation = coercion_expression_result_collation(targetTypeId, node); + + fexpr = makeFuncExpr(funcId, targetTypeId, args, collation, cformat); fexpr->location = location; return (Node *) fexpr; } @@ -2081,3 +2094,120 @@ typeIsOfTypedTable(Oid reltypeId, Oid reloftypeId) return result; } + + +/* + * select_common_collation() -- determine one collation to apply for + * an expression node, for evaluating the expression itself or to + * label the result of the expression node. + * + * none_ok means that it is permitted to return "no" collation. It is + * then not possible to sort the result value of whatever expression + * is applying this. none_ok = true reflects the rules of SQL + * standard clause "Result of data type combinations", none_ok = false + * reflects the rules of clause "Collation determination" (in some + * cases invoked via "Grouping operations"). + */ +Oid +select_common_collation(ParseState *pstate, List *exprs, bool none_ok) +{ + ListCell *lc; + + /* + * Check if there are any explicit collation derivations. If so, + * they must all be the same. + */ + foreach(lc, exprs) + { + Node *pexpr = (Node *) lfirst(lc); + Oid pcoll = exprCollation(pexpr); + bool pexplicit = IsA(pexpr, CollateClause); + + if (pcoll && pexplicit) + { + ListCell *lc2; + for_each_cell(lc2, lnext(lc)) + { + Node *nexpr = (Node *) lfirst(lc2); + Oid ncoll = exprCollation(nexpr); + bool nexplicit = IsA(nexpr, CollateClause); + + if (!ncoll || !nexplicit) + continue; + + if (ncoll != pcoll) + ereport(ERROR, + (errcode(ERRCODE_COLLATION_MISMATCH), + errmsg("collation mismatch between explicit collations \"%s\" and \"%s\"", + get_collation_name(pcoll), + get_collation_name(ncoll)), + parser_errposition(pstate, exprLocation(nexpr)))); + } + + return pcoll; + } + } + + /* + * Check if there are any implicit collation derivations. + */ + foreach(lc, exprs) + { + Node *pexpr = (Node *) lfirst(lc); + Oid pcoll = exprCollation(pexpr); + + if (pcoll && pcoll != DEFAULT_COLLATION_OID) + { + ListCell *lc2; + for_each_cell(lc2, lnext(lc)) + { + Node *nexpr = (Node *) lfirst(lc2); + Oid ncoll = exprCollation(nexpr); + + if (!ncoll || ncoll == DEFAULT_COLLATION_OID) + continue; + + if (ncoll != pcoll) + { + if (none_ok) + return InvalidOid; + ereport(ERROR, + (errcode(ERRCODE_COLLATION_MISMATCH), + errmsg("collation mismatch between implicit collations \"%s\" and \"%s\"", + get_collation_name(pcoll), + get_collation_name(ncoll)), + errhint("You can override the collation by applying the COLLATE clause to one or both expressions."), + parser_errposition(pstate, exprLocation(nexpr)))); + } + } + + return pcoll; + } + } + + foreach(lc, exprs) + { + Node *pexpr = (Node *) lfirst(lc); + Oid pcoll = exprCollation(pexpr); + + if (pcoll == DEFAULT_COLLATION_OID) + { + ListCell *lc2; + for_each_cell(lc2, lnext(lc)) + { + Node *nexpr = (Node *) lfirst(lc2); + Oid ncoll = exprCollation(nexpr); + + if (ncoll != pcoll) + break; + } + + return pcoll; + } + } + + /* + * Else use default + */ + return InvalidOid; +} diff --git a/src/backend/parser/parse_cte.c b/src/backend/parser/parse_cte.c index 24ba008f9e..4d3d33eb07 100644 --- a/src/backend/parser/parse_cte.c +++ b/src/backend/parser/parse_cte.c @@ -14,11 +14,13 @@ */ #include "postgres.h" +#include "catalog/pg_collation.h" #include "catalog/pg_type.h" #include "nodes/nodeFuncs.h" #include "parser/analyze.h" #include "parser/parse_cte.h" #include "utils/builtins.h" +#include "utils/lsyscache.h" /* Enumeration of contexts in which a self-reference is disallowed */ @@ -263,11 +265,13 @@ analyzeCTE(ParseState *pstate, CommonTableExpr *cte) */ ListCell *lctlist, *lctyp, - *lctypmod; + *lctypmod, + *lccoll; int varattno; lctyp = list_head(cte->ctecoltypes); lctypmod = list_head(cte->ctecoltypmods); + lccoll = list_head(cte->ctecolcollations); varattno = 0; foreach(lctlist, query->targetList) { @@ -278,7 +282,7 @@ analyzeCTE(ParseState *pstate, CommonTableExpr *cte) continue; varattno++; Assert(varattno == te->resno); - if (lctyp == NULL || lctypmod == NULL) /* shouldn't happen */ + if (lctyp == NULL || lctypmod == NULL || lccoll == NULL) /* shouldn't happen */ elog(ERROR, "wrong number of output columns in WITH"); texpr = (Node *) te->expr; if (exprType(texpr) != lfirst_oid(lctyp) || @@ -293,10 +297,20 @@ analyzeCTE(ParseState *pstate, CommonTableExpr *cte) exprTypmod(texpr))), errhint("Cast the output of the non-recursive term to the correct type."), parser_errposition(pstate, exprLocation(texpr)))); + if (exprCollation(texpr) != lfirst_oid(lccoll)) + ereport(ERROR, + (errcode(ERRCODE_COLLATION_MISMATCH), + errmsg("recursive query \"%s\" column %d has collation \"%s\" in non-recursive term but collation \"%s\" overall", + cte->ctename, varattno, + get_collation_name(lfirst_oid(lccoll)), + get_collation_name(exprCollation(texpr))), + errhint("Use the COLLATE clause to set the collation of the non-recursive term."), + parser_errposition(pstate, exprLocation(texpr)))); lctyp = lnext(lctyp); lctypmod = lnext(lctypmod); + lccoll = lnext(lccoll); } - if (lctyp != NULL || lctypmod != NULL) /* shouldn't happen */ + if (lctyp != NULL || lctypmod != NULL || lccoll != NULL) /* shouldn't happen */ elog(ERROR, "wrong number of output columns in WITH"); } } @@ -331,7 +345,7 @@ analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist) * handling.) */ cte->ctecolnames = copyObject(cte->aliascolnames); - cte->ctecoltypes = cte->ctecoltypmods = NIL; + cte->ctecoltypes = cte->ctecoltypmods = cte->ctecolcollations = NIL; numaliases = list_length(cte->aliascolnames); varattno = 0; foreach(tlistitem, tlist) @@ -339,6 +353,7 @@ analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist) TargetEntry *te = (TargetEntry *) lfirst(tlistitem); Oid coltype; int32 coltypmod; + Oid colcoll; if (te->resjunk) continue; @@ -353,6 +368,7 @@ analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist) } coltype = exprType((Node *) te->expr); coltypmod = exprTypmod((Node *) te->expr); + colcoll = exprCollation((Node *) te->expr); /* * If the CTE is recursive, force the exposed column type of any @@ -366,9 +382,11 @@ analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist) { coltype = TEXTOID; coltypmod = -1; /* should be -1 already, but be sure */ + colcoll = DEFAULT_COLLATION_OID; } cte->ctecoltypes = lappend_oid(cte->ctecoltypes, coltype); cte->ctecoltypmods = lappend_int(cte->ctecoltypmods, coltypmod); + cte->ctecolcollations = lappend_oid(cte->ctecolcollations, colcoll); } if (varattno < numaliases) ereport(ERROR, diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 129b39cb26..ae56532592 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -65,6 +65,7 @@ static Node *transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte, static Node *transformIndirection(ParseState *pstate, Node *basenode, List *indirection); static Node *transformTypeCast(ParseState *pstate, TypeCast *tc); +static Node *transformCollateClause(ParseState *pstate, CollateClause *c); static Node *make_row_comparison_op(ParseState *pstate, List *opname, List *largs, List *rargs, int location); static Node *make_row_distinct_op(ParseState *pstate, List *opname, @@ -146,6 +147,12 @@ transformExpr(ParseState *pstate, Node *expr) { TypeCast *tc = (TypeCast *) expr; + if (tc->typeName->collnames) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("COLLATE clause not allowed in cast target"), + parser_errposition(pstate, tc->typeName->location))); + /* * If the subject of the typecast is an ARRAY[] construct and * the target type is an array type, we invoke @@ -185,6 +192,10 @@ transformExpr(ParseState *pstate, Node *expr) break; } + case T_CollateClause: + result = transformCollateClause(pstate, (CollateClause *) expr); + break; + case T_A_Expr: { A_Expr *a = (A_Expr *) expr; @@ -423,6 +434,7 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection) exprType(result), InvalidOid, exprTypmod(result), + exprCollation(result), subscripts, NULL); subscripts = NIL; @@ -444,6 +456,7 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection) exprType(result), InvalidOid, exprTypmod(result), + exprCollation(result), subscripts, NULL); @@ -1267,6 +1280,7 @@ transformCaseExpr(ParseState *pstate, CaseExpr *c) placeholder = makeNode(CaseTestExpr); placeholder->typeId = exprType(arg); placeholder->typeMod = exprTypmod(arg); + placeholder->collation = exprCollation(arg); } else placeholder = NULL; @@ -1351,6 +1365,8 @@ transformCaseExpr(ParseState *pstate, CaseExpr *c) "CASE/WHEN"); } + newc->casecollation = select_common_collation(pstate, resultexprs, true); + newc->location = c->location; return (Node *) newc; @@ -1461,6 +1477,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink) param->paramid = tent->resno; param->paramtype = exprType((Node *) tent->expr); param->paramtypmod = exprTypmod((Node *) tent->expr); + param->paramcollation = exprCollation((Node *) tent->expr); param->location = -1; right_list = lappend(right_list, param); @@ -1704,6 +1721,7 @@ transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c) } newc->args = newcoercedargs; + newc->coalescecollation = select_common_collation(pstate, newcoercedargs, true); newc->location = c->location; return (Node *) newc; } @@ -1728,6 +1746,7 @@ transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m) } newm->minmaxtype = select_common_type(pstate, newargs, funcname, NULL); + newm->collid = select_common_collation(pstate, newargs, false); /* Convert arguments if necessary */ foreach(args, newargs) @@ -2083,6 +2102,36 @@ transformTypeCast(ParseState *pstate, TypeCast *tc) } /* + * Handle an explicit COLLATE clause. + * + * Transform the argument, and look up the collation name. + */ +static Node * +transformCollateClause(ParseState *pstate, CollateClause *c) +{ + CollateClause *newc; + Oid argtype; + + newc = makeNode(CollateClause); + newc->arg = (Expr *) transformExpr(pstate, (Node *) c->arg); + + argtype = exprType((Node *) newc->arg); + /* The unknown type is not collatable, but coerce_type() takes + * care of it separately, so we'll let it go here. */ + if (!type_is_collatable(argtype) && argtype != UNKNOWNOID) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("collations are not supported by type %s", + format_type_be(argtype)))); + + newc->collOid = LookupCollation(pstate, c->collnames, c->location); + newc->collnames = c->collnames; + newc->location = c->location; + + return (Node *) newc; +} + +/* * Transform a "row compare-op row" construct * * The inputs are lists of already-transformed expressions. @@ -2103,6 +2152,7 @@ make_row_comparison_op(ParseState *pstate, List *opname, List *opexprs; List *opnos; List *opfamilies; + List *collids; ListCell *l, *r; List **opfamily_lists; @@ -2273,6 +2323,7 @@ make_row_comparison_op(ParseState *pstate, List *opname, * possibility that make_op inserted coercion operations. */ opnos = NIL; + collids = NIL; largs = NIL; rargs = NIL; foreach(l, opexprs) @@ -2280,6 +2331,7 @@ make_row_comparison_op(ParseState *pstate, List *opname, OpExpr *cmp = (OpExpr *) lfirst(l); opnos = lappend_oid(opnos, cmp->opno); + collids = lappend_oid(collids, cmp->collid); largs = lappend(largs, linitial(cmp->args)); rargs = lappend(rargs, lsecond(cmp->args)); } @@ -2288,6 +2340,7 @@ make_row_comparison_op(ParseState *pstate, List *opname, rcexpr->rctype = rctype; rcexpr->opnos = opnos; rcexpr->opfamilies = opfamilies; + rcexpr->collids = collids; rcexpr->largs = largs; rcexpr->rargs = rargs; diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index f1a4f9b959..0af9cbd92b 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -78,6 +78,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, bool retset; int nvargs; FuncDetailCode fdresult; + Oid funccollid; /* * Most of the rest of the parser just assumes that functions do not have @@ -343,6 +344,12 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, /* perform the necessary typecasting of arguments */ make_fn_arguments(pstate, fargs, actual_arg_types, declared_arg_types); + /* XXX: If we knew which functions required collation information, + * we could selectively set the last argument to true here. */ + funccollid = select_common_collation(pstate, fargs, false); + if (!OidIsValid(funccollid)) + funccollid = get_typcollation(rettype); + /* * If it's a variadic function call, transform the last nvargs arguments * into an array --- unless it's an "any" variadic. @@ -383,6 +390,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, funcexpr->funcretset = retset; funcexpr->funcformat = COERCE_EXPLICIT_CALL; funcexpr->args = fargs; + funcexpr->collid = funccollid; funcexpr->location = location; retval = (Node *) funcexpr; @@ -396,6 +404,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, aggref->aggtype = rettype; /* args, aggorder, aggdistinct will be set by transformAggregateCall */ aggref->aggstar = agg_star; + aggref->collid = funccollid; /* agglevelsup will be set by transformAggregateCall */ aggref->location = location; @@ -453,6 +462,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, /* winref will be set by transformWindowFuncCall */ wfunc->winstar = agg_star; wfunc->winagg = (fdresult == FUNCDETAIL_AGGREGATE); + wfunc->collid = funccollid; wfunc->location = location; /* @@ -1303,7 +1313,7 @@ FuncNameAsType(List *funcname) Oid result; Type typtup; - typtup = LookupTypeName(NULL, makeTypeNameFromNameList(funcname), NULL); + typtup = LookupTypeName(NULL, makeTypeNameFromNameList(funcname), NULL, NULL); if (typtup == NULL) return InvalidOid; @@ -1380,6 +1390,7 @@ ParseComplexProjection(ParseState *pstate, char *funcname, Node *first_arg, fselect->fieldnum = i + 1; fselect->resulttype = att->atttypid; fselect->resulttypmod = att->atttypmod; + fselect->resultcollation = att->attcollation; return (Node *) fselect; } } @@ -1489,7 +1500,7 @@ LookupTypeNameOid(const TypeName *typename) Oid result; Type typtup; - typtup = LookupTypeName(NULL, typename, NULL); + typtup = LookupTypeName(NULL, typename, NULL, NULL); if (typtup == NULL) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c index aed404eb1d..163fc89179 100644 --- a/src/backend/parser/parse_node.c +++ b/src/backend/parser/parse_node.c @@ -189,10 +189,11 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno, int location) sublevels_up; Oid vartypeid; int32 type_mod; + Oid varcollid; vnum = RTERangeTablePosn(pstate, rte, &sublevels_up); - get_rte_attribute_type(rte, attrno, &vartypeid, &type_mod); - result = makeVar(vnum, attrno, vartypeid, type_mod, sublevels_up); + get_rte_attribute_type(rte, attrno, &vartypeid, &type_mod, &varcollid); + result = makeVar(vnum, attrno, vartypeid, type_mod, varcollid, sublevels_up); result->location = location; return result; } @@ -269,6 +270,7 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod) * elementType OID of array's element type (fetch with transformArrayType, * or pass InvalidOid to do it here) * arrayTypMod typmod for the array (which is also typmod for the elements) + * arrayColl OID of collation of array and array's elements * indirection Untransformed list of subscripts (must not be NIL) * assignFrom NULL for array fetch, else transformed expression for source. */ @@ -278,6 +280,7 @@ transformArraySubscripts(ParseState *pstate, Oid arrayType, Oid elementType, int32 arrayTypMod, + Oid arrayColl, List *indirection, Node *assignFrom) { @@ -404,6 +407,7 @@ transformArraySubscripts(ParseState *pstate, aref->refarraytype = arrayType; aref->refelemtype = elementType; aref->reftypmod = arrayTypMod; + aref->refcollid = arrayColl; aref->refupperindexpr = upperIndexpr; aref->reflowerindexpr = lowerIndexpr; aref->refexpr = (Expr *) arrayBase; diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c index 1f50bdcc34..cad41d46f0 100644 --- a/src/backend/parser/parse_oper.c +++ b/src/backend/parser/parse_oper.c @@ -782,6 +782,7 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree, List *args; Oid rettype; OpExpr *result; + Oid opcollid; /* Select the operator */ if (rtree == NULL) @@ -861,6 +862,12 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree, /* perform the necessary typecasting of arguments */ make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types); + /* XXX: If we knew which functions required collation information, + * we could selectively set the last argument to true here. */ + opcollid = select_common_collation(pstate, args, false); + if (!OidIsValid(opcollid)) + opcollid = get_typcollation(rettype); + /* and build the expression node */ result = makeNode(OpExpr); result->opno = oprid(tup); @@ -868,6 +875,7 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree, result->opresulttype = rettype; result->opretset = get_func_retset(opform->oprcode); result->args = args; + result->collid = opcollid; result->location = location; ReleaseSysCache(tup); @@ -896,6 +904,7 @@ make_scalar_array_op(ParseState *pstate, List *opname, List *args; Oid rettype; ScalarArrayOpExpr *result; + Oid opcollid; ltypeId = exprType(ltree); atypeId = exprType(rtree); @@ -990,12 +999,19 @@ make_scalar_array_op(ParseState *pstate, List *opname, /* perform the necessary typecasting of arguments */ make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types); + /* XXX: If we knew which functions required collation information, + * we could selectively set the last argument to true here. */ + opcollid = select_common_collation(pstate, args, false); + if (!OidIsValid(opcollid)) + opcollid = get_typcollation(rettype); + /* and build the expression node */ result = makeNode(ScalarArrayOpExpr); result->opno = oprid(tup); result->opfuncid = opform->oprcode; result->useOr = useOr; result->args = args; + result->collid = opcollid; result->location = location; ReleaseSysCache(tup); diff --git a/src/backend/parser/parse_param.c b/src/backend/parser/parse_param.c index c9987d2723..9e9f2e3ca0 100644 --- a/src/backend/parser/parse_param.c +++ b/src/backend/parser/parse_param.c @@ -30,6 +30,7 @@ #include "nodes/nodeFuncs.h" #include "parser/parse_param.h" #include "utils/builtins.h" +#include "utils/lsyscache.h" typedef struct FixedParamState @@ -113,6 +114,7 @@ fixed_paramref_hook(ParseState *pstate, ParamRef *pref) param->paramid = paramno; param->paramtype = parstate->paramTypes[paramno - 1]; param->paramtypmod = -1; + param->paramcollation = get_typcollation(param->paramtype); param->location = pref->location; return (Node *) param; @@ -165,6 +167,7 @@ variable_paramref_hook(ParseState *pstate, ParamRef *pref) param->paramid = paramno; param->paramtype = *pptype; param->paramtypmod = -1; + param->paramcollation = get_typcollation(param->paramtype); param->location = pref->location; return (Node *) param; diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 331ac670ff..497c726f31 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -1098,6 +1098,7 @@ addRangeTableEntryForFunction(ParseState *pstate, rte->funcexpr = funcexpr; rte->funccoltypes = NIL; rte->funccoltypmods = NIL; + rte->funccolcollations = NIL; rte->alias = alias; eref = makeAlias(alias ? alias->aliasname : funcname, NIL); @@ -1157,6 +1158,7 @@ addRangeTableEntryForFunction(ParseState *pstate, char *attrname; Oid attrtype; int32 attrtypmod; + Oid attrcollation; attrname = pstrdup(n->colname); if (n->typeName->setof) @@ -1165,10 +1167,11 @@ addRangeTableEntryForFunction(ParseState *pstate, errmsg("column \"%s\" cannot be declared SETOF", attrname), parser_errposition(pstate, n->typeName->location))); - typenameTypeIdAndMod(pstate, n->typeName, &attrtype, &attrtypmod); + typenameTypeIdModColl(pstate, n->typeName, &attrtype, &attrtypmod, &attrcollation); eref->colnames = lappend(eref->colnames, makeString(attrname)); rte->funccoltypes = lappend_oid(rte->funccoltypes, attrtype); rte->funccoltypmods = lappend_int(rte->funccoltypmods, attrtypmod); + rte->funccolcollations = lappend_oid(rte->funccolcollations, attrcollation); } } else @@ -1381,6 +1384,7 @@ addRangeTableEntryForCTE(ParseState *pstate, rte->ctecoltypes = cte->ctecoltypes; rte->ctecoltypmods = cte->ctecoltypmods; + rte->ctecolcollations = cte->ctecolcollations; rte->alias = alias; if (alias) @@ -1573,6 +1577,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, varnode = makeVar(rtindex, varattno, exprType((Node *) te->expr), exprTypmod((Node *) te->expr), + exprCollation((Node *) te->expr), sublevels_up); varnode->location = location; @@ -1612,6 +1617,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, varnode = makeVar(rtindex, 1, funcrettype, -1, + exprCollation(rte->funcexpr), sublevels_up); varnode->location = location; @@ -1626,12 +1632,14 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, { ListCell *l1; ListCell *l2; + ListCell *l3; int attnum = 0; - forboth(l1, rte->funccoltypes, l2, rte->funccoltypmods) + forthree(l1, rte->funccoltypes, l2, rte->funccoltypmods, l3, rte->funccolcollations) { Oid attrtype = lfirst_oid(l1); int32 attrtypmod = lfirst_int(l2); + Oid attrcollation = lfirst_oid(l3); Var *varnode; attnum++; @@ -1639,6 +1647,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, attnum, attrtype, attrtypmod, + attrcollation, sublevels_up); varnode->location = location; *colvars = lappend(*colvars, varnode); @@ -1681,6 +1690,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, varnode = makeVar(rtindex, varattno, exprType(col), exprTypmod(col), + exprCollation(col), sublevels_up); varnode->location = location; *colvars = lappend(*colvars, varnode); @@ -1740,6 +1750,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, varnode = makeVar(rtindex, varattno, exprType(avar), exprTypmod(avar), + exprCollation(avar), sublevels_up); varnode->location = location; @@ -1753,12 +1764,14 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, ListCell *aliasp_item = list_head(rte->eref->colnames); ListCell *lct; ListCell *lcm; + ListCell *lcc; varattno = 0; - forboth(lct, rte->ctecoltypes, lcm, rte->ctecoltypmods) + forthree(lct, rte->ctecoltypes, lcm, rte->ctecoltypmods, lcc, rte->ctecolcollations) { Oid coltype = lfirst_oid(lct); int32 coltypmod = lfirst_int(lcm); + Oid colcoll = lfirst_oid(lcc); varattno++; @@ -1776,7 +1789,7 @@ expandRTE(RangeTblEntry *rte, int rtindex, int sublevels_up, Var *varnode; varnode = makeVar(rtindex, varattno, - coltype, coltypmod, + coltype, coltypmod, colcoll, sublevels_up); *colvars = lappend(*colvars, varnode); } @@ -1857,7 +1870,7 @@ expandTupleDesc(TupleDesc tupdesc, Alias *eref, Var *varnode; varnode = makeVar(rtindex, attr->attnum, - attr->atttypid, attr->atttypmod, + attr->atttypid, attr->atttypmod, attr->attcollation, sublevels_up); varnode->location = location; @@ -1968,7 +1981,7 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum) */ void get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, - Oid *vartype, int32 *vartypmod) + Oid *vartype, int32 *vartypmod, Oid *varcollid) { switch (rte->rtekind) { @@ -1998,6 +2011,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, get_rel_name(rte->relid)))); *vartype = att_tup->atttypid; *vartypmod = att_tup->atttypmod; + *varcollid = att_tup->attcollation; ReleaseSysCache(tp); } break; @@ -2012,6 +2026,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, rte->eref->aliasname, attnum); *vartype = exprType((Node *) te->expr); *vartypmod = exprTypmod((Node *) te->expr); + *varcollid = exprCollation((Node *) te->expr); } break; case RTE_FUNCTION: @@ -2053,17 +2068,20 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, rte->eref->aliasname))); *vartype = att_tup->atttypid; *vartypmod = att_tup->atttypmod; + *varcollid = att_tup->attcollation; } else if (functypclass == TYPEFUNC_SCALAR) { /* Base data type, i.e. scalar */ *vartype = funcrettype; *vartypmod = -1; + *varcollid = exprCollation(rte->funcexpr); } else if (functypclass == TYPEFUNC_RECORD) { *vartype = list_nth_oid(rte->funccoltypes, attnum - 1); *vartypmod = list_nth_int(rte->funccoltypmods, attnum - 1); + *varcollid = list_nth_oid(rte->funccolcollations, attnum - 1); } else { @@ -2084,6 +2102,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, col = (Node *) list_nth(collist, attnum - 1); *vartype = exprType(col); *vartypmod = exprTypmod(col); + *varcollid = exprCollation(col); } break; case RTE_JOIN: @@ -2097,6 +2116,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, aliasvar = (Node *) list_nth(rte->joinaliasvars, attnum - 1); *vartype = exprType(aliasvar); *vartypmod = exprTypmod(aliasvar); + *varcollid = exprCollation(aliasvar); } break; case RTE_CTE: @@ -2105,6 +2125,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, Assert(attnum > 0 && attnum <= list_length(rte->ctecoltypes)); *vartype = list_nth_oid(rte->ctecoltypes, attnum - 1); *vartypmod = list_nth_int(rte->ctecoltypmods, attnum - 1); + *varcollid = list_nth_oid(rte->ctecolcollations, attnum - 1); } break; default: diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c index 7d77e0b63f..a0761da875 100644 --- a/src/backend/parser/parse_target.c +++ b/src/backend/parser/parse_target.c @@ -374,6 +374,7 @@ transformAssignedExpr(ParseState *pstate, Oid type_id; /* type of value provided */ Oid attrtype; /* type of target column */ int32 attrtypmod; + Oid attrcollation; Relation rd = pstate->p_target_relation; Assert(rd != NULL); @@ -385,6 +386,7 @@ transformAssignedExpr(ParseState *pstate, parser_errposition(pstate, location))); attrtype = attnumTypeId(rd, attrno); attrtypmod = rd->rd_att->attrs[attrno - 1]->atttypmod; + attrcollation = rd->rd_att->attrs[attrno - 1]->attcollation; /* * If the expression is a DEFAULT placeholder, insert the attribute's @@ -400,6 +402,7 @@ transformAssignedExpr(ParseState *pstate, def->typeId = attrtype; def->typeMod = attrtypmod; + def->collid = attrcollation; if (indirection) { if (IsA(linitial(indirection), A_Indices)) @@ -786,6 +789,7 @@ transformAssignmentSubscripts(ParseState *pstate, arrayType, elementTypeId, arrayTypMod, + InvalidOid, subscripts, rhs); @@ -1267,6 +1271,7 @@ ExpandRowReference(ParseState *pstate, Node *expr, fselect->fieldnum = i + 1; fselect->resulttype = att->atttypid; fselect->resulttypmod = att->atttypmod; + fselect->resultcollation = att->attcollation; if (targetlist) { @@ -1338,6 +1343,8 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup) exprType(varnode), exprTypmod(varnode), 0); + TupleDescInitEntryCollation(tupleDesc, i, + exprCollation(varnode)); i++; } Assert(lname == NULL && lvar == NULL); /* lists same length? */ @@ -1583,6 +1590,8 @@ FigureColnameInternal(Node *node, char **name) } } break; + case T_CollateClause: + return FigureColnameInternal((Node *) ((CollateClause *) node)->arg, name); case T_CaseExpr: strength = FigureColnameInternal((Node *) ((CaseExpr *) node)->defresult, name); diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c index 0b601c8b75..20cb47e712 100644 --- a/src/backend/parser/parse_type.c +++ b/src/backend/parser/parse_type.c @@ -29,6 +29,8 @@ static int32 typenameTypeMod(ParseState *pstate, const TypeName *typeName, Type typ); +static Oid typenameCollation(ParseState *pstate, const TypeName *typeName, + Type typ); /* @@ -36,7 +38,8 @@ static int32 typenameTypeMod(ParseState *pstate, const TypeName *typeName, * Given a TypeName object, lookup the pg_type syscache entry of the type. * Returns NULL if no such type can be found. If the type is found, * the typmod value represented in the TypeName struct is computed and - * stored into *typmod_p. + * stored into *typmod_p, and the collation is looked up and stored into + * *colloid_p. * * NB: on success, the caller must ReleaseSysCache the type tuple when done * with it. @@ -51,15 +54,18 @@ static int32 typenameTypeMod(ParseState *pstate, const TypeName *typeName, * found but is a shell, and there is typmod decoration, an error will be * thrown --- this is intentional. * + * colloid_p can also be null. + * * pstate is only used for error location info, and may be NULL. */ Type LookupTypeName(ParseState *pstate, const TypeName *typeName, - int32 *typmod_p) + int32 *typmod_p, Oid *collid_p) { Oid typoid; HeapTuple tup; int32 typmod; + Oid collid; if (typeName->names == NIL) { @@ -174,22 +180,28 @@ LookupTypeName(ParseState *pstate, const TypeName *typeName, if (typmod_p) *typmod_p = typmod; + collid = typenameCollation(pstate, typeName, (Type) tup); + + if (collid_p) + *collid_p = collid; + return (Type) tup; } /* - * typenameType - given a TypeName, return a Type structure and typmod + * typenameType - given a TypeName, return a Type structure, typmod, and + * collation * * This is equivalent to LookupTypeName, except that this will report * a suitable error message if the type cannot be found or is not defined. * Callers of this can therefore assume the result is a fully valid type. */ Type -typenameType(ParseState *pstate, const TypeName *typeName, int32 *typmod_p) +typenameType(ParseState *pstate, const TypeName *typeName, int32 *typmod_p, Oid *collid_p) { Type tup; - tup = LookupTypeName(pstate, typeName, typmod_p); + tup = LookupTypeName(pstate, typeName, typmod_p, collid_p); if (tup == NULL) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), @@ -217,7 +229,7 @@ typenameTypeId(ParseState *pstate, const TypeName *typeName) Oid typoid; Type tup; - tup = typenameType(pstate, typeName, NULL); + tup = typenameType(pstate, typeName, NULL, NULL); typoid = HeapTupleGetOid(tup); ReleaseSysCache(tup); @@ -236,7 +248,25 @@ typenameTypeIdAndMod(ParseState *pstate, const TypeName *typeName, { Type tup; - tup = typenameType(pstate, typeName, typmod_p); + tup = typenameType(pstate, typeName, typmod_p, NULL); + *typeid_p = HeapTupleGetOid(tup); + ReleaseSysCache(tup); +} + +/* + * typenameTypeIdModColl - given a TypeName, return the type's OID, + * typmod, and collation + * + * This is equivalent to typenameType, but we only hand back the type OID, + * typmod, and collation, not the syscache entry. + */ +void +typenameTypeIdModColl(ParseState *pstate, const TypeName *typeName, + Oid *typeid_p, int32 *typmod_p, Oid *collid_p) +{ + Type tup; + + tup = typenameType(pstate, typeName, typmod_p, collid_p); *typeid_p = HeapTupleGetOid(tup); ReleaseSysCache(tup); } @@ -351,6 +381,62 @@ typenameTypeMod(ParseState *pstate, const TypeName *typeName, Type typ) } /* + * typenameCollation - given a TypeName, return the collation OID + * + * This will throw an error if the TypeName includes a collation but + * the data type does not support collations. + * + * The actual type OID represented by the TypeName must already have been + * looked up, and is passed as "typ". + * + * pstate is only used for error location info, and may be NULL. + */ +static Oid +typenameCollation(ParseState *pstate, const TypeName *typeName, Type typ) +{ + Oid typcollation = ((Form_pg_type) GETSTRUCT(typ))->typcollation; + + /* return prespecified collation OID if no collation name specified */ + if (typeName->collnames == NIL) + { + if (typeName->collOid == InvalidOid) + return typcollation; + else + return typeName->collOid; + } + + if (!OidIsValid(typcollation)) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("collations are not supported by type %s", + format_type_be(HeapTupleGetOid(typ))), + parser_errposition(pstate, typeName->location))); + + return LookupCollation(pstate, typeName->collnames, typeName->location); +} + +/* + * LookupCollation + * + * Look up collation by name, return OID, with support for error + * location. + */ +Oid +LookupCollation(ParseState *pstate, List *collnames, int location) +{ + Oid colloid; + ParseCallbackState pcbstate; + + setup_parser_errposition_callback(&pcbstate, pstate, location); + + colloid = get_collation_oid(collnames, false); + + cancel_parser_errposition_callback(&pcbstate); + + return colloid; +} + +/* * appendTypeNameToBuffer * Append a string representing the name of a TypeName to a StringInfo. * This is the shared guts of TypeNameToString and TypeNameListToString. diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index e0ab88232b..61ce840a5e 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -627,7 +627,8 @@ transformInhRelation(CreateStmtContext *cxt, InhRelation *inhRelation) def = makeNode(ColumnDef); def->colname = pstrdup(attributeName); def->typeName = makeTypeNameFromOid(attribute->atttypid, - attribute->atttypmod); + attribute->atttypmod, + attribute->attcollation); def->inhcount = 0; def->is_local = true; def->is_not_null = attribute->attnotnull; @@ -821,7 +822,7 @@ transformOfType(CreateStmtContext *cxt, TypeName *ofTypename) AssertArg(ofTypename); - tuple = typenameType(NULL, ofTypename, NULL); + tuple = typenameType(NULL, ofTypename, NULL, NULL); typ = (Form_pg_type) GETSTRUCT(tuple); ofTypeId = HeapTupleGetOid(tuple); ofTypename->typeOid = ofTypeId; /* cached for later */ @@ -842,7 +843,7 @@ transformOfType(CreateStmtContext *cxt, TypeName *ofTypename) continue; n->colname = pstrdup(NameStr(attr->attname)); - n->typeName = makeTypeNameFromOid(attr->atttypid, attr->atttypmod); + n->typeName = makeTypeNameFromOid(attr->atttypid, attr->atttypmod, attr->attcollation); n->constraints = NULL; n->is_local = true; n->is_from_type = true; @@ -2446,7 +2447,7 @@ transformColumnType(CreateStmtContext *cxt, ColumnDef *column) /* * All we really need to do here is verify that the type is valid. */ - Type ctype = typenameType(cxt->pstate, column->typeName, NULL); + Type ctype = typenameType(cxt->pstate, column->typeName, NULL, NULL); ReleaseSysCache(ctype); } diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c index 301568f6df..b391b48153 100644 --- a/src/backend/postmaster/pgstat.c +++ b/src/backend/postmaster/pgstat.c @@ -3160,6 +3160,8 @@ pgstat_get_db_entry(Oid databaseid, bool create) result->n_conflict_bufferpin = 0; result->n_conflict_startup_deadlock = 0; + result->stat_reset_timestamp = GetCurrentTimestamp(); + memset(&hash_ctl, 0, sizeof(hash_ctl)); hash_ctl.keysize = sizeof(Oid); hash_ctl.entrysize = sizeof(PgStat_StatTabEntry); @@ -3440,6 +3442,12 @@ pgstat_read_statsfile(Oid onlydb, bool permanent) memset(&globalStats, 0, sizeof(globalStats)); /* + * Set the current timestamp (will be kept only in case we can't load an + * existing statsfile. + */ + globalStats.stat_reset_timestamp = GetCurrentTimestamp(); + + /* * Try to open the status file. If it doesn't exist, the backends simply * return zero for anything and the collector simply starts from scratch * with empty counters. @@ -4052,6 +4060,8 @@ pgstat_recv_resetcounter(PgStat_MsgResetcounter *msg, int len) dbentry->n_tuples_deleted = 0; dbentry->last_autovac_time = 0; + dbentry->stat_reset_timestamp = GetCurrentTimestamp(); + memset(&hash_ctl, 0, sizeof(hash_ctl)); hash_ctl.keysize = sizeof(Oid); hash_ctl.entrysize = sizeof(PgStat_StatTabEntry); @@ -4083,6 +4093,7 @@ pgstat_recv_resetsharedcounter(PgStat_MsgResetsharedcounter *msg, int len) { /* Reset the global background writer statistics for the cluster. */ memset(&globalStats, 0, sizeof(globalStats)); + globalStats.stat_reset_timestamp = GetCurrentTimestamp(); } /* @@ -4107,6 +4118,8 @@ pgstat_recv_resetsinglecounter(PgStat_MsgResetsinglecounter *msg, int len) if (!dbentry) return; + /* Set the reset timestamp for the whole database */ + dbentry->stat_reset_timestamp = GetCurrentTimestamp(); /* Remove object if it exists, ignore it if not */ if (msg->m_resettype == RESET_TABLE) diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 8f77d1bfc9..997af5bf07 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -4284,6 +4284,14 @@ sigusr1_handler(SIGNAL_ARGS) WalReceiverPID = StartWalReceiver(); } + if (CheckPromoteSignal() && StartupPID != 0 && + (pmState == PM_STARTUP || pmState == PM_RECOVERY || + pmState == PM_HOT_STANDBY || pmState == PM_WAIT_READONLY)) + { + /* Tell startup process to finish recovery */ + signal_child(StartupPID, SIGUSR2); + } + PG_SETMASK(&UnBlockSig); errno = save_errno; diff --git a/src/backend/replication/README b/src/backend/replication/README index 9c2e0d8e97..744ddc7fe8 100644 --- a/src/backend/replication/README +++ b/src/backend/replication/README @@ -45,10 +45,10 @@ to fetch more WAL (if streaming replication is configured). Walreceiver is a postmaster subprocess, so the startup process can't fork it directly. Instead, it sends a signal to postmaster, asking postmaster to launch it. Before that, however, startup process fills in WalRcvData->conninfo, -and initializes the starting point in WalRcvData->receivedUpTo. +and initializes the starting point in WalRcvData->receivedUpto. As walreceiver receives WAL from the master server, and writes and flushes -it to disk (in pg_xlog), it updates WalRcvData->receivedUpTo. Startup process +it to disk (in pg_xlog), it updates WalRcvData->receivedUpto. Startup process polls that to know how far it can proceed with WAL replay. Walsender IPC diff --git a/src/backend/replication/basebackup.c b/src/backend/replication/basebackup.c index d94b61f23e..db4cc640e4 100644 --- a/src/backend/replication/basebackup.c +++ b/src/backend/replication/basebackup.c @@ -37,6 +37,7 @@ typedef struct const char *label; bool progress; bool fastcheckpoint; + bool nowait; bool includewal; } basebackup_options; @@ -173,7 +174,7 @@ perform_base_backup(basebackup_options *opt, DIR *tblspcdir) } PG_END_ENSURE_ERROR_CLEANUP(base_backup_cleanup, (Datum) 0); - endptr = do_pg_stop_backup(labelfile); + endptr = do_pg_stop_backup(labelfile, !opt->nowait); if (opt->includewal) { @@ -260,6 +261,7 @@ parse_basebackup_options(List *options, basebackup_options *opt) bool o_label = false; bool o_progress = false; bool o_fast = false; + bool o_nowait = false; bool o_wal = false; MemSet(opt, 0, sizeof(*opt)); @@ -294,6 +296,15 @@ parse_basebackup_options(List *options, basebackup_options *opt) opt->fastcheckpoint = true; o_fast = true; } + else if (strcmp(defel->defname, "nowait") == 0) + { + if (o_nowait) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("duplicate option \"%s\"", defel->defname))); + opt->nowait = true; + o_nowait = true; + } else if (strcmp(defel->defname, "wal") == 0) { if (o_wal) diff --git a/src/backend/replication/repl_gram.y b/src/backend/replication/repl_gram.y index e1a4a51a0e..4930ad1d09 100644 --- a/src/backend/replication/repl_gram.y +++ b/src/backend/replication/repl_gram.y @@ -71,6 +71,7 @@ Node *replication_parse_result; %token K_LABEL %token K_PROGRESS %token K_FAST +%token K_NOWAIT %token K_WAL %token K_START_REPLICATION @@ -107,7 +108,7 @@ identify_system: ; /* - * BASE_BACKUP [LABEL '<label>'] [PROGRESS] [FAST] [WAL] + * BASE_BACKUP [LABEL '<label>'] [PROGRESS] [FAST] [WAL] [NOWAIT] */ base_backup: K_BASE_BACKUP base_backup_opt_list @@ -142,6 +143,11 @@ base_backup_opt: $$ = makeDefElem("wal", (Node *)makeInteger(TRUE)); } + | K_NOWAIT + { + $$ = makeDefElem("nowait", + (Node *)makeInteger(TRUE)); + } ; /* diff --git a/src/backend/replication/repl_scanner.l b/src/backend/replication/repl_scanner.l index 87e77d975a..f5b260119e 100644 --- a/src/backend/replication/repl_scanner.l +++ b/src/backend/replication/repl_scanner.l @@ -60,6 +60,7 @@ BASE_BACKUP { return K_BASE_BACKUP; } FAST { return K_FAST; } IDENTIFY_SYSTEM { return K_IDENTIFY_SYSTEM; } LABEL { return K_LABEL; } +NOWAIT { return K_NOWAIT; } PROGRESS { return K_PROGRESS; } WAL { return K_WAL; } START_REPLICATION { return K_START_REPLICATION; } diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c index 7005307dc2..ee09468db1 100644 --- a/src/backend/replication/walreceiver.c +++ b/src/backend/replication/walreceiver.c @@ -12,7 +12,7 @@ * in the primary server), and then keeps receiving XLOG records and * writing them to the disk as long as the connection is alive. As XLOG * records are received and flushed to disk, it updates the - * WalRcv->receivedUpTo variable in shared memory, to inform the startup + * WalRcv->receivedUpto variable in shared memory, to inform the startup * process of how far it can proceed with XLOG replay. * * Normal termination is by SIGTERM, which instructs the walreceiver to @@ -38,6 +38,7 @@ #include <signal.h> #include <unistd.h> +#include "access/transam.h" #include "access/xlog_internal.h" #include "libpq/pqsignal.h" #include "miscadmin.h" @@ -45,6 +46,7 @@ #include "replication/walreceiver.h" #include "storage/ipc.h" #include "storage/pmsignal.h" +#include "storage/procarray.h" #include "utils/builtins.h" #include "utils/guc.h" #include "utils/memutils.h" @@ -54,6 +56,10 @@ /* Global variable to indicate if this process is a walreceiver process */ bool am_walreceiver; +/* GUC variable */ +int wal_receiver_status_interval; +bool hot_standby_feedback; + /* libpqreceiver hooks to these when loaded */ walrcv_connect_type walrcv_connect = NULL; walrcv_receive_type walrcv_receive = NULL; @@ -88,6 +94,8 @@ static struct XLogRecPtr Flush; /* last byte + 1 flushed in the standby */ } LogstreamResult; +static StandbyReplyMessage reply_message; + /* * About SIGTERM handling: * @@ -113,7 +121,8 @@ static void DisableWalRcvImmediateExit(void); static void WalRcvDie(int code, Datum arg); static void XLogWalRcvProcessMsg(unsigned char type, char *buf, Size len); static void XLogWalRcvWrite(char *buf, Size nbytes, XLogRecPtr recptr); -static void XLogWalRcvFlush(void); +static void XLogWalRcvFlush(bool dying); +static void XLogWalRcvSendReply(void); /* Signal handlers */ static void WalRcvSigHupHandler(SIGNAL_ARGS); @@ -306,11 +315,22 @@ WalReceiverMain(void) while (walrcv_receive(0, &type, &buf, &len)) XLogWalRcvProcessMsg(type, buf, len); + /* Let the master know that we received some data. */ + XLogWalRcvSendReply(); + /* * If we've written some records, flush them to disk and let the * startup process know about them. */ - XLogWalRcvFlush(); + XLogWalRcvFlush(false); + } + else + { + /* + * We didn't receive anything new, but send a status update to + * the master anyway, to report any progress in applying WAL. + */ + XLogWalRcvSendReply(); } } } @@ -325,7 +345,7 @@ WalRcvDie(int code, Datum arg) volatile WalRcvData *walrcv = WalRcv; /* Ensure that all WAL records received are flushed to disk */ - XLogWalRcvFlush(); + XLogWalRcvFlush(true); SpinLockAcquire(&walrcv->mutex); Assert(walrcv->walRcvState == WALRCV_RUNNING || @@ -444,7 +464,7 @@ XLogWalRcvWrite(char *buf, Size nbytes, XLogRecPtr recptr) */ if (recvFile >= 0) { - XLogWalRcvFlush(); + XLogWalRcvFlush(false); /* * XLOG segment files will be re-read by recovery in startup @@ -514,9 +534,14 @@ XLogWalRcvWrite(char *buf, Size nbytes, XLogRecPtr recptr) } } -/* Flush the log to disk */ +/* + * Flush the log to disk. + * + * If we're in the midst of dying, it's unwise to do anything that might throw + * an error, so we skip sending a reply in that case. + */ static void -XLogWalRcvFlush(void) +XLogWalRcvFlush(bool dying) { if (XLByteLT(LogstreamResult.Flush, LogstreamResult.Write)) { @@ -546,5 +571,88 @@ XLogWalRcvFlush(void) LogstreamResult.Write.xrecoff); set_ps_display(activitymsg, false); } + + /* Also let the master know that we made some progress */ + if (!dying) + XLogWalRcvSendReply(); } } + +/* + * Send reply message to primary, indicating our current XLOG positions and + * the current time. + */ +static void +XLogWalRcvSendReply(void) +{ + char buf[sizeof(StandbyReplyMessage) + 1]; + TimestampTz now; + + /* + * If the user doesn't want status to be reported to the master, be sure + * to exit before doing anything at all. + */ + if (wal_receiver_status_interval <= 0) + return; + + /* Get current timestamp. */ + now = GetCurrentTimestamp(); + + /* + * We can compare the write and flush positions to the last message we + * sent without taking any lock, but the apply position requires a spin + * lock, so we don't check that unless something else has changed or 10 + * seconds have passed. This means that the apply log position will + * appear, from the master's point of view, to lag slightly, but since + * this is only for reporting purposes and only on idle systems, that's + * probably OK. + */ + if (XLByteEQ(reply_message.write, LogstreamResult.Write) + && XLByteEQ(reply_message.flush, LogstreamResult.Flush) + && !TimestampDifferenceExceeds(reply_message.sendTime, now, + wal_receiver_status_interval * 1000)) + return; + + /* Construct a new message */ + reply_message.write = LogstreamResult.Write; + reply_message.flush = LogstreamResult.Flush; + reply_message.apply = GetXLogReplayRecPtr(); + reply_message.sendTime = now; + + /* + * Get the OldestXmin and its associated epoch + */ + if (hot_standby_feedback && HotStandbyActive()) + { + TransactionId nextXid; + uint32 nextEpoch; + + reply_message.xmin = GetOldestXmin(true, false); + + /* + * Get epoch and adjust if nextXid and oldestXmin are different + * sides of the epoch boundary. + */ + GetNextXidAndEpoch(&nextXid, &nextEpoch); + if (nextXid < reply_message.xmin) + nextEpoch--; + reply_message.epoch = nextEpoch; + } + else + { + reply_message.xmin = InvalidTransactionId; + reply_message.epoch = 0; + } + + elog(DEBUG2, "sending write %X/%X flush %X/%X apply %X/%X xmin %u epoch %u", + reply_message.write.xlogid, reply_message.write.xrecoff, + reply_message.flush.xlogid, reply_message.flush.xrecoff, + reply_message.apply.xlogid, reply_message.apply.xrecoff, + reply_message.xmin, + reply_message.epoch); + + /* Prepend with the message type and send it. */ + buf[0] = 'r'; + memcpy(&buf[1], &reply_message, sizeof(StandbyReplyMessage)); + walrcv_send(buf, sizeof(StandbyReplyMessage) + 1); +} diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c index 78963c1e6b..a6a7a1425b 100644 --- a/src/backend/replication/walsender.c +++ b/src/backend/replication/walsender.c @@ -39,6 +39,7 @@ #include "funcapi.h" #include "access/xlog_internal.h" +#include "access/transam.h" #include "catalog/pg_type.h" #include "libpq/libpq.h" #include "libpq/pqformat.h" @@ -51,6 +52,8 @@ #include "storage/fd.h" #include "storage/ipc.h" #include "storage/pmsignal.h" +#include "storage/proc.h" +#include "storage/procarray.h" #include "tcop/tcopprot.h" #include "utils/builtins.h" #include "utils/guc.h" @@ -87,6 +90,11 @@ static uint32 sendOff = 0; */ static XLogRecPtr sentPtr = {0, 0}; +/* + * Buffer for processing reply messages. + */ +static StringInfoData reply_message; + /* Flags set by signal handlers for later service in main loop */ static volatile sig_atomic_t got_SIGHUP = false; volatile sig_atomic_t walsender_shutdown_requested = false; @@ -106,9 +114,10 @@ static void InitWalSnd(void); static void WalSndHandshake(void); static void WalSndKill(int code, Datum arg); static bool XLogSend(char *msgbuf, bool *caughtup); -static void CheckClosedConnection(void); static void IdentifySystem(void); static void StartReplication(StartReplicationCmd * cmd); +static void ProcessStandbyReplyMessage(void); +static void ProcessRepliesIfAny(void); /* Main entry point for walsender process */ @@ -442,7 +451,7 @@ HandleReplicationCommand(const char *cmd_string) * Check if the remote end has closed the connection. */ static void -CheckClosedConnection(void) +ProcessRepliesIfAny(void) { unsigned char firstchar; int r; @@ -466,6 +475,13 @@ CheckClosedConnection(void) switch (firstchar) { /* + * 'd' means a standby reply wrapped in a CopyData packet. + */ + case 'd': + ProcessStandbyReplyMessage(); + break; + + /* * 'X' means that the standby is closing down the socket. */ case 'X': @@ -479,6 +495,130 @@ CheckClosedConnection(void) } } +/* + * Process a status update message received from standby. + */ +static void +ProcessStandbyReplyMessage(void) +{ + StandbyReplyMessage reply; + char msgtype; + TransactionId newxmin = InvalidTransactionId; + + resetStringInfo(&reply_message); + + /* + * Read the message contents. + */ + if (pq_getmessage(&reply_message, 0)) + { + ereport(COMMERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("unexpected EOF on standby connection"))); + proc_exit(0); + } + + /* + * Check message type from the first byte. At the moment, there is only + * one type. + */ + msgtype = pq_getmsgbyte(&reply_message); + if (msgtype != 'r') + { + ereport(COMMERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("unexpected message type %c", msgtype))); + proc_exit(0); + } + + pq_copymsgbytes(&reply_message, (char *) &reply, sizeof(StandbyReplyMessage)); + + elog(DEBUG2, "write %X/%X flush %X/%X apply %X/%X xmin %u epoch %u", + reply.write.xlogid, reply.write.xrecoff, + reply.flush.xlogid, reply.flush.xrecoff, + reply.apply.xlogid, reply.apply.xrecoff, + reply.xmin, + reply.epoch); + + /* + * Update shared state for this WalSender process + * based on reply data from standby. + */ + { + /* use volatile pointer to prevent code rearrangement */ + volatile WalSnd *walsnd = MyWalSnd; + + SpinLockAcquire(&walsnd->mutex); + walsnd->write = reply.write; + walsnd->flush = reply.flush; + walsnd->apply = reply.apply; + SpinLockRelease(&walsnd->mutex); + } + + /* + * Update the WalSender's proc xmin to allow it to be visible + * to snapshots. This will hold back the removal of dead rows + * and thereby prevent the generation of cleanup conflicts + * on the standby server. + */ + if (TransactionIdIsValid(reply.xmin)) + { + TransactionId nextXid; + uint32 nextEpoch; + bool epochOK = false; + + GetNextXidAndEpoch(&nextXid, &nextEpoch); + + /* + * Epoch of oldestXmin should be same as standby or + * if the counter has wrapped, then one less than reply. + */ + if (reply.xmin <= nextXid) + { + if (reply.epoch == nextEpoch) + epochOK = true; + } + else + { + if (nextEpoch > 0 && reply.epoch == nextEpoch - 1) + epochOK = true; + } + + /* + * Feedback from standby must not go backwards, nor should it go + * forwards further than our most recent xid. + */ + if (epochOK && TransactionIdPrecedesOrEquals(reply.xmin, nextXid)) + { + if (!TransactionIdIsValid(MyProc->xmin)) + { + TransactionId oldestXmin = GetOldestXmin(true, true); + if (TransactionIdPrecedes(oldestXmin, reply.xmin)) + newxmin = reply.xmin; + else + newxmin = oldestXmin; + } + else + { + if (TransactionIdPrecedes(MyProc->xmin, reply.xmin)) + newxmin = reply.xmin; + else + newxmin = MyProc->xmin; /* stay the same */ + } + } + } + + /* + * Grab the ProcArrayLock to set xmin, or invalidate for bad reply + */ + if (MyProc->xmin != newxmin) + { + LWLockAcquire(ProcArrayLock, LW_SHARED); + MyProc->xmin = newxmin; + LWLockRelease(ProcArrayLock); + } +} + /* Main loop of walsender process */ static int WalSndLoop(void) @@ -493,6 +633,12 @@ WalSndLoop(void) */ output_message = palloc(1 + sizeof(WalDataMessageHeader) + MAX_SEND_SIZE); + /* + * Allocate buffer that will be used for processing reply messages. As + * above, do this just once to reduce palloc overhead. + */ + initStringInfo(&reply_message); + /* Loop forever, unless we get an error */ for (;;) { @@ -518,6 +664,7 @@ WalSndLoop(void) { if (!XLogSend(output_message, &caughtup)) break; + ProcessRepliesIfAny(); if (caughtup) walsender_shutdown_requested = true; } @@ -561,9 +708,6 @@ WalSndLoop(void) WaitLatchOrSocket(&MyWalSnd->latch, MyProcPort->sock, WalSndDelay * 1000L); } - - /* Check if the connection was closed */ - CheckClosedConnection(); } else { @@ -574,6 +718,7 @@ WalSndLoop(void) /* Update our state to indicate if we're behind or not */ WalSndSetState(caughtup ? WALSNDSTATE_STREAMING : WALSNDSTATE_CATCHUP); + ProcessRepliesIfAny(); } /* @@ -1104,7 +1249,7 @@ WalSndGetStateString(WalSndState state) Datum pg_stat_get_wal_senders(PG_FUNCTION_ARGS) { -#define PG_STAT_GET_WAL_SENDERS_COLS 3 +#define PG_STAT_GET_WAL_SENDERS_COLS 6 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; TupleDesc tupdesc; Tuplestorestate *tupstore; @@ -1141,8 +1286,11 @@ pg_stat_get_wal_senders(PG_FUNCTION_ARGS) { /* use volatile pointer to prevent code rearrangement */ volatile WalSnd *walsnd = &WalSndCtl->walsnds[i]; - char sent_location[MAXFNAMELEN]; + char location[MAXFNAMELEN]; XLogRecPtr sentPtr; + XLogRecPtr write; + XLogRecPtr flush; + XLogRecPtr apply; WalSndState state; Datum values[PG_STAT_GET_WAL_SENDERS_COLS]; bool nulls[PG_STAT_GET_WAL_SENDERS_COLS]; @@ -1153,13 +1301,14 @@ pg_stat_get_wal_senders(PG_FUNCTION_ARGS) SpinLockAcquire(&walsnd->mutex); sentPtr = walsnd->sentPtr; state = walsnd->state; + write = walsnd->write; + flush = walsnd->flush; + apply = walsnd->apply; SpinLockRelease(&walsnd->mutex); - snprintf(sent_location, sizeof(sent_location), "%X/%X", - sentPtr.xlogid, sentPtr.xrecoff); - memset(nulls, 0, sizeof(nulls)); values[0] = Int32GetDatum(walsnd->pid); + if (!superuser()) { /* @@ -1168,11 +1317,35 @@ pg_stat_get_wal_senders(PG_FUNCTION_ARGS) */ nulls[1] = true; nulls[2] = true; + nulls[3] = true; + nulls[4] = true; + nulls[5] = true; } else { values[1] = CStringGetTextDatum(WalSndGetStateString(state)); - values[2] = CStringGetTextDatum(sent_location); + + snprintf(location, sizeof(location), "%X/%X", + sentPtr.xlogid, sentPtr.xrecoff); + values[2] = CStringGetTextDatum(location); + + if (write.xlogid == 0 && write.xrecoff == 0) + nulls[3] = true; + snprintf(location, sizeof(location), "%X/%X", + write.xlogid, write.xrecoff); + values[3] = CStringGetTextDatum(location); + + if (flush.xlogid == 0 && flush.xrecoff == 0) + nulls[4] = true; + snprintf(location, sizeof(location), "%X/%X", + flush.xlogid, flush.xrecoff); + values[4] = CStringGetTextDatum(location); + + if (apply.xlogid == 0 && apply.xrecoff == 0) + nulls[5] = true; + snprintf(location, sizeof(location), "%X/%X", + apply.xlogid, apply.xrecoff); + values[5] = CStringGetTextDatum(location); } tuplestore_putvalues(tupstore, tupdesc, values, nulls); diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 99c397b4f2..fecc4e27fa 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -143,7 +143,7 @@ InsertRule(char *rulname, /* If replacing, get rid of old dependencies and make new ones */ if (is_update) - deleteDependencyRecordsFor(RewriteRelationId, rewriteObjectId); + deleteDependencyRecordsFor(RewriteRelationId, rewriteObjectId, false); /* * Install dependency on rule's relation to ensure it will go away on diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index c12d4cb6a3..c6491e1519 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -747,6 +747,7 @@ rewriteTargetListIU(Query *parsetree, Relation target_relation, attrno, att_tup->atttypid, att_tup->atttypmod, + att_tup->attcollation, 0); new_tle = makeTargetEntry((Expr *) new_expr, @@ -1127,6 +1128,7 @@ rewriteTargetListUD(Query *parsetree, RangeTblEntry *target_rte, SelfItemPointerAttributeNumber, TIDOID, -1, + InvalidOid, 0); attrname = "ctid"; diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c index 8b36df4759..2473881e8f 100644 --- a/src/backend/storage/ipc/procarray.c +++ b/src/backend/storage/ipc/procarray.c @@ -1034,7 +1034,9 @@ GetOldestXmin(bool allDbs, bool ignoreVacuum) if (ignoreVacuum && (proc->vacuumFlags & PROC_IN_VACUUM)) continue; - if (allDbs || proc->databaseId == MyDatabaseId) + if (allDbs || + proc->databaseId == MyDatabaseId || + proc->databaseId == 0) /* include WalSender */ { /* Fetch xid just once - see GetNewTransactionId */ TransactionId xid = proc->xid; @@ -1066,28 +1068,35 @@ GetOldestXmin(bool allDbs, bool ignoreVacuum) */ TransactionId kaxmin = KnownAssignedXidsGetOldestXmin(); + LWLockRelease(ProcArrayLock); + if (TransactionIdIsNormal(kaxmin) && TransactionIdPrecedes(kaxmin, result)) result = kaxmin; } + else + { + /* + * No other information needed, so release the lock immediately. + */ + LWLockRelease(ProcArrayLock); - LWLockRelease(ProcArrayLock); - - /* - * Compute the cutoff XID, being careful not to generate a "permanent" - * XID. - * - * vacuum_defer_cleanup_age provides some additional "slop" for the - * benefit of hot standby queries on slave servers. This is quick and - * dirty, and perhaps not all that useful unless the master has a - * predictable transaction rate, but it's what we've got. Note that we - * are assuming vacuum_defer_cleanup_age isn't large enough to cause - * wraparound --- so guc.c should limit it to no more than the - * xidStopLimit threshold in varsup.c. - */ - result -= vacuum_defer_cleanup_age; - if (!TransactionIdIsNormal(result)) - result = FirstNormalTransactionId; + /* + * Compute the cutoff XID, being careful not to generate a "permanent" + * XID. We need do this only on the primary, never on standby. + * + * vacuum_defer_cleanup_age provides some additional "slop" for the + * benefit of hot standby queries on slave servers. This is quick and + * dirty, and perhaps not all that useful unless the master has a + * predictable transaction rate, but it's what we've got. Note that we + * are assuming vacuum_defer_cleanup_age isn't large enough to cause + * wraparound --- so guc.c should limit it to no more than the + * xidStopLimit threshold in varsup.c. + */ + result -= vacuum_defer_cleanup_age; + if (!TransactionIdIsNormal(result)) + result = FirstNormalTransactionId; + } return result; } diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c index b66fd8678c..e2d79e20b4 100644 --- a/src/backend/storage/lmgr/predicate.c +++ b/src/backend/storage/lmgr/predicate.c @@ -1018,7 +1018,6 @@ InitPredicateLocks(void) * PredicateLockShmemSize! */ max_table_size = (MaxBackends + max_prepared_xacts); - init_table_size = max_table_size / 2; /* * Allocate a list to hold information on transactions participating in @@ -1029,7 +1028,6 @@ InitPredicateLocks(void) * be summarized for storage in SLRU and the "dummy" transaction. */ max_table_size *= 10; - init_table_size *= 10; PredXact = ShmemInitStruct("PredXactList", PredXactListDataSize, @@ -1092,7 +1090,7 @@ InitPredicateLocks(void) hash_flags = (HASH_ELEM | HASH_FUNCTION); SerializableXidHash = ShmemInitHash("SERIALIZABLEXID hash", - init_table_size, + max_table_size, max_table_size, &info, hash_flags); @@ -1119,7 +1117,7 @@ InitPredicateLocks(void) SHMQueueInit(&RWConflictPool->availableList); requestSize = mul_size((Size) max_table_size, - PredXactListElementDataSize); + RWConflictDataSize); RWConflictPool->element = ShmemAlloc(requestSize); if (RWConflictPool->element == NULL) ereport(ERROR, @@ -1190,11 +1188,17 @@ PredicateLockShmemSize(void) size = add_size(size, hash_estimate_size(max_table_size, sizeof(SERIALIZABLEXID))); + /* rw-conflict pool */ + max_table_size *= 5; + size = add_size(size, RWConflictPoolHeaderDataSize); + size = add_size(size, mul_size((Size) max_table_size, + RWConflictDataSize)); + /* Head for list of finished serializable transactions. */ size = add_size(size, sizeof(SHM_QUEUE)); /* Shared memory structures for SLRU tracking of old committed xids. */ - size = add_size(size, sizeof(OldSerXidControl)); + size = add_size(size, sizeof(OldSerXidControlData)); size = add_size(size, SimpleLruShmemSize(NUM_OLDSERXID_BUFFERS, 0)); return size; @@ -1589,10 +1593,10 @@ RegisterPredicateLockingXid(const TransactionId xid) &sxidtag, HASH_ENTER, &found); if (!sxid) + /* This should not be possible, based on allocation. */ ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), - errmsg("out of shared memory"), - errhint("You might need to increase max_predicate_locks_per_transaction."))); + errmsg("out of shared memory"))); Assert(!found); @@ -1870,7 +1874,7 @@ DeleteChildTargetLocks(const PREDICATELOCKTARGETTAG *newtargettag) * thresholds are, either making it proportional to the number of * tuples in a page & pages in a relation, or at least making it a * GUC. Currently the threshold is 3 for a page lock, and - * max_predicate_locks_per_transaction/2 for a relation lock, chosen + * max_pred_locks_per_transaction/2 for a relation lock, chosen * entirely arbitrarily (and without benchmarking). */ static int @@ -2059,7 +2063,7 @@ CreatePredicateLock(const PREDICATELOCKTARGETTAG *targettag, ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of shared memory"), - errhint("You might need to increase max_predicate_locks_per_transaction."))); + errhint("You might need to increase max_pred_locks_per_transaction."))); if (!found) { SHMQueueInit(&(target->predicateLocks)); @@ -2078,7 +2082,7 @@ CreatePredicateLock(const PREDICATELOCKTARGETTAG *targettag, ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of shared memory"), - errhint("You might need to increase max_predicate_locks_per_transaction."))); + errhint("You might need to increase max_pred_locks_per_transaction."))); if (!found) { @@ -2337,7 +2341,7 @@ PredicateLockTupleRowVersionLink(const Relation relation, ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of shared memory"), - errhint("You might need to increase max_predicate_locks_per_transaction."))); + errhint("You might need to increase max_pred_locks_per_transaction."))); if (!found) { SHMQueueInit(&(newtarget->predicateLocks)); @@ -3333,7 +3337,7 @@ ReleaseOneSerializableXact(SERIALIZABLEXACT *sxact, bool partial, ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("out of shared memory"), - errhint("You might need to increase max_predicate_locks_per_transaction."))); + errhint("You might need to increase max_pred_locks_per_transaction."))); if (found) { if (predlock->commitSeqNo < sxact->commitSeqNo) diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index af2eba01d6..67aa5e2ab6 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -26,12 +26,14 @@ #include "commands/async.h" #include "commands/cluster.h" #include "commands/comment.h" +#include "commands/collationcmds.h" #include "commands/conversioncmds.h" #include "commands/copy.h" #include "commands/dbcommands.h" #include "commands/defrem.h" #include "commands/discard.h" #include "commands/explain.h" +#include "commands/extension.h" #include "commands/lockcmds.h" #include "commands/portalcmds.h" #include "commands/prepare.h" @@ -210,6 +212,9 @@ check_xact_readonly(Node *parsetree) case T_ReassignOwnedStmt: case T_AlterTSDictionaryStmt: case T_AlterTSConfigurationStmt: + case T_CreateExtensionStmt: + case T_AlterExtensionStmt: + case T_AlterExtensionContentsStmt: case T_CreateFdwStmt: case T_AlterFdwStmt: case T_DropFdwStmt: @@ -594,6 +599,18 @@ standard_ProcessUtility(Node *parsetree, AlterTableSpaceOptions((AlterTableSpaceOptionsStmt *) parsetree); break; + case T_CreateExtensionStmt: + CreateExtension((CreateExtensionStmt *) parsetree); + break; + + case T_AlterExtensionStmt: + ExecAlterExtensionStmt((AlterExtensionStmt *) parsetree); + break; + + case T_AlterExtensionContentsStmt: + ExecAlterExtensionContentsStmt((AlterExtensionContentsStmt *) parsetree); + break; + case T_CreateFdwStmt: CreateForeignDataWrapper((CreateFdwStmt *) parsetree); break; @@ -649,6 +666,10 @@ standard_ProcessUtility(Node *parsetree, RemoveTypes(stmt); break; + case OBJECT_COLLATION: + DropCollationsCommand(stmt); + break; + case OBJECT_CONVERSION: DropConversionsCommand(stmt); break; @@ -673,6 +694,10 @@ standard_ProcessUtility(Node *parsetree, RemoveTSConfigurations(stmt); break; + case OBJECT_EXTENSION: + RemoveExtensions(stmt); + break; + default: elog(ERROR, "unrecognized drop object type: %d", (int) stmt->removeType); @@ -864,6 +889,10 @@ standard_ProcessUtility(Node *parsetree, Assert(stmt->args == NIL); DefineTSConfiguration(stmt->defnames, stmt->definition); break; + case OBJECT_COLLATION: + Assert(stmt->args == NIL); + DefineCollation(stmt->defnames, stmt->definition); + break; default: elog(ERROR, "unrecognized define stmt type: %d", (int) stmt->kind); @@ -1412,6 +1441,126 @@ QueryReturnsTuples(Query *parsetree) /* + * AlterObjectTypeCommandTag + * helper function for CreateCommandTag + * + * This covers most cases where ALTER is used with an ObjectType enum. + */ +static const char * +AlterObjectTypeCommandTag(ObjectType objtype) +{ + const char *tag; + + switch (objtype) + { + case OBJECT_AGGREGATE: + tag = "ALTER AGGREGATE"; + break; + case OBJECT_ATTRIBUTE: + tag = "ALTER TYPE"; + break; + case OBJECT_CAST: + tag = "ALTER CAST"; + break; + case OBJECT_COLLATION: + tag = "ALTER COLLATION"; + break; + case OBJECT_COLUMN: + tag = "ALTER TABLE"; + break; + case OBJECT_CONSTRAINT: + tag = "ALTER TABLE"; + break; + case OBJECT_CONVERSION: + tag = "ALTER CONVERSION"; + break; + case OBJECT_DATABASE: + tag = "ALTER DATABASE"; + break; + case OBJECT_DOMAIN: + tag = "ALTER DOMAIN"; + break; + case OBJECT_EXTENSION: + tag = "ALTER EXTENSION"; + break; + case OBJECT_FDW: + tag = "ALTER FOREIGN DATA WRAPPER"; + break; + case OBJECT_FOREIGN_SERVER: + tag = "ALTER SERVER"; + break; + case OBJECT_FOREIGN_TABLE: + tag = "ALTER FOREIGN TABLE"; + break; + case OBJECT_FUNCTION: + tag = "ALTER FUNCTION"; + break; + case OBJECT_INDEX: + tag = "ALTER INDEX"; + break; + case OBJECT_LANGUAGE: + tag = "ALTER LANGUAGE"; + break; + case OBJECT_LARGEOBJECT: + tag = "ALTER LARGE OBJECT"; + break; + case OBJECT_OPCLASS: + tag = "ALTER OPERATOR CLASS"; + break; + case OBJECT_OPERATOR: + tag = "ALTER OPERATOR"; + break; + case OBJECT_OPFAMILY: + tag = "ALTER OPERATOR FAMILY"; + break; + case OBJECT_ROLE: + tag = "ALTER ROLE"; + break; + case OBJECT_RULE: + tag = "ALTER RULE"; + break; + case OBJECT_SCHEMA: + tag = "ALTER SCHEMA"; + break; + case OBJECT_SEQUENCE: + tag = "ALTER SEQUENCE"; + break; + case OBJECT_TABLE: + tag = "ALTER TABLE"; + break; + case OBJECT_TABLESPACE: + tag = "ALTER TABLESPACE"; + break; + case OBJECT_TRIGGER: + tag = "ALTER TRIGGER"; + break; + case OBJECT_TSCONFIGURATION: + tag = "ALTER TEXT SEARCH CONFIGURATION"; + break; + case OBJECT_TSDICTIONARY: + tag = "ALTER TEXT SEARCH DICTIONARY"; + break; + case OBJECT_TSPARSER: + tag = "ALTER TEXT SEARCH PARSER"; + break; + case OBJECT_TSTEMPLATE: + tag = "ALTER TEXT SEARCH TEMPLATE"; + break; + case OBJECT_TYPE: + tag = "ALTER TYPE"; + break; + case OBJECT_VIEW: + tag = "ALTER VIEW"; + break; + default: + tag = "???"; + break; + } + + return tag; +} + +/* * CreateCommandTag * utility to get a string representation of the command operation, * given either a raw (un-analyzed) parsetree or a planned query. @@ -1544,6 +1693,18 @@ CreateCommandTag(Node *parsetree) tag = "ALTER TABLESPACE"; break; + case T_CreateExtensionStmt: + tag = "CREATE EXTENSION"; + break; + + case T_AlterExtensionStmt: + tag = "ALTER EXTENSION"; + break; + + case T_AlterExtensionContentsStmt: + tag = "ALTER EXTENSION"; + break; + case T_CreateFdwStmt: tag = "CREATE FOREIGN DATA WRAPPER"; break; @@ -1605,6 +1766,9 @@ CreateCommandTag(Node *parsetree) case OBJECT_DOMAIN: tag = "DROP DOMAIN"; break; + case OBJECT_COLLATION: + tag = "DROP COLLATION"; + break; case OBJECT_CONVERSION: tag = "DROP CONVERSION"; break; @@ -1626,6 +1790,9 @@ CreateCommandTag(Node *parsetree) case OBJECT_FOREIGN_TABLE: tag = "DROP FOREIGN TABLE"; break; + case OBJECT_EXTENSION: + tag = "DROP EXTENSION"; + break; default: tag = "???"; } @@ -1648,232 +1815,19 @@ CreateCommandTag(Node *parsetree) break; case T_RenameStmt: - switch (((RenameStmt *) parsetree)->renameType) - { - case OBJECT_AGGREGATE: - tag = "ALTER AGGREGATE"; - break; - case OBJECT_CONVERSION: - tag = "ALTER CONVERSION"; - break; - case OBJECT_DATABASE: - tag = "ALTER DATABASE"; - break; - case OBJECT_FUNCTION: - tag = "ALTER FUNCTION"; - break; - case OBJECT_INDEX: - tag = "ALTER INDEX"; - break; - case OBJECT_LANGUAGE: - tag = "ALTER LANGUAGE"; - break; - case OBJECT_OPCLASS: - tag = "ALTER OPERATOR CLASS"; - break; - case OBJECT_OPFAMILY: - tag = "ALTER OPERATOR FAMILY"; - break; - case OBJECT_ROLE: - tag = "ALTER ROLE"; - break; - case OBJECT_SCHEMA: - tag = "ALTER SCHEMA"; - break; - case OBJECT_SEQUENCE: - tag = "ALTER SEQUENCE"; - break; - case OBJECT_COLUMN: - { - RenameStmt *stmt = (RenameStmt *) parsetree; - if (stmt->relationType == OBJECT_FOREIGN_TABLE) - tag = "ALTER FOREIGN TABLE"; - else - tag = "ALTER TABLE"; - } - break; - case OBJECT_TABLE: - tag = "ALTER TABLE"; - break; - case OBJECT_TABLESPACE: - tag = "ALTER TABLESPACE"; - break; - case OBJECT_TRIGGER: - tag = "ALTER TRIGGER"; - break; - case OBJECT_VIEW: - tag = "ALTER VIEW"; - break; - case OBJECT_FOREIGN_TABLE: - tag = "ALTER FOREIGN TABLE"; - break; - case OBJECT_TSPARSER: - tag = "ALTER TEXT SEARCH PARSER"; - break; - case OBJECT_TSDICTIONARY: - tag = "ALTER TEXT SEARCH DICTIONARY"; - break; - case OBJECT_TSTEMPLATE: - tag = "ALTER TEXT SEARCH TEMPLATE"; - break; - case OBJECT_TSCONFIGURATION: - tag = "ALTER TEXT SEARCH CONFIGURATION"; - break; - case OBJECT_ATTRIBUTE: - case OBJECT_TYPE: - tag = "ALTER TYPE"; - break; - default: - tag = "???"; - break; - } + tag = AlterObjectTypeCommandTag(((RenameStmt *) parsetree)->renameType); break; case T_AlterObjectSchemaStmt: - switch (((AlterObjectSchemaStmt *) parsetree)->objectType) - { - case OBJECT_AGGREGATE: - tag = "ALTER AGGREGATE"; - break; - case OBJECT_CONVERSION: - tag = "ALTER CONVERSION"; - break; - case OBJECT_DOMAIN: - tag = "ALTER DOMAIN"; - break; - case OBJECT_OPERATOR: - tag = "ALTER OPERATOR"; - break; - case OBJECT_OPCLASS: - tag = "ALTER OPERATOR CLASS"; - break; - case OBJECT_OPFAMILY: - tag = "ALTER OPERATOR FAMILY"; - break; - case OBJECT_FUNCTION: - tag = "ALTER FUNCTION"; - break; - case OBJECT_SEQUENCE: - tag = "ALTER SEQUENCE"; - break; - case OBJECT_TABLE: - tag = "ALTER TABLE"; - break; - case OBJECT_TYPE: - tag = "ALTER TYPE"; - break; - case OBJECT_TSPARSER: - tag = "ALTER TEXT SEARCH PARSER"; - break; - case OBJECT_TSDICTIONARY: - tag = "ALTER TEXT SEARCH DICTIONARY"; - break; - case OBJECT_TSTEMPLATE: - tag = "ALTER TEXT SEARCH TEMPLATE"; - break; - case OBJECT_TSCONFIGURATION: - tag = "ALTER TEXT SEARCH CONFIGURATION"; - break; - case OBJECT_VIEW: - tag = "ALTER VIEW"; - break; - case OBJECT_FOREIGN_TABLE: - tag = "ALTER FOREIGN TABLE"; - break; - default: - tag = "???"; - break; - } + tag = AlterObjectTypeCommandTag(((AlterObjectSchemaStmt *) parsetree)->objectType); break; case T_AlterOwnerStmt: - switch (((AlterOwnerStmt *) parsetree)->objectType) - { - case OBJECT_AGGREGATE: - tag = "ALTER AGGREGATE"; - break; - case OBJECT_CONVERSION: - tag = "ALTER CONVERSION"; - break; - case OBJECT_DATABASE: - tag = "ALTER DATABASE"; - break; - case OBJECT_DOMAIN: - tag = "ALTER DOMAIN"; - break; - case OBJECT_FUNCTION: - tag = "ALTER FUNCTION"; - break; - case OBJECT_LANGUAGE: - tag = "ALTER LANGUAGE"; - break; - case OBJECT_LARGEOBJECT: - tag = "ALTER LARGE OBJECT"; - break; - case OBJECT_OPERATOR: - tag = "ALTER OPERATOR"; - break; - case OBJECT_OPCLASS: - tag = "ALTER OPERATOR CLASS"; - break; - case OBJECT_OPFAMILY: - tag = "ALTER OPERATOR FAMILY"; - break; - case OBJECT_SCHEMA: - tag = "ALTER SCHEMA"; - break; - case OBJECT_TABLESPACE: - tag = "ALTER TABLESPACE"; - break; - case OBJECT_TYPE: - tag = "ALTER TYPE"; - break; - case OBJECT_TSCONFIGURATION: - tag = "ALTER TEXT SEARCH CONFIGURATION"; - break; - case OBJECT_TSDICTIONARY: - tag = "ALTER TEXT SEARCH DICTIONARY"; - break; - case OBJECT_FDW: - tag = "ALTER FOREIGN DATA WRAPPER"; - break; - case OBJECT_FOREIGN_SERVER: - tag = "ALTER SERVER"; - break; - case OBJECT_FOREIGN_TABLE: - tag = "ALTER FOREIGN TABLE"; - break; - default: - tag = "???"; - break; - } + tag = AlterObjectTypeCommandTag(((AlterOwnerStmt *) parsetree)->objectType); break; case T_AlterTableStmt: - switch (((AlterTableStmt *) parsetree)->relkind) - { - case OBJECT_TABLE: - tag = "ALTER TABLE"; - break; - case OBJECT_INDEX: - tag = "ALTER INDEX"; - break; - case OBJECT_SEQUENCE: - tag = "ALTER SEQUENCE"; - break; - case OBJECT_TYPE: - tag = "ALTER TYPE"; - break; - case OBJECT_VIEW: - tag = "ALTER VIEW"; - break; - case OBJECT_FOREIGN_TABLE: - tag = "ALTER FOREIGN TABLE"; - break; - default: - tag = "???"; - break; - } + tag = AlterObjectTypeCommandTag(((AlterTableStmt *) parsetree)->relkind); break; case T_AlterDomainStmt: @@ -1928,6 +1882,9 @@ CreateCommandTag(Node *parsetree) case OBJECT_TSCONFIGURATION: tag = "CREATE TEXT SEARCH CONFIGURATION"; break; + case OBJECT_COLLATION: + tag = "CREATE COLLATION"; + break; default: tag = "???"; } @@ -2371,14 +2328,14 @@ GetCommandLogLevel(Node *parsetree) break; case T_CreateTableSpaceStmt: - lev = LOGSTMT_DDL; - break; - case T_DropTableSpaceStmt: + case T_AlterTableSpaceOptionsStmt: lev = LOGSTMT_DDL; break; - case T_AlterTableSpaceOptionsStmt: + case T_CreateExtensionStmt: + case T_AlterExtensionStmt: + case T_AlterExtensionContentsStmt: lev = LOGSTMT_DDL; break; diff --git a/src/backend/tsearch/ts_locale.c b/src/backend/tsearch/ts_locale.c index 2b6a6cb946..c66f4aa8bf 100644 --- a/src/backend/tsearch/ts_locale.c +++ b/src/backend/tsearch/ts_locale.c @@ -13,6 +13,7 @@ */ #include "postgres.h" +#include "catalog/pg_collation.h" #include "storage/fd.h" #include "tsearch/ts_locale.h" #include "tsearch/ts_public.h" @@ -27,11 +28,12 @@ t_isdigit(const char *ptr) { int clen = pg_mblen(ptr); wchar_t character[2]; + Oid collation = DEFAULT_COLLATION_OID; /*TODO*/ - if (clen == 1 || lc_ctype_is_c()) + if (clen == 1 || lc_ctype_is_c(collation)) return isdigit(TOUCHAR(ptr)); - char2wchar(character, 2, ptr, clen); + char2wchar(character, 2, ptr, clen, collation); return iswdigit((wint_t) character[0]); } @@ -41,11 +43,12 @@ t_isspace(const char *ptr) { int clen = pg_mblen(ptr); wchar_t character[2]; + Oid collation = DEFAULT_COLLATION_OID; /*TODO*/ - if (clen == 1 || lc_ctype_is_c()) + if (clen == 1 || lc_ctype_is_c(collation)) return isspace(TOUCHAR(ptr)); - char2wchar(character, 2, ptr, clen); + char2wchar(character, 2, ptr, clen, collation); return iswspace((wint_t) character[0]); } @@ -55,11 +58,12 @@ t_isalpha(const char *ptr) { int clen = pg_mblen(ptr); wchar_t character[2]; + Oid collation = DEFAULT_COLLATION_OID; /*TODO*/ - if (clen == 1 || lc_ctype_is_c()) + if (clen == 1 || lc_ctype_is_c(collation)) return isalpha(TOUCHAR(ptr)); - char2wchar(character, 2, ptr, clen); + char2wchar(character, 2, ptr, clen, collation); return iswalpha((wint_t) character[0]); } @@ -69,11 +73,12 @@ t_isprint(const char *ptr) { int clen = pg_mblen(ptr); wchar_t character[2]; + Oid collation = DEFAULT_COLLATION_OID; /*TODO*/ - if (clen == 1 || lc_ctype_is_c()) + if (clen == 1 || lc_ctype_is_c(collation)) return isprint(TOUCHAR(ptr)); - char2wchar(character, 2, ptr, clen); + char2wchar(character, 2, ptr, clen, collation); return iswprint((wint_t) character[0]); } @@ -238,6 +243,9 @@ char * lowerstr_with_len(const char *str, int len) { char *out; +#ifdef USE_WIDE_UPPER_LOWER + Oid collation = DEFAULT_COLLATION_OID; /*TODO*/ +#endif if (len == 0) return pstrdup(""); @@ -250,7 +258,7 @@ lowerstr_with_len(const char *str, int len) * Also, for a C locale there is no need to process as multibyte. From * backend/utils/adt/oracle_compat.c Teodor */ - if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c()) + if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c(collation)) { wchar_t *wstr, *wptr; @@ -263,7 +271,7 @@ lowerstr_with_len(const char *str, int len) */ wptr = wstr = (wchar_t *) palloc(sizeof(wchar_t) * (len + 1)); - wlen = char2wchar(wstr, len + 1, str, len); + wlen = char2wchar(wstr, len + 1, str, len, collation); Assert(wlen <= len); while (*wptr) @@ -278,7 +286,7 @@ lowerstr_with_len(const char *str, int len) len = pg_database_encoding_max_length() * wlen + 1; out = (char *) palloc(len); - wlen = wchar2char(out, wstr, len); + wlen = wchar2char(out, wstr, len, collation); pfree(wstr); diff --git a/src/backend/tsearch/wparser_def.c b/src/backend/tsearch/wparser_def.c index 40eca64895..3981a50589 100644 --- a/src/backend/tsearch/wparser_def.c +++ b/src/backend/tsearch/wparser_def.c @@ -14,6 +14,7 @@ #include "postgres.h" +#include "catalog/pg_collation.h" #include "commands/defrem.h" #include "tsearch/ts_locale.h" #include "tsearch/ts_public.h" @@ -298,8 +299,10 @@ TParserInit(char *str, int len) */ if (prs->charmaxlen > 1) { + Oid collation = DEFAULT_COLLATION_OID; /*TODO*/ + prs->usewide = true; - if ( lc_ctype_is_c() ) + if ( lc_ctype_is_c(collation) ) { /* * char2wchar doesn't work for C-locale and @@ -311,7 +314,7 @@ TParserInit(char *str, int len) else { prs->wstr = (wchar_t *) palloc(sizeof(wchar_t) * (prs->lenstr + 1)); - char2wchar(prs->wstr, prs->lenstr + 1, prs->str, prs->lenstr); + char2wchar(prs->wstr, prs->lenstr + 1, prs->str, prs->lenstr, collation); } } else diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c index 931c6953cb..e023b2458e 100644 --- a/src/backend/utils/adt/arrayfuncs.c +++ b/src/backend/utils/adt/arrayfuncs.c @@ -50,6 +50,30 @@ typedef enum ARRAY_LEVEL_DELIMITED } ArrayParseState; +/* Working state for array_iterate() */ +typedef struct ArrayIteratorData +{ + /* basic info about the array, set up during array_create_iterator() */ + ArrayType *arr; /* array we're iterating through */ + bits8 *nullbitmap; /* its null bitmap, if any */ + int nitems; /* total number of elements in array */ + int16 typlen; /* element type's length */ + bool typbyval; /* element type's byval property */ + char typalign; /* element type's align property */ + + /* information about the requested slice size */ + int slice_ndim; /* slice dimension, or 0 if not slicing */ + int slice_len; /* number of elements per slice */ + int *slice_dims; /* slice dims array */ + int *slice_lbound; /* slice lbound array */ + Datum *slice_values; /* workspace of length slice_len */ + bool *slice_nulls; /* workspace of length slice_len */ + + /* current position information, updated on each iteration */ + char *data_ptr; /* our current position in the array */ + int current_item; /* the item # we're at in the array */ +} ArrayIteratorData; + static bool array_isspace(char ch); static int ArrayCount(const char *str, int *dim, char typdelim); static void ReadArrayStr(char *arrayStr, const char *origStr, @@ -3307,6 +3331,7 @@ array_cmp(FunctionCallInfo fcinfo) { ArrayType *array1 = PG_GETARG_ARRAYTYPE_P(0); ArrayType *array2 = PG_GETARG_ARRAYTYPE_P(1); + Oid collation = PG_GET_COLLATION(); int ndims1 = ARR_NDIM(array1); int ndims2 = ARR_NDIM(array2); int *dims1 = ARR_DIMS(array1); @@ -3341,7 +3366,8 @@ array_cmp(FunctionCallInfo fcinfo) */ typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra; if (typentry == NULL || - typentry->type_id != element_type) + typentry->type_id != element_type || + typentry->cmp_proc_finfo.fn_collation != collation) { typentry = lookup_type_cache(element_type, TYPECACHE_CMP_PROC_FINFO); @@ -3351,6 +3377,7 @@ array_cmp(FunctionCallInfo fcinfo) errmsg("could not identify a comparison function for type %s", format_type_be(element_type)))); fcinfo->flinfo->fn_extra = (void *) typentry; + typentry->cmp_proc_finfo.fn_collation = collation; } typlen = typentry->typlen; typbyval = typentry->typbyval; @@ -3830,6 +3857,188 @@ arraycontained(PG_FUNCTION_ARGS) } +/*----------------------------------------------------------------------------- + * Array iteration functions + * These functions are used to iterate efficiently through arrays + *----------------------------------------------------------------------------- + */ + +/* + * array_create_iterator --- set up to iterate through an array + * + * If slice_ndim is zero, we will iterate element-by-element; the returned + * datums are of the array's element type. + * + * If slice_ndim is 1..ARR_NDIM(arr), we will iterate by slices: the + * returned datums are of the same array type as 'arr', but of size + * equal to the rightmost N dimensions of 'arr'. + * + * The passed-in array must remain valid for the lifetime of the iterator. + */ +ArrayIterator +array_create_iterator(ArrayType *arr, int slice_ndim) +{ + ArrayIterator iterator = palloc0(sizeof(ArrayIteratorData)); + + /* + * Sanity-check inputs --- caller should have got this right already + */ + Assert(PointerIsValid(arr)); + if (slice_ndim < 0 || slice_ndim > ARR_NDIM(arr)) + elog(ERROR, "invalid arguments to array_create_iterator"); + + /* + * Remember basic info about the array and its element type + */ + iterator->arr = arr; + iterator->nullbitmap = ARR_NULLBITMAP(arr); + iterator->nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr)); + get_typlenbyvalalign(ARR_ELEMTYPE(arr), + &iterator->typlen, + &iterator->typbyval, + &iterator->typalign); + + /* + * Remember the slicing parameters. + */ + iterator->slice_ndim = slice_ndim; + + if (slice_ndim > 0) + { + /* + * Get pointers into the array's dims and lbound arrays to represent + * the dims/lbound arrays of a slice. These are the same as the + * rightmost N dimensions of the array. + */ + iterator->slice_dims = ARR_DIMS(arr) + ARR_NDIM(arr) - slice_ndim; + iterator->slice_lbound = ARR_LBOUND(arr) + ARR_NDIM(arr) - slice_ndim; + + /* + * Compute number of elements in a slice. + */ + iterator->slice_len = ArrayGetNItems(slice_ndim, + iterator->slice_dims); + + /* + * Create workspace for building sub-arrays. + */ + iterator->slice_values = (Datum *) + palloc(iterator->slice_len * sizeof(Datum)); + iterator->slice_nulls = (bool *) + palloc(iterator->slice_len * sizeof(bool)); + } + + /* + * Initialize our data pointer and linear element number. These will + * advance through the array during array_iterate(). + */ + iterator->data_ptr = ARR_DATA_PTR(arr); + iterator->current_item = 0; + + return iterator; +} + +/* + * Iterate through the array referenced by 'iterator'. + * + * As long as there is another element (or slice), return it into + * *value / *isnull, and return true. Return false when no more data. + */ +bool +array_iterate(ArrayIterator iterator, Datum *value, bool *isnull) +{ + /* Done if we have reached the end of the array */ + if (iterator->current_item >= iterator->nitems) + return false; + + if (iterator->slice_ndim == 0) + { + /* + * Scalar case: return one element. + */ + if (array_get_isnull(iterator->nullbitmap, iterator->current_item++)) + { + *isnull = true; + *value = (Datum) 0; + } + else + { + /* non-NULL, so fetch the individual Datum to return */ + char *p = iterator->data_ptr; + + *isnull = false; + *value = fetch_att(p, iterator->typbyval, iterator->typlen); + + /* Move our data pointer forward to the next element */ + p = att_addlength_pointer(p, iterator->typlen, p); + p = (char *) att_align_nominal(p, iterator->typalign); + iterator->data_ptr = p; + } + } + else + { + /* + * Slice case: build and return an array of the requested size. + */ + ArrayType *result; + Datum *values = iterator->slice_values; + bool *nulls = iterator->slice_nulls; + char *p = iterator->data_ptr; + int i; + + for (i = 0; i < iterator->slice_len; i++) + { + if (array_get_isnull(iterator->nullbitmap, + iterator->current_item++)) + { + nulls[i] = true; + values[i] = (Datum) 0; + } + else + { + nulls[i] = false; + values[i] = fetch_att(p, iterator->typbyval, iterator->typlen); + + /* Move our data pointer forward to the next element */ + p = att_addlength_pointer(p, iterator->typlen, p); + p = (char *) att_align_nominal(p, iterator->typalign); + } + } + + iterator->data_ptr = p; + + result = construct_md_array(values, + nulls, + iterator->slice_ndim, + iterator->slice_dims, + iterator->slice_lbound, + ARR_ELEMTYPE(iterator->arr), + iterator->typlen, + iterator->typbyval, + iterator->typalign); + + *isnull = false; + *value = PointerGetDatum(result); + } + + return true; +} + +/* + * Release an ArrayIterator data structure + */ +void +array_free_iterator(ArrayIterator iterator) +{ + if (iterator->slice_ndim > 0) + { + pfree(iterator->slice_values); + pfree(iterator->slice_nulls); + } + pfree(iterator); +} + + /***************************************************************************/ /******************| Support Routines |*****************/ /***************************************************************************/ diff --git a/src/backend/utils/adt/format_type.c b/src/backend/utils/adt/format_type.c index b56bb74bdc..f85e0bbd00 100644 --- a/src/backend/utils/adt/format_type.c +++ b/src/backend/utils/adt/format_type.c @@ -18,6 +18,7 @@ #include <ctype.h> #include "catalog/namespace.h" +#include "catalog/pg_collation.h" #include "catalog/pg_type.h" #include "utils/builtins.h" #include "utils/lsyscache.h" @@ -28,7 +29,8 @@ #define MAX_INT32_LEN 11 static char *format_type_internal(Oid type_oid, int32 typemod, - bool typemod_given, bool allow_invalid); + bool typemod_given, bool allow_invalid, + Oid collation_oid); static char *printTypmod(const char *typname, int32 typmod, Oid typmodout); static char * psnprintf(size_t len, const char *fmt,...) @@ -67,6 +69,7 @@ format_type(PG_FUNCTION_ARGS) { Oid type_oid; int32 typemod; + Oid collation_oid; char *result; /* Since this function is not strict, we must test for null args */ @@ -74,13 +77,14 @@ format_type(PG_FUNCTION_ARGS) PG_RETURN_NULL(); type_oid = PG_GETARG_OID(0); + collation_oid = PG_ARGISNULL(2) ? InvalidOid : PG_GETARG_OID(2); if (PG_ARGISNULL(1)) - result = format_type_internal(type_oid, -1, false, true); + result = format_type_internal(type_oid, -1, false, true, collation_oid); else { typemod = PG_GETARG_INT32(1); - result = format_type_internal(type_oid, typemod, true, true); + result = format_type_internal(type_oid, typemod, true, true, collation_oid); } PG_RETURN_TEXT_P(cstring_to_text(result)); @@ -95,7 +99,7 @@ format_type(PG_FUNCTION_ARGS) char * format_type_be(Oid type_oid) { - return format_type_internal(type_oid, -1, false, false); + return format_type_internal(type_oid, -1, false, false, InvalidOid); } /* @@ -104,14 +108,15 @@ format_type_be(Oid type_oid) char * format_type_with_typemod(Oid type_oid, int32 typemod) { - return format_type_internal(type_oid, typemod, true, false); + return format_type_internal(type_oid, typemod, true, false, InvalidOid); } static char * format_type_internal(Oid type_oid, int32 typemod, - bool typemod_given, bool allow_invalid) + bool typemod_given, bool allow_invalid, + Oid collation_oid) { bool with_typemod = typemod_given && (typemod >= 0); HeapTuple tuple; @@ -317,6 +322,12 @@ format_type_internal(Oid type_oid, int32 typemod, ReleaseSysCache(tuple); + if (collation_oid && collation_oid != DEFAULT_COLLATION_OID) + { + char *collstr = generate_collation_name(collation_oid); + buf = psnprintf(strlen(buf) + 10 + strlen(collstr), "%s COLLATE %s", buf, collstr); + } + return buf; } @@ -420,7 +431,7 @@ oidvectortypes(PG_FUNCTION_ARGS) for (num = 0; num < numargs; num++) { char *typename = format_type_internal(oidArray->values[num], -1, - false, true); + false, true, InvalidOid); size_t slen = strlen(typename); if (left < (slen + 2)) diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c index 4855bac41d..f90d36d24c 100644 --- a/src/backend/utils/adt/formatting.c +++ b/src/backend/utils/adt/formatting.c @@ -82,6 +82,7 @@ #include <wctype.h> #endif +#include "catalog/pg_collation.h" #include "mb/pg_wchar.h" #include "utils/builtins.h" #include "utils/date.h" @@ -953,7 +954,7 @@ static void parse_format(FormatNode *node, char *str, const KeyWord *kw, KeySuffix *suf, const int *index, int ver, NUMDesc *Num); static void DCH_to_char(FormatNode *node, bool is_interval, - TmToChar *in, char *out); + TmToChar *in, char *out, Oid collid); static void DCH_from_char(FormatNode *node, char *in, TmFromChar *out); #ifdef DEBUG_TO_FROM_CHAR @@ -981,7 +982,7 @@ static char *get_last_relevant_decnum(char *num); static void NUM_numpart_from_char(NUMProc *Np, int id, int plen); static void NUM_numpart_to_char(NUMProc *Np, int id); static char *NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number, - int plen, int sign, bool is_to_char); + int plen, int sign, bool is_to_char, Oid collid); static DCHCacheEntry *DCH_cache_search(char *str); static DCHCacheEntry *DCH_cache_getnew(char *str); @@ -1470,15 +1471,19 @@ str_numth(char *dest, char *num, int type) * to this function. The result is a palloc'd, null-terminated string. */ char * -str_tolower(const char *buff, size_t nbytes) +str_tolower(const char *buff, size_t nbytes, Oid collid) { char *result; + pg_locale_t mylocale = 0; if (!buff) return NULL; + if (collid != DEFAULT_COLLATION_OID) + mylocale = pg_newlocale_from_collation(collid); + #ifdef USE_WIDE_UPPER_LOWER - if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c()) + if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c(collid)) { wchar_t *workspace; size_t curr_char; @@ -1493,16 +1498,21 @@ str_tolower(const char *buff, size_t nbytes) /* Output workspace cannot have more codes than input bytes */ workspace = (wchar_t *) palloc((nbytes + 1) * sizeof(wchar_t)); - char2wchar(workspace, nbytes + 1, buff, nbytes); + char2wchar(workspace, nbytes + 1, buff, nbytes, collid); for (curr_char = 0; workspace[curr_char] != 0; curr_char++) +#ifdef HAVE_LOCALE_T + if (mylocale) + workspace[curr_char] = towlower_l(workspace[curr_char], mylocale); + else +#endif workspace[curr_char] = towlower(workspace[curr_char]); /* Make result large enough; case change might change number of bytes */ result_size = curr_char * pg_database_encoding_max_length() + 1; result = palloc(result_size); - wchar2char(result, workspace, result_size); + wchar2char(result, workspace, result_size, collid); pfree(workspace); } else @@ -1526,15 +1536,19 @@ str_tolower(const char *buff, size_t nbytes) * to this function. The result is a palloc'd, null-terminated string. */ char * -str_toupper(const char *buff, size_t nbytes) +str_toupper(const char *buff, size_t nbytes, Oid collid) { char *result; + pg_locale_t mylocale = 0; if (!buff) return NULL; + if (collid != DEFAULT_COLLATION_OID) + mylocale = pg_newlocale_from_collation(collid); + #ifdef USE_WIDE_UPPER_LOWER - if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c()) + if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c(collid)) { wchar_t *workspace; size_t curr_char; @@ -1549,16 +1563,21 @@ str_toupper(const char *buff, size_t nbytes) /* Output workspace cannot have more codes than input bytes */ workspace = (wchar_t *) palloc((nbytes + 1) * sizeof(wchar_t)); - char2wchar(workspace, nbytes + 1, buff, nbytes); + char2wchar(workspace, nbytes + 1, buff, nbytes, collid); for (curr_char = 0; workspace[curr_char] != 0; curr_char++) +#ifdef HAVE_LOCALE_T + if (mylocale) + workspace[curr_char] = towupper_l(workspace[curr_char], mylocale); + else +#endif workspace[curr_char] = towupper(workspace[curr_char]); /* Make result large enough; case change might change number of bytes */ result_size = curr_char * pg_database_encoding_max_length() + 1; result = palloc(result_size); - wchar2char(result, workspace, result_size); + wchar2char(result, workspace, result_size, collid); pfree(workspace); } else @@ -1582,16 +1601,20 @@ str_toupper(const char *buff, size_t nbytes) * to this function. The result is a palloc'd, null-terminated string. */ char * -str_initcap(const char *buff, size_t nbytes) +str_initcap(const char *buff, size_t nbytes, Oid collid) { char *result; int wasalnum = false; + pg_locale_t mylocale = 0; if (!buff) return NULL; + if (collid != DEFAULT_COLLATION_OID) + mylocale = pg_newlocale_from_collation(collid); + #ifdef USE_WIDE_UPPER_LOWER - if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c()) + if (pg_database_encoding_max_length() > 1 && !lc_ctype_is_c(collid)) { wchar_t *workspace; size_t curr_char; @@ -1606,22 +1629,35 @@ str_initcap(const char *buff, size_t nbytes) /* Output workspace cannot have more codes than input bytes */ workspace = (wchar_t *) palloc((nbytes + 1) * sizeof(wchar_t)); - char2wchar(workspace, nbytes + 1, buff, nbytes); + char2wchar(workspace, nbytes + 1, buff, nbytes, collid); for (curr_char = 0; workspace[curr_char] != 0; curr_char++) { - if (wasalnum) - workspace[curr_char] = towlower(workspace[curr_char]); +#ifdef HAVE_LOCALE_T + if (mylocale) + { + if (wasalnum) + workspace[curr_char] = towlower_l(workspace[curr_char], mylocale); + else + workspace[curr_char] = towupper_l(workspace[curr_char], mylocale); + wasalnum = iswalnum_l(workspace[curr_char], mylocale); + } else - workspace[curr_char] = towupper(workspace[curr_char]); - wasalnum = iswalnum(workspace[curr_char]); +#endif + { + if (wasalnum) + workspace[curr_char] = towlower(workspace[curr_char]); + else + workspace[curr_char] = towupper(workspace[curr_char]); + wasalnum = iswalnum(workspace[curr_char]); + } } /* Make result large enough; case change might change number of bytes */ result_size = curr_char * pg_database_encoding_max_length() + 1; result = palloc(result_size); - wchar2char(result, workspace, result_size); + wchar2char(result, workspace, result_size, collid); pfree(workspace); } else @@ -1647,21 +1683,21 @@ str_initcap(const char *buff, size_t nbytes) /* convenience routines for when the input is null-terminated */ static char * -str_tolower_z(const char *buff) +str_tolower_z(const char *buff, Oid collid) { - return str_tolower(buff, strlen(buff)); + return str_tolower(buff, strlen(buff), collid); } static char * -str_toupper_z(const char *buff) +str_toupper_z(const char *buff, Oid collid) { - return str_toupper(buff, strlen(buff)); + return str_toupper(buff, strlen(buff), collid); } static char * -str_initcap_z(const char *buff) +str_initcap_z(const char *buff, Oid collid) { - return str_initcap(buff, strlen(buff)); + return str_initcap(buff, strlen(buff), collid); } @@ -2039,7 +2075,7 @@ from_char_seq_search(int *dest, char **src, char **array, int type, int max, * ---------- */ static void -DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out) +DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out, Oid collid) { FormatNode *n; char *s; @@ -2151,7 +2187,7 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out) INVALID_FOR_INTERVAL; if (tmtcTzn(in)) { - char *p = str_tolower_z(tmtcTzn(in)); + char *p = str_tolower_z(tmtcTzn(in), collid); strcpy(s, p); pfree(p); @@ -2195,10 +2231,10 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out) if (!tm->tm_mon) break; if (S_TM(n->suffix)) - strcpy(s, str_toupper_z(localized_full_months[tm->tm_mon - 1])); + strcpy(s, str_toupper_z(localized_full_months[tm->tm_mon - 1], collid)); else sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, - str_toupper_z(months_full[tm->tm_mon - 1])); + str_toupper_z(months_full[tm->tm_mon - 1], collid)); s += strlen(s); break; case DCH_Month: @@ -2206,7 +2242,7 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out) if (!tm->tm_mon) break; if (S_TM(n->suffix)) - strcpy(s, str_initcap_z(localized_full_months[tm->tm_mon - 1])); + strcpy(s, str_initcap_z(localized_full_months[tm->tm_mon - 1], collid)); else sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, months_full[tm->tm_mon - 1]); s += strlen(s); @@ -2216,7 +2252,7 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out) if (!tm->tm_mon) break; if (S_TM(n->suffix)) - strcpy(s, str_tolower_z(localized_full_months[tm->tm_mon - 1])); + strcpy(s, str_tolower_z(localized_full_months[tm->tm_mon - 1], collid)); else { sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, months_full[tm->tm_mon - 1]); @@ -2229,9 +2265,9 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out) if (!tm->tm_mon) break; if (S_TM(n->suffix)) - strcpy(s, str_toupper_z(localized_abbrev_months[tm->tm_mon - 1])); + strcpy(s, str_toupper_z(localized_abbrev_months[tm->tm_mon - 1], collid)); else - strcpy(s, str_toupper_z(months[tm->tm_mon - 1])); + strcpy(s, str_toupper_z(months[tm->tm_mon - 1], collid)); s += strlen(s); break; case DCH_Mon: @@ -2239,7 +2275,7 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out) if (!tm->tm_mon) break; if (S_TM(n->suffix)) - strcpy(s, str_initcap_z(localized_abbrev_months[tm->tm_mon - 1])); + strcpy(s, str_initcap_z(localized_abbrev_months[tm->tm_mon - 1], collid)); else strcpy(s, months[tm->tm_mon - 1]); s += strlen(s); @@ -2249,7 +2285,7 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out) if (!tm->tm_mon) break; if (S_TM(n->suffix)) - strcpy(s, str_tolower_z(localized_abbrev_months[tm->tm_mon - 1])); + strcpy(s, str_tolower_z(localized_abbrev_months[tm->tm_mon - 1], collid)); else { strcpy(s, months[tm->tm_mon - 1]); @@ -2266,16 +2302,16 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out) case DCH_DAY: INVALID_FOR_INTERVAL; if (S_TM(n->suffix)) - strcpy(s, str_toupper_z(localized_full_days[tm->tm_wday])); + strcpy(s, str_toupper_z(localized_full_days[tm->tm_wday], collid)); else sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, - str_toupper_z(days[tm->tm_wday])); + str_toupper_z(days[tm->tm_wday], collid)); s += strlen(s); break; case DCH_Day: INVALID_FOR_INTERVAL; if (S_TM(n->suffix)) - strcpy(s, str_initcap_z(localized_full_days[tm->tm_wday])); + strcpy(s, str_initcap_z(localized_full_days[tm->tm_wday], collid)); else sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, days[tm->tm_wday]); s += strlen(s); @@ -2283,7 +2319,7 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out) case DCH_day: INVALID_FOR_INTERVAL; if (S_TM(n->suffix)) - strcpy(s, str_tolower_z(localized_full_days[tm->tm_wday])); + strcpy(s, str_tolower_z(localized_full_days[tm->tm_wday], collid)); else { sprintf(s, "%*s", S_FM(n->suffix) ? 0 : -9, days[tm->tm_wday]); @@ -2294,15 +2330,15 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out) case DCH_DY: INVALID_FOR_INTERVAL; if (S_TM(n->suffix)) - strcpy(s, str_toupper_z(localized_abbrev_days[tm->tm_wday])); + strcpy(s, str_toupper_z(localized_abbrev_days[tm->tm_wday], collid)); else - strcpy(s, str_toupper_z(days_short[tm->tm_wday])); + strcpy(s, str_toupper_z(days_short[tm->tm_wday], collid)); s += strlen(s); break; case DCH_Dy: INVALID_FOR_INTERVAL; if (S_TM(n->suffix)) - strcpy(s, str_initcap_z(localized_abbrev_days[tm->tm_wday])); + strcpy(s, str_initcap_z(localized_abbrev_days[tm->tm_wday], collid)); else strcpy(s, days_short[tm->tm_wday]); s += strlen(s); @@ -2310,7 +2346,7 @@ DCH_to_char(FormatNode *node, bool is_interval, TmToChar *in, char *out) case DCH_dy: INVALID_FOR_INTERVAL; if (S_TM(n->suffix)) - strcpy(s, str_tolower_z(localized_abbrev_days[tm->tm_wday])); + strcpy(s, str_tolower_z(localized_abbrev_days[tm->tm_wday], collid)); else { strcpy(s, days_short[tm->tm_wday]); @@ -2846,7 +2882,7 @@ DCH_cache_search(char *str) * for formatting. */ static text * -datetime_to_char_body(TmToChar *tmtc, text *fmt, bool is_interval) +datetime_to_char_body(TmToChar *tmtc, text *fmt, bool is_interval, Oid collid) { FormatNode *format; char *fmt_str, @@ -2912,7 +2948,7 @@ datetime_to_char_body(TmToChar *tmtc, text *fmt, bool is_interval) } /* The real work is here */ - DCH_to_char(format, is_interval, tmtc, result); + DCH_to_char(format, is_interval, tmtc, result, collid); if (!incache) pfree(format); @@ -2959,7 +2995,7 @@ timestamp_to_char(PG_FUNCTION_ARGS) tm->tm_wday = (thisdate + 1) % 7; tm->tm_yday = thisdate - date2j(tm->tm_year, 1, 1) + 1; - if (!(res = datetime_to_char_body(&tmtc, fmt, false))) + if (!(res = datetime_to_char_body(&tmtc, fmt, false, PG_GET_COLLATION()))) PG_RETURN_NULL(); PG_RETURN_TEXT_P(res); @@ -2991,7 +3027,7 @@ timestamptz_to_char(PG_FUNCTION_ARGS) tm->tm_wday = (thisdate + 1) % 7; tm->tm_yday = thisdate - date2j(tm->tm_year, 1, 1) + 1; - if (!(res = datetime_to_char_body(&tmtc, fmt, false))) + if (!(res = datetime_to_char_body(&tmtc, fmt, false, PG_GET_COLLATION()))) PG_RETURN_NULL(); PG_RETURN_TEXT_P(res); @@ -3023,7 +3059,7 @@ interval_to_char(PG_FUNCTION_ARGS) /* wday is meaningless, yday approximates the total span in days */ tm->tm_yday = (tm->tm_year * MONTHS_PER_YEAR + tm->tm_mon) * DAYS_PER_MONTH + tm->tm_mday; - if (!(res = datetime_to_char_body(&tmtc, fmt, true))) + if (!(res = datetime_to_char_body(&tmtc, fmt, true, PG_GET_COLLATION()))) PG_RETURN_NULL(); PG_RETURN_TEXT_P(res); @@ -4123,7 +4159,7 @@ NUM_numpart_to_char(NUMProc *Np, int id) */ static char * NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number, - int plen, int sign, bool is_to_char) + int plen, int sign, bool is_to_char, Oid collid) { FormatNode *n; NUMProc _Np, @@ -4403,12 +4439,12 @@ NUM_processor(FormatNode *node, NUMDesc *Num, char *inout, char *number, case NUM_rn: if (IS_FILLMODE(Np->Num)) { - strcpy(Np->inout_p, str_tolower_z(Np->number_p)); + strcpy(Np->inout_p, str_tolower_z(Np->number_p, collid)); Np->inout_p += strlen(Np->inout_p) - 1; } else { - sprintf(Np->inout_p, "%15s", str_tolower_z(Np->number_p)); + sprintf(Np->inout_p, "%15s", str_tolower_z(Np->number_p, collid)); Np->inout_p += strlen(Np->inout_p) - 1; } break; @@ -4541,7 +4577,7 @@ do { \ */ #define NUM_TOCHAR_finish \ do { \ - NUM_processor(format, &Num, VARDATA(result), numstr, plen, sign, true); \ + NUM_processor(format, &Num, VARDATA(result), numstr, plen, sign, true, PG_GET_COLLATION()); \ \ if (shouldFree) \ pfree(format); \ @@ -4583,7 +4619,7 @@ numeric_to_number(PG_FUNCTION_ARGS) numstr = (char *) palloc((len * NUM_MAX_ITEM_SIZ) + 1); NUM_processor(format, &Num, VARDATA(value), numstr, - VARSIZE(value) - VARHDRSZ, 0, false); + VARSIZE(value) - VARHDRSZ, 0, false, PG_GET_COLLATION()); scale = Num.post; precision = Max(0, Num.pre) + scale; diff --git a/src/backend/utils/adt/genfile.c b/src/backend/utils/adt/genfile.c index 93bc401c2d..7c59e9a20e 100644 --- a/src/backend/utils/adt/genfile.c +++ b/src/backend/utils/adt/genfile.c @@ -51,53 +51,46 @@ convert_and_check_filename(text *arg) filename = text_to_cstring(arg); canonicalize_path(filename); /* filename can change length here */ - /* Disallow ".." in the path */ - if (path_contains_parent_reference(filename)) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - (errmsg("reference to parent directory (\"..\") not allowed")))); - if (is_absolute_path(filename)) { - /* Allow absolute references within DataDir */ - if (path_is_prefix_of_path(DataDir, filename)) - return filename; - /* The log directory might be outside our datadir, but allow it */ - if (is_absolute_path(Log_directory) && - path_is_prefix_of_path(Log_directory, filename)) - return filename; - - ereport(ERROR, + /* Disallow '/a/b/data/..' */ + if (path_contains_parent_reference(filename)) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("reference to parent directory (\"..\") not allowed")))); + /* + * Allow absolute paths if within DataDir or Log_directory, even + * though Log_directory might be outside DataDir. + */ + if (!path_is_prefix_of_path(DataDir, filename) && + (!is_absolute_path(Log_directory) || + !path_is_prefix_of_path(Log_directory, filename))) + ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), (errmsg("absolute path not allowed")))); - return NULL; /* keep compiler quiet */ - } - else - { - return filename; } + else if (!path_is_relative_and_below_cwd(filename)) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("path must be in or below the current directory")))); + + return filename; } /* * Read a section of a file, returning it as bytea * - * We read the whole of the file when bytes_to_read is nagative. + * Caller is responsible for all permissions checking. + * + * We read the whole of the file when bytes_to_read is negative. */ -static bytea * -read_binary_file(text *filename_t, int64 seek_offset, int64 bytes_to_read) +bytea * +read_binary_file(const char *filename, int64 seek_offset, int64 bytes_to_read) { bytea *buf; size_t nbytes; FILE *file; - char *filename; - - if (!superuser()) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - (errmsg("must be superuser to read files")))); - - filename = convert_and_check_filename(filename_t); if (bytes_to_read < 0) { @@ -146,7 +139,6 @@ read_binary_file(text *filename_t, int64 seek_offset, int64 bytes_to_read) SET_VARSIZE(buf, nbytes + VARHDRSZ); FreeFile(file); - pfree(filename); return buf; } @@ -156,9 +148,11 @@ read_binary_file(text *filename_t, int64 seek_offset, int64 bytes_to_read) * in the database encoding. */ static text * -read_text_file(text *filename, int64 seek_offset, int64 bytes_to_read) +read_text_file(const char *filename, int64 seek_offset, int64 bytes_to_read) { - bytea *buf = read_binary_file(filename, seek_offset, bytes_to_read); + bytea *buf; + + buf = read_binary_file(filename, seek_offset, bytes_to_read); /* Make sure the input is valid */ pg_verifymbstr(VARDATA(buf), VARSIZE(buf) - VARHDRSZ, false); @@ -176,13 +170,21 @@ pg_read_file(PG_FUNCTION_ARGS) text *filename_t = PG_GETARG_TEXT_P(0); int64 seek_offset = PG_GETARG_INT64(1); int64 bytes_to_read = PG_GETARG_INT64(2); + char *filename; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to read files")))); + + filename = convert_and_check_filename(filename_t); if (bytes_to_read < 0) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("requested length cannot be negative"))); - PG_RETURN_TEXT_P(read_text_file(filename_t, seek_offset, bytes_to_read)); + PG_RETURN_TEXT_P(read_text_file(filename, seek_offset, bytes_to_read)); } /* @@ -192,8 +194,16 @@ Datum pg_read_file_all(PG_FUNCTION_ARGS) { text *filename_t = PG_GETARG_TEXT_P(0); + char *filename; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to read files")))); - PG_RETURN_TEXT_P(read_text_file(filename_t, 0, -1)); + filename = convert_and_check_filename(filename_t); + + PG_RETURN_TEXT_P(read_text_file(filename, 0, -1)); } /* @@ -205,13 +215,21 @@ pg_read_binary_file(PG_FUNCTION_ARGS) text *filename_t = PG_GETARG_TEXT_P(0); int64 seek_offset = PG_GETARG_INT64(1); int64 bytes_to_read = PG_GETARG_INT64(2); + char *filename; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to read files")))); + + filename = convert_and_check_filename(filename_t); if (bytes_to_read < 0) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("requested length cannot be negative"))); - PG_RETURN_BYTEA_P(read_binary_file(filename_t, seek_offset, bytes_to_read)); + PG_RETURN_BYTEA_P(read_binary_file(filename, seek_offset, bytes_to_read)); } /* @@ -221,8 +239,16 @@ Datum pg_read_binary_file_all(PG_FUNCTION_ARGS) { text *filename_t = PG_GETARG_TEXT_P(0); + char *filename; + + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + (errmsg("must be superuser to read files")))); + + filename = convert_and_check_filename(filename_t); - PG_RETURN_BYTEA_P(read_binary_file(filename_t, 0, -1)); + PG_RETURN_BYTEA_P(read_binary_file(filename, 0, -1)); } /* diff --git a/src/backend/utils/adt/like.c b/src/backend/utils/adt/like.c index 1e7a6f32ea..1edbe88b74 100644 --- a/src/backend/utils/adt/like.c +++ b/src/backend/utils/adt/like.c @@ -39,7 +39,7 @@ static int UTF8_MatchText(char *t, int tlen, char *p, int plen); static int SB_IMatchText(char *t, int tlen, char *p, int plen); static int GenericMatchText(char *s, int slen, char *p, int plen); -static int Generic_Text_IC_like(text *str, text *pat); +static int Generic_Text_IC_like(text *str, text *pat, Oid collation); /*-------------------- * Support routine for MatchText. Compares given multibyte streams @@ -133,7 +133,7 @@ GenericMatchText(char *s, int slen, char *p, int plen) } static inline int -Generic_Text_IC_like(text *str, text *pat) +Generic_Text_IC_like(text *str, text *pat, Oid collation) { char *s, *p; @@ -149,10 +149,10 @@ Generic_Text_IC_like(text *str, text *pat) if (pg_database_encoding_max_length() > 1) { /* lower's result is never packed, so OK to use old macros here */ - pat = DatumGetTextP(DirectFunctionCall1(lower, PointerGetDatum(pat))); + pat = DatumGetTextP(DirectFunctionCall1WithCollation(lower, collation, PointerGetDatum(pat))); p = VARDATA(pat); plen = (VARSIZE(pat) - VARHDRSZ); - str = DatumGetTextP(DirectFunctionCall1(lower, PointerGetDatum(str))); + str = DatumGetTextP(DirectFunctionCall1WithCollation(lower, collation, PointerGetDatum(str))); s = VARDATA(str); slen = (VARSIZE(str) - VARHDRSZ); if (GetDatabaseEncoding() == PG_UTF8) @@ -314,7 +314,7 @@ nameiclike(PG_FUNCTION_ARGS) strtext = DatumGetTextP(DirectFunctionCall1(name_text, NameGetDatum(str))); - result = (Generic_Text_IC_like(strtext, pat) == LIKE_TRUE); + result = (Generic_Text_IC_like(strtext, pat, PG_GET_COLLATION()) == LIKE_TRUE); PG_RETURN_BOOL(result); } @@ -329,7 +329,7 @@ nameicnlike(PG_FUNCTION_ARGS) strtext = DatumGetTextP(DirectFunctionCall1(name_text, NameGetDatum(str))); - result = (Generic_Text_IC_like(strtext, pat) != LIKE_TRUE); + result = (Generic_Text_IC_like(strtext, pat, PG_GET_COLLATION()) != LIKE_TRUE); PG_RETURN_BOOL(result); } @@ -341,7 +341,7 @@ texticlike(PG_FUNCTION_ARGS) text *pat = PG_GETARG_TEXT_PP(1); bool result; - result = (Generic_Text_IC_like(str, pat) == LIKE_TRUE); + result = (Generic_Text_IC_like(str, pat, PG_GET_COLLATION()) == LIKE_TRUE); PG_RETURN_BOOL(result); } @@ -353,7 +353,7 @@ texticnlike(PG_FUNCTION_ARGS) text *pat = PG_GETARG_TEXT_PP(1); bool result; - result = (Generic_Text_IC_like(str, pat) != LIKE_TRUE); + result = (Generic_Text_IC_like(str, pat, PG_GET_COLLATION()) != LIKE_TRUE); PG_RETURN_BOOL(result); } diff --git a/src/backend/utils/adt/oracle_compat.c b/src/backend/utils/adt/oracle_compat.c index 65559dff58..4487b0a181 100644 --- a/src/backend/utils/adt/oracle_compat.c +++ b/src/backend/utils/adt/oracle_compat.c @@ -47,7 +47,8 @@ lower(PG_FUNCTION_ARGS) text *result; out_string = str_tolower(VARDATA_ANY(in_string), - VARSIZE_ANY_EXHDR(in_string)); + VARSIZE_ANY_EXHDR(in_string), + PG_GET_COLLATION()); result = cstring_to_text(out_string); pfree(out_string); @@ -77,7 +78,8 @@ upper(PG_FUNCTION_ARGS) text *result; out_string = str_toupper(VARDATA_ANY(in_string), - VARSIZE_ANY_EXHDR(in_string)); + VARSIZE_ANY_EXHDR(in_string), + PG_GET_COLLATION()); result = cstring_to_text(out_string); pfree(out_string); @@ -110,7 +112,8 @@ initcap(PG_FUNCTION_ARGS) text *result; out_string = str_initcap(VARDATA_ANY(in_string), - VARSIZE_ANY_EXHDR(in_string)); + VARSIZE_ANY_EXHDR(in_string), + PG_GET_COLLATION()); result = cstring_to_text(out_string); pfree(out_string); diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c index f76305a219..2b9b321b26 100644 --- a/src/backend/utils/adt/pg_locale.c +++ b/src/backend/utils/adt/pg_locale.c @@ -54,10 +54,13 @@ #include <locale.h> #include <time.h> +#include "catalog/pg_collation.h" #include "catalog/pg_control.h" #include "mb/pg_wchar.h" +#include "utils/hsearch.h" #include "utils/memutils.h" #include "utils/pg_locale.h" +#include "utils/syscache.h" #ifdef WIN32 #include <shlwapi.h> @@ -100,6 +103,11 @@ static char lc_time_envbuf[LC_ENV_BUFSIZE]; static char *IsoLocaleName(const char *); /* MSVC specific */ #endif +static HTAB *locale_cness_cache = NULL; +#ifdef HAVE_LOCALE_T +static HTAB *locale_t_cache = NULL; +#endif + /* * pg_perm_setlocale @@ -305,16 +313,90 @@ locale_messages_assign(const char *value, bool doit, GucSource source) /* - * We'd like to cache whether LC_COLLATE is C (or POSIX), so we can - * optimize a few code paths in various places. + * We'd like to cache whether LC_COLLATE or LC_CTYPE is C (or POSIX), + * so we can optimize a few code paths in various places. + * + * Note that some code relies on this not reporting false negatives + * (that is, saying it's not C when it is). For example, char2wchar() + * could fail if the locale is C, so str_tolower() shouldn't call it + * in that case. */ + +struct locale_cness_cache_entry +{ + Oid collid; + bool collate_is_c; + bool ctype_is_c; +}; + +static void +init_locale_cness_cache(void) +{ + HASHCTL ctl; + + memset(&ctl, 0, sizeof(ctl)); + ctl.keysize = sizeof(Oid); + ctl.entrysize = sizeof(struct locale_cness_cache_entry); + ctl.hash = oid_hash; + locale_cness_cache = hash_create("locale C-ness cache", 1000, &ctl, HASH_ELEM | HASH_FUNCTION); +} + +/* + * Handle caching of locale "C-ness" for nondefault collation objects. + * Relying on the system cache directly isn't fast enough. + */ +static bool +lookup_collation_cness(Oid collation, int category) +{ + struct locale_cness_cache_entry *cache_entry; + bool found; + HeapTuple tp; + char *localeptr; + + Assert(OidIsValid(collation)); + Assert(category == LC_COLLATE || category == LC_CTYPE); + + if (!locale_cness_cache) + init_locale_cness_cache(); + + cache_entry = hash_search(locale_cness_cache, &collation, HASH_ENTER, &found); + if (found) + { + if (category == LC_COLLATE) + return cache_entry->collate_is_c; + else + return cache_entry->ctype_is_c; + } + + tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collation)); + if (!HeapTupleIsValid(tp)) + elog(ERROR, "cache lookup failed for collation %u", collation); + + localeptr = NameStr(((Form_pg_collation) GETSTRUCT(tp))->collcollate); + cache_entry->collate_is_c = (strcmp(localeptr, "C") == 0) || (strcmp(localeptr, "POSIX") == 0); + + localeptr = NameStr(((Form_pg_collation) GETSTRUCT(tp))->collctype); + cache_entry->ctype_is_c = (strcmp(localeptr, "C") == 0) || (strcmp(localeptr, "POSIX") == 0); + + ReleaseSysCache(tp); + + return category == LC_COLLATE ? cache_entry->collate_is_c : cache_entry->ctype_is_c; +} + + bool -lc_collate_is_c(void) +lc_collate_is_c(Oid collation) { /* Cache result so we only have to compute it once */ static int result = -1; char *localeptr; + if (!OidIsValid(collation)) + return false; + + if (collation != DEFAULT_COLLATION_OID) + return lookup_collation_cness(collation, LC_COLLATE); + if (result >= 0) return (bool) result; localeptr = setlocale(LC_COLLATE, NULL); @@ -331,17 +413,19 @@ lc_collate_is_c(void) } -/* - * We'd like to cache whether LC_CTYPE is C (or POSIX), so we can - * optimize a few code paths in various places. - */ bool -lc_ctype_is_c(void) +lc_ctype_is_c(Oid collation) { /* Cache result so we only have to compute it once */ static int result = -1; char *localeptr; + if (!OidIsValid(collation)) + return false; + + if (collation != DEFAULT_COLLATION_OID) + return lookup_collation_cness(collation, LC_CTYPE); + if (result >= 0) return (bool) result; localeptr = setlocale(LC_CTYPE, NULL); @@ -483,7 +567,7 @@ PGLC_localeconv(void) /* Get formatting information for numeric */ setlocale(LC_NUMERIC, locale_numeric); extlconv = localeconv(); - encoding = pg_get_encoding_from_locale(locale_numeric); + encoding = pg_get_encoding_from_locale(locale_numeric, true); decimal_point = db_encoding_strdup(encoding, extlconv->decimal_point); thousands_sep = db_encoding_strdup(encoding, extlconv->thousands_sep); @@ -497,7 +581,7 @@ PGLC_localeconv(void) /* Get formatting information for monetary */ setlocale(LC_MONETARY, locale_monetary); extlconv = localeconv(); - encoding = pg_get_encoding_from_locale(locale_monetary); + encoding = pg_get_encoding_from_locale(locale_monetary, true); /* * Must copy all values since restoring internal settings may overwrite @@ -758,3 +842,118 @@ IsoLocaleName(const char *winlocname) } #endif /* WIN32 && LC_MESSAGES */ + + +#ifdef HAVE_LOCALE_T +struct locale_t_cache_entry +{ + Oid collid; + locale_t locale; +}; + +static void +init_locale_t_cache(void) +{ + HASHCTL ctl; + + memset(&ctl, 0, sizeof(ctl)); + ctl.keysize = sizeof(Oid); + ctl.entrysize = sizeof(struct locale_t_cache_entry); + ctl.hash = oid_hash; + locale_t_cache = hash_create("locale_t cache", 1000, &ctl, HASH_ELEM | HASH_FUNCTION); +} +#endif /* HAVE_LOCALE_T */ + +/* + * Create a locale_t from a collation OID. Results are cached for the + * lifetime of the backend. Thus, do not free the result with + * freelocale(). + * + * As a special optimization, the default/database collation returns + * 0. Callers should then revert to the non-locale_t-enabled code + * path. In fact, they shouldn't call this function at all when they + * are dealing with the default locale. That can save quite a bit in + * hotspots. + * + * For simplicity, we always generate COLLATE + CTYPE even though we + * might only need one of them. Since this is called only once per + * session, it shouldn't cost much. + */ +pg_locale_t +pg_newlocale_from_collation(Oid collid) +{ +#ifdef HAVE_LOCALE_T + HeapTuple tp; + const char *collcollate; + const char *collctype; + locale_t result; + struct locale_t_cache_entry *cache_entry; + bool found; + + if (collid == DEFAULT_COLLATION_OID) + return (locale_t) 0; + + if (!OidIsValid(collid)) + elog(ERROR, "locale operation to be invoked, but no collation was derived"); + + if (!locale_t_cache) + init_locale_t_cache(); + + cache_entry = hash_search(locale_t_cache, &collid, HASH_ENTER, &found); + if (found) + return cache_entry->locale; + + tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid)); + if (!HeapTupleIsValid(tp)) + elog(ERROR, "cache lookup failed for collation %u", collid); + + collcollate = NameStr(((Form_pg_collation) GETSTRUCT(tp))->collcollate); + collctype = NameStr(((Form_pg_collation) GETSTRUCT(tp))->collctype); + + if (strcmp(collcollate, collctype) == 0) + { + result = newlocale(LC_COLLATE_MASK | LC_CTYPE_MASK, collcollate, NULL); + if (!result) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not create locale \"%s\": %m", collcollate))); + } + else + { + locale_t loc1; + + loc1 = newlocale(LC_COLLATE_MASK, collcollate, NULL); + if (!loc1) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not create locale \"%s\": %m", collcollate))); + result = newlocale(LC_CTYPE_MASK, collctype, loc1); + if (!result) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not create locale \"%s\": %m", collctype))); + } + + ReleaseSysCache(tp); + + cache_entry->locale = result; + + return result; +#else /* not HAVE_LOCALE_T */ + /* + * For platforms that don't support locale_t, check that we are + * dealing with the default locale. It's unlikely that we'll get + * here, but it's possible if users are creating collations even + * though they are not supported, or they are mixing builds in odd + * ways. + */ + if (!OidIsValid(collid)) + elog(ERROR, "locale operation to be invoked, but no collation was derived"); + else if (collid != DEFAULT_COLLATION_OID) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("nondefault collations are not supported on this platform"))); + + return 0; +#endif /* not HAVE_LOCALE_T */ +} diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c index a95ba8b3eb..f8f9961514 100644 --- a/src/backend/utils/adt/pgstatfuncs.c +++ b/src/backend/utils/adt/pgstatfuncs.c @@ -77,12 +77,14 @@ extern Datum pg_stat_get_db_conflict_snapshot(PG_FUNCTION_ARGS); extern Datum pg_stat_get_db_conflict_bufferpin(PG_FUNCTION_ARGS); extern Datum pg_stat_get_db_conflict_startup_deadlock(PG_FUNCTION_ARGS); extern Datum pg_stat_get_db_conflict_all(PG_FUNCTION_ARGS); +extern Datum pg_stat_get_db_stat_reset_time(PG_FUNCTION_ARGS); extern Datum pg_stat_get_bgwriter_timed_checkpoints(PG_FUNCTION_ARGS); extern Datum pg_stat_get_bgwriter_requested_checkpoints(PG_FUNCTION_ARGS); extern Datum pg_stat_get_bgwriter_buf_written_checkpoints(PG_FUNCTION_ARGS); extern Datum pg_stat_get_bgwriter_buf_written_clean(PG_FUNCTION_ARGS); extern Datum pg_stat_get_bgwriter_maxwritten_clean(PG_FUNCTION_ARGS); +extern Datum pg_stat_get_bgwriter_stat_reset_time(PG_FUNCTION_ARGS); extern Datum pg_stat_get_buf_written_backend(PG_FUNCTION_ARGS); extern Datum pg_stat_get_buf_fsync_backend(PG_FUNCTION_ARGS); extern Datum pg_stat_get_buf_alloc(PG_FUNCTION_ARGS); @@ -1136,6 +1138,24 @@ pg_stat_get_db_tuples_deleted(PG_FUNCTION_ARGS) } Datum +pg_stat_get_db_stat_reset_time(PG_FUNCTION_ARGS) +{ + Oid dbid = PG_GETARG_OID(0); + TimestampTz result; + PgStat_StatDBEntry *dbentry; + + if ((dbentry = pgstat_fetch_stat_dbentry(dbid)) == NULL) + result = 0; + else + result = dbentry->stat_reset_timestamp; + + if (result == 0) + PG_RETURN_NULL(); + else + PG_RETURN_TIMESTAMPTZ(result); +} + +Datum pg_stat_get_db_conflict_tablespace(PG_FUNCTION_ARGS) { Oid dbid = PG_GETARG_OID(0); @@ -1261,6 +1281,12 @@ pg_stat_get_bgwriter_maxwritten_clean(PG_FUNCTION_ARGS) } Datum +pg_stat_get_bgwriter_stat_reset_time(PG_FUNCTION_ARGS) +{ + PG_RETURN_TIMESTAMPTZ(pgstat_fetch_global()->stat_reset_timestamp); +} + +Datum pg_stat_get_buf_written_backend(PG_FUNCTION_ARGS) { PG_RETURN_INT64(pgstat_fetch_global()->buf_written_backend); diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 7bc1ab1e81..5ef1563d1c 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -2608,8 +2608,11 @@ RI_FKey_keyequal_upd_fk(Trigger *trigger, Relation fk_rel, * This is not a trigger procedure, but is called during ALTER TABLE * ADD FOREIGN KEY to validate the initial table contents. * - * We expect that a ShareRowExclusiveLock or higher has been taken on rel and pkrel; - * hence, we do not need to lock individual rows for the check. + * We expect that the caller has made provision to prevent any problems + * caused by concurrent actions. This could be either by locking rel and + * pkrel at ShareRowExclusiveLock or higher, or by otherwise ensuring + * that triggers implementing the checks are already active. + * Hence, we do not need to lock individual rows for the check. * * If the check fails because the current user doesn't have permissions * to read both tables, return false to let our caller know that they will diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index b8259febb8..cd64235438 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -23,6 +23,7 @@ #include "catalog/dependency.h" #include "catalog/indexing.h" #include "catalog/pg_authid.h" +#include "catalog/pg_collation.h" #include "catalog/pg_constraint.h" #include "catalog/pg_depend.h" #include "catalog/pg_language.h" @@ -233,7 +234,7 @@ static void get_from_clause_item(Node *jtnode, Query *query, deparse_context *context); static void get_from_clause_alias(Alias *alias, RangeTblEntry *rte, deparse_context *context); -static void get_from_clause_coldeflist(List *names, List *types, List *typmods, +static void get_from_clause_coldeflist(List *names, List *types, List *typmods, List *collations, deparse_context *context); static void get_opclass_name(Oid opclass, Oid actual_datatype, StringInfo buf); @@ -788,9 +789,11 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, Oid indrelid; int keyno; Oid keycoltype; + Datum indcollDatum; Datum indclassDatum; Datum indoptionDatum; bool isnull; + oidvector *indcollation; oidvector *indclass; int2vector *indoption; StringInfoData buf; @@ -808,11 +811,17 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, indrelid = idxrec->indrelid; Assert(indexrelid == idxrec->indexrelid); - /* Must get indclass and indoption the hard way */ + /* Must get indcollation, indclass, and indoption the hard way */ + indcollDatum = SysCacheGetAttr(INDEXRELID, ht_idx, + Anum_pg_index_indcollation, &isnull); + Assert(!isnull); + indcollation = (oidvector *) DatumGetPointer(indcollDatum); + indclassDatum = SysCacheGetAttr(INDEXRELID, ht_idx, Anum_pg_index_indclass, &isnull); Assert(!isnull); indclass = (oidvector *) DatumGetPointer(indclassDatum); + indoptionDatum = SysCacheGetAttr(INDEXRELID, ht_idx, Anum_pg_index_indoption, &isnull); Assert(!isnull); @@ -928,6 +937,13 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, if (!attrsOnly && (!colno || colno == keyno + 1)) { + Oid coll; + + /* Add collation, if not default */ + coll = indcollation->values[keyno]; + if (coll && coll != DEFAULT_COLLATION_OID && coll != get_attcollation(indrelid, attnum)) + appendStringInfo(&buf, " COLLATE %s", generate_collation_name((indcollation->values[keyno]))); + /* Add the operator class name, if not default */ get_opclass_name(indclass->values[keyno], keycoltype, &buf); @@ -5054,6 +5070,20 @@ get_rule_expr(Node *node, deparse_context *context, } break; + case T_CollateClause: + { + CollateClause *collate = (CollateClause *) node; + Node *arg = (Node *) collate->arg; + + if (!PRETTY_PAREN(context)) + appendStringInfoChar(buf, '('); + get_rule_expr_paren(arg, context, false, node); + appendStringInfo(buf, " COLLATE %s", generate_collation_name(collate->collOid)); + if (!PRETTY_PAREN(context)) + appendStringInfoChar(buf, ')'); + } + break; + case T_CoerceViaIO: { CoerceViaIO *iocoerce = (CoerceViaIO *) node; @@ -6345,6 +6375,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context) get_from_clause_coldeflist(rte->eref->colnames, rte->funccoltypes, rte->funccoltypmods, + rte->funccolcollations, context); } else @@ -6543,35 +6574,42 @@ get_from_clause_alias(Alias *alias, RangeTblEntry *rte, * responsible for ensuring that an alias or AS is present before it. */ static void -get_from_clause_coldeflist(List *names, List *types, List *typmods, +get_from_clause_coldeflist(List *names, List *types, List *typmods, List *collations, deparse_context *context) { StringInfo buf = context->buf; ListCell *l1; ListCell *l2; ListCell *l3; + ListCell *l4; int i = 0; appendStringInfoChar(buf, '('); l2 = list_head(types); l3 = list_head(typmods); + l4 = list_head(collations); foreach(l1, names) { char *attname = strVal(lfirst(l1)); Oid atttypid; int32 atttypmod; + Oid attcollation; atttypid = lfirst_oid(l2); l2 = lnext(l2); atttypmod = lfirst_int(l3); l3 = lnext(l3); + attcollation = lfirst_oid(l4); + l4 = lnext(l4); if (i > 0) appendStringInfo(buf, ", "); appendStringInfo(buf, "%s %s", quote_identifier(attname), format_type_with_typemod(atttypid, atttypmod)); + if (attcollation && attcollation != DEFAULT_COLLATION_OID) + appendStringInfo(buf, " COLLATE %s", generate_collation_name(attcollation)); i++; } @@ -7039,6 +7077,39 @@ generate_operator_name(Oid operid, Oid arg1, Oid arg2) } /* + * generate_collation_name + * Compute the name to display for a collation specified by OID + * + * The result includes all necessary quoting and schema-prefixing. + */ +char * +generate_collation_name(Oid collid) +{ + HeapTuple tp; + Form_pg_collation colltup; + char *collname; + char *nspname; + char *result; + + tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid)); + if (!HeapTupleIsValid(tp)) + elog(ERROR, "cache lookup failed for collation %u", collid); + colltup = (Form_pg_collation) GETSTRUCT(tp); + collname = NameStr(colltup->collname); + + if (!CollationIsVisible(collid)) + nspname = get_namespace_name(colltup->collnamespace); + else + nspname = NULL; + + result = quote_qualified_identifier(nspname, collname); + + ReleaseSysCache(tp); + + return result; +} + +/* * Given a C string, produce a TEXT datum. * * We assume that the input was palloc'd and may be freed. diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index 7e3ff864c8..f10110b1b7 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -94,6 +94,7 @@ #include "access/gin.h" #include "access/sysattr.h" #include "catalog/index.h" +#include "catalog/pg_collation.h" #include "catalog/pg_opfamily.h" #include "catalog/pg_statistic.h" #include "catalog/pg_type.h" @@ -144,7 +145,7 @@ static double eqjoinsel_inner(Oid operator, static double eqjoinsel_semi(Oid operator, VariableStatData *vardata1, VariableStatData *vardata2); static bool convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue, - Datum lobound, Datum hibound, Oid boundstypid, + Datum lobound, Datum hibound, Oid boundstypid, Oid boundscollid, double *scaledlobound, double *scaledhibound); static double convert_numeric_to_scalar(Datum value, Oid typid); static void convert_string_to_scalar(char *value, @@ -163,10 +164,10 @@ static double convert_one_string_to_scalar(char *value, int rangelo, int rangehi); static double convert_one_bytea_to_scalar(unsigned char *value, int valuelen, int rangelo, int rangehi); -static char *convert_string_datum(Datum value, Oid typid); +static char *convert_string_datum(Datum value, Oid typid, Oid collid); static double convert_timevalue_to_scalar(Datum value, Oid typid); static bool get_variable_range(PlannerInfo *root, VariableStatData *vardata, - Oid sortop, Datum *min, Datum *max); + Oid sortop, Oid collation, Datum *min, Datum *max); static bool get_actual_variable_range(PlannerInfo *root, VariableStatData *vardata, Oid sortop, @@ -513,6 +514,7 @@ scalarineqsel(PlannerInfo *root, Oid operator, bool isgt, stats = (Form_pg_statistic) GETSTRUCT(vardata->statsTuple); fmgr_info(get_opcode(operator), &opproc); + fmgr_info_collation(vardata->attcollation, &opproc); /* * If we have most-common-values info, add up the fractions of the MCV @@ -837,7 +839,7 @@ ineq_histogram_selectivity(PlannerInfo *root, */ if (convert_to_scalar(constval, consttype, &val, values[i - 1], values[i], - vardata->vartype, + vardata->vartype, vardata->attcollation, &low, &high)) { if (high <= low) @@ -1249,6 +1251,7 @@ patternsel(PG_FUNCTION_ARGS, Pattern_Type ptype, bool negate) /* Try to use the histogram entries to get selectivity */ fmgr_info(get_opcode(operator), &opproc); + fmgr_info_collation(DEFAULT_COLLATION_OID, &opproc); selec = histogram_selectivity(&vardata, &opproc, constval, true, 10, 1, &hist_size); @@ -2585,7 +2588,7 @@ icnlikejoinsel(PG_FUNCTION_ARGS) */ void mergejoinscansel(PlannerInfo *root, Node *clause, - Oid opfamily, int strategy, bool nulls_first, + Oid opfamily, Oid collation, int strategy, bool nulls_first, Selectivity *leftstart, Selectivity *leftend, Selectivity *rightstart, Selectivity *rightend) { @@ -2754,20 +2757,20 @@ mergejoinscansel(PlannerInfo *root, Node *clause, /* Try to get ranges of both inputs */ if (!isgt) { - if (!get_variable_range(root, &leftvar, lstatop, + if (!get_variable_range(root, &leftvar, lstatop, collation, &leftmin, &leftmax)) goto fail; /* no range available from stats */ - if (!get_variable_range(root, &rightvar, rstatop, + if (!get_variable_range(root, &rightvar, rstatop, collation, &rightmin, &rightmax)) goto fail; /* no range available from stats */ } else { /* need to swap the max and min */ - if (!get_variable_range(root, &leftvar, lstatop, + if (!get_variable_range(root, &leftvar, lstatop, collation, &leftmax, &leftmin)) goto fail; /* no range available from stats */ - if (!get_variable_range(root, &rightvar, rstatop, + if (!get_variable_range(root, &rightvar, rstatop, collation, &rightmax, &rightmin)) goto fail; /* no range available from stats */ } @@ -3368,7 +3371,7 @@ estimate_hash_bucketsize(PlannerInfo *root, Node *hashkey, double nbuckets) */ static bool convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue, - Datum lobound, Datum hibound, Oid boundstypid, + Datum lobound, Datum hibound, Oid boundstypid, Oid boundscollid, double *scaledlobound, double *scaledhibound) { /* @@ -3421,9 +3424,9 @@ convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue, case TEXTOID: case NAMEOID: { - char *valstr = convert_string_datum(value, valuetypid); - char *lostr = convert_string_datum(lobound, boundstypid); - char *histr = convert_string_datum(hibound, boundstypid); + char *valstr = convert_string_datum(value, valuetypid, boundscollid); + char *lostr = convert_string_datum(lobound, boundstypid, boundscollid); + char *histr = convert_string_datum(hibound, boundstypid, boundscollid); convert_string_to_scalar(valstr, scaledvalue, lostr, scaledlobound, @@ -3667,7 +3670,7 @@ convert_one_string_to_scalar(char *value, int rangelo, int rangehi) * before continuing, so as to generate correct locale-specific results. */ static char * -convert_string_datum(Datum value, Oid typid) +convert_string_datum(Datum value, Oid typid, Oid collid) { char *val; @@ -3700,7 +3703,7 @@ convert_string_datum(Datum value, Oid typid) return NULL; } - if (!lc_collate_is_c()) + if (!lc_collate_is_c(collid)) { char *xfrmstr; size_t xfrmlen; @@ -4099,6 +4102,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, vardata->rel = find_base_rel(root, var->varno); vardata->atttype = var->vartype; vardata->atttypmod = var->vartypmod; + vardata->attcollation = var->varcollid; vardata->isunique = has_unique_index(vardata->rel, var->varattno); rte = root->simple_rte_array[var->varno]; @@ -4184,6 +4188,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid, vardata->var = node; vardata->atttype = exprType(node); vardata->atttypmod = exprTypmod(node); + vardata->attcollation = exprCollation(node); if (onerel) { @@ -4392,7 +4397,7 @@ get_variable_numdistinct(VariableStatData *vardata) * be "<" not ">", as only the former is likely to be found in pg_statistic. */ static bool -get_variable_range(PlannerInfo *root, VariableStatData *vardata, Oid sortop, +get_variable_range(PlannerInfo *root, VariableStatData *vardata, Oid sortop, Oid collation, Datum *min, Datum *max) { Datum tmin = 0; @@ -4477,6 +4482,7 @@ get_variable_range(PlannerInfo *root, VariableStatData *vardata, Oid sortop, FmgrInfo opproc; fmgr_info(get_opcode(sortop), &opproc); + fmgr_info_collation(collation, &opproc); for (i = 0; i < nvalues; i++) { @@ -4556,10 +4562,10 @@ get_actual_variable_range(PlannerInfo *root, VariableStatData *vardata, continue; /* - * The index list might include fictitious indexes inserted by a + * The index list might include hypothetical indexes inserted by a * get_relation_info hook --- don't try to access them. */ - if (!OidIsValid(index->indexoid)) + if (index->hypothetical) continue; /* @@ -5482,7 +5488,7 @@ make_greater_string(const Const *str_const, FmgrInfo *ltproc) { workstr = TextDatumGetCString(str_const->constvalue); len = strlen(workstr); - if (lc_collate_is_c() || len == 0) + if (lc_collate_is_c(ltproc->fn_collation) || len == 0) cmpstr = str_const->constvalue; else { @@ -5494,11 +5500,11 @@ make_greater_string(const Const *str_const, FmgrInfo *ltproc) char *best; best = "Z"; - if (varstr_cmp(best, 1, "z", 1) < 0) + if (varstr_cmp(best, 1, "z", 1, DEFAULT_COLLATION_OID) < 0) best = "z"; - if (varstr_cmp(best, 1, "y", 1) < 0) + if (varstr_cmp(best, 1, "y", 1, DEFAULT_COLLATION_OID) < 0) best = "y"; - if (varstr_cmp(best, 1, "9", 1) < 0) + if (varstr_cmp(best, 1, "9", 1, DEFAULT_COLLATION_OID) < 0) best = "9"; suffixchar = *best; } diff --git a/src/backend/utils/adt/tsginidx.c b/src/backend/utils/adt/tsginidx.c index 56cd9b70d2..41700bfcf1 100644 --- a/src/backend/utils/adt/tsginidx.c +++ b/src/backend/utils/adt/tsginidx.c @@ -230,3 +230,43 @@ gin_tsquery_consistent(PG_FUNCTION_ARGS) PG_RETURN_BOOL(res); } + +/* + * Formerly, gin_extract_tsvector had only two arguments. Now it has three, + * but we still need a pg_proc entry with two args to support reloading + * pre-9.1 contrib/tsearch2 opclass declarations. This compatibility + * function should go away eventually. (Note: you might say "hey, but the + * code above is only *using* two args, so let's just declare it that way". + * If you try that you'll find the opr_sanity regression test complains.) + */ +Datum +gin_extract_tsvector_2args(PG_FUNCTION_ARGS) +{ + if (PG_NARGS() < 3) /* should not happen */ + elog(ERROR, "gin_extract_tsvector requires three arguments"); + return gin_extract_tsvector(fcinfo); +} + +/* + * Likewise, we need a stub version of gin_extract_tsquery declared with + * only five arguments. + */ +Datum +gin_extract_tsquery_5args(PG_FUNCTION_ARGS) +{ + if (PG_NARGS() < 7) /* should not happen */ + elog(ERROR, "gin_extract_tsquery requires seven arguments"); + return gin_extract_tsquery(fcinfo); +} + +/* + * Likewise, we need a stub version of gin_tsquery_consistent declared with + * only six arguments. + */ +Datum +gin_tsquery_consistent_6args(PG_FUNCTION_ARGS) +{ + if (PG_NARGS() < 8) /* should not happen */ + elog(ERROR, "gin_tsquery_consistent requires eight arguments"); + return gin_tsquery_consistent(fcinfo); +} diff --git a/src/backend/utils/adt/varchar.c b/src/backend/utils/adt/varchar.c index 08be966249..1c0ef921a7 100644 --- a/src/backend/utils/adt/varchar.c +++ b/src/backend/utils/adt/varchar.c @@ -737,7 +737,8 @@ bpcharlt(PG_FUNCTION_ARGS) len1 = bcTruelen(arg1); len2 = bcTruelen(arg2); - cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2); + cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2, + PG_GET_COLLATION()); PG_FREE_IF_COPY(arg1, 0); PG_FREE_IF_COPY(arg2, 1); @@ -757,7 +758,8 @@ bpcharle(PG_FUNCTION_ARGS) len1 = bcTruelen(arg1); len2 = bcTruelen(arg2); - cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2); + cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2, + PG_GET_COLLATION()); PG_FREE_IF_COPY(arg1, 0); PG_FREE_IF_COPY(arg2, 1); @@ -777,7 +779,8 @@ bpchargt(PG_FUNCTION_ARGS) len1 = bcTruelen(arg1); len2 = bcTruelen(arg2); - cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2); + cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2, + PG_GET_COLLATION()); PG_FREE_IF_COPY(arg1, 0); PG_FREE_IF_COPY(arg2, 1); @@ -797,7 +800,8 @@ bpcharge(PG_FUNCTION_ARGS) len1 = bcTruelen(arg1); len2 = bcTruelen(arg2); - cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2); + cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2, + PG_GET_COLLATION()); PG_FREE_IF_COPY(arg1, 0); PG_FREE_IF_COPY(arg2, 1); @@ -817,7 +821,8 @@ bpcharcmp(PG_FUNCTION_ARGS) len1 = bcTruelen(arg1); len2 = bcTruelen(arg2); - cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2); + cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2, + PG_GET_COLLATION()); PG_FREE_IF_COPY(arg1, 0); PG_FREE_IF_COPY(arg2, 1); @@ -837,7 +842,8 @@ bpchar_larger(PG_FUNCTION_ARGS) len1 = bcTruelen(arg1); len2 = bcTruelen(arg2); - cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2); + cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2, + PG_GET_COLLATION()); PG_RETURN_BPCHAR_P((cmp >= 0) ? arg1 : arg2); } @@ -854,7 +860,8 @@ bpchar_smaller(PG_FUNCTION_ARGS) len1 = bcTruelen(arg1); len2 = bcTruelen(arg2); - cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2); + cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2, + PG_GET_COLLATION()); PG_RETURN_BPCHAR_P((cmp <= 0) ? arg1 : arg2); } diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c index e111d2650b..8a7a3cf45b 100644 --- a/src/backend/utils/adt/varlena.c +++ b/src/backend/utils/adt/varlena.c @@ -18,6 +18,7 @@ #include <limits.h> #include "access/tuptoaster.h" +#include "catalog/pg_collation.h" #include "catalog/pg_type.h" #include "libpq/md5.h" #include "libpq/pqformat.h" @@ -55,7 +56,7 @@ typedef struct #define PG_GETARG_UNKNOWN_P_COPY(n) DatumGetUnknownPCopy(PG_GETARG_DATUM(n)) #define PG_RETURN_UNKNOWN_P(x) PG_RETURN_POINTER(x) -static int text_cmp(text *arg1, text *arg2); +static int text_cmp(text *arg1, text *arg2, Oid collid); static int32 text_length(Datum str); static int text_position(text *t1, text *t2); static void text_position_setup(text *t1, text *t2, TextPositionState *state); @@ -1274,7 +1275,7 @@ text_position_cleanup(TextPositionState *state) * whether arg1 is less than, equal to, or greater than arg2. */ int -varstr_cmp(char *arg1, int len1, char *arg2, int len2) +varstr_cmp(char *arg1, int len1, char *arg2, int len2, Oid collid) { int result; @@ -1284,7 +1285,7 @@ varstr_cmp(char *arg1, int len1, char *arg2, int len2) * slower, so we optimize the case where LC_COLLATE is C. We also try to * optimize relatively-short strings by avoiding palloc/pfree overhead. */ - if (lc_collate_is_c()) + if (lc_collate_is_c(collid)) { result = memcmp(arg1, arg2, Min(len1, len2)); if ((result == 0) && (len1 != len2)) @@ -1298,6 +1299,10 @@ varstr_cmp(char *arg1, int len1, char *arg2, int len2) char a2buf[STACKBUFLEN]; char *a1p, *a2p; + pg_locale_t mylocale = 0; + + if (collid != DEFAULT_COLLATION_OID) + mylocale = pg_newlocale_from_collation(collid); #ifdef WIN32 /* Win32 does not have UTF-8, so we need to map to UTF-16 */ @@ -1398,6 +1403,11 @@ varstr_cmp(char *arg1, int len1, char *arg2, int len2) memcpy(a2p, arg2, len2); a2p[len2] = '\0'; +#ifdef HAVE_LOCALE_T + if (mylocale) + result = strcoll_l(a1p, a2p, mylocale); + else +#endif result = strcoll(a1p, a2p); /* @@ -1424,7 +1434,7 @@ varstr_cmp(char *arg1, int len1, char *arg2, int len2) * Returns -1, 0 or 1 */ static int -text_cmp(text *arg1, text *arg2) +text_cmp(text *arg1, text *arg2, Oid collid) { char *a1p, *a2p; @@ -1437,7 +1447,7 @@ text_cmp(text *arg1, text *arg2) len1 = VARSIZE_ANY_EXHDR(arg1); len2 = VARSIZE_ANY_EXHDR(arg2); - return varstr_cmp(a1p, len1, a2p, len2); + return varstr_cmp(a1p, len1, a2p, len2, collid); } /* @@ -1519,7 +1529,7 @@ text_lt(PG_FUNCTION_ARGS) text *arg2 = PG_GETARG_TEXT_PP(1); bool result; - result = (text_cmp(arg1, arg2) < 0); + result = (text_cmp(arg1, arg2, PG_GET_COLLATION()) < 0); PG_FREE_IF_COPY(arg1, 0); PG_FREE_IF_COPY(arg2, 1); @@ -1534,7 +1544,7 @@ text_le(PG_FUNCTION_ARGS) text *arg2 = PG_GETARG_TEXT_PP(1); bool result; - result = (text_cmp(arg1, arg2) <= 0); + result = (text_cmp(arg1, arg2, PG_GET_COLLATION()) <= 0); PG_FREE_IF_COPY(arg1, 0); PG_FREE_IF_COPY(arg2, 1); @@ -1549,7 +1559,7 @@ text_gt(PG_FUNCTION_ARGS) text *arg2 = PG_GETARG_TEXT_PP(1); bool result; - result = (text_cmp(arg1, arg2) > 0); + result = (text_cmp(arg1, arg2, PG_GET_COLLATION()) > 0); PG_FREE_IF_COPY(arg1, 0); PG_FREE_IF_COPY(arg2, 1); @@ -1564,7 +1574,7 @@ text_ge(PG_FUNCTION_ARGS) text *arg2 = PG_GETARG_TEXT_PP(1); bool result; - result = (text_cmp(arg1, arg2) >= 0); + result = (text_cmp(arg1, arg2, PG_GET_COLLATION()) >= 0); PG_FREE_IF_COPY(arg1, 0); PG_FREE_IF_COPY(arg2, 1); @@ -1579,7 +1589,7 @@ bttextcmp(PG_FUNCTION_ARGS) text *arg2 = PG_GETARG_TEXT_PP(1); int32 result; - result = text_cmp(arg1, arg2); + result = text_cmp(arg1, arg2, PG_GET_COLLATION()); PG_FREE_IF_COPY(arg1, 0); PG_FREE_IF_COPY(arg2, 1); @@ -1595,7 +1605,7 @@ text_larger(PG_FUNCTION_ARGS) text *arg2 = PG_GETARG_TEXT_PP(1); text *result; - result = ((text_cmp(arg1, arg2) > 0) ? arg1 : arg2); + result = ((text_cmp(arg1, arg2, PG_GET_COLLATION()) > 0) ? arg1 : arg2); PG_RETURN_TEXT_P(result); } @@ -1607,7 +1617,7 @@ text_smaller(PG_FUNCTION_ARGS) text *arg2 = PG_GETARG_TEXT_PP(1); text *result; - result = ((text_cmp(arg1, arg2) < 0) ? arg1 : arg2); + result = ((text_cmp(arg1, arg2, PG_GET_COLLATION()) < 0) ? arg1 : arg2); PG_RETURN_TEXT_P(result); } diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index 0a4144ba54..6af23429ad 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -20,6 +20,7 @@ #include "bootstrap/bootstrap.h" #include "catalog/pg_amop.h" #include "catalog/pg_amproc.h" +#include "catalog/pg_collation.h" #include "catalog/pg_constraint.h" #include "catalog/pg_namespace.h" #include "catalog/pg_opclass.h" @@ -903,6 +904,33 @@ get_atttypmod(Oid relid, AttrNumber attnum) } /* + * get_attcollation + * + * Given the relation id and the attribute number, + * return the "attcollation" field from the attribute relation. + */ +Oid +get_attcollation(Oid relid, AttrNumber attnum) +{ + HeapTuple tp; + + tp = SearchSysCache2(ATTNUM, + ObjectIdGetDatum(relid), + Int16GetDatum(attnum)); + if (HeapTupleIsValid(tp)) + { + Form_pg_attribute att_tup = (Form_pg_attribute) GETSTRUCT(tp); + Oid result; + + result = att_tup->attcollation; + ReleaseSysCache(tp); + return result; + } + else + return InvalidOid; +} + +/* * get_atttypetypmod * * A two-fer: given the relation id and the attribute number, @@ -931,6 +959,36 @@ get_atttypetypmod(Oid relid, AttrNumber attnum, ReleaseSysCache(tp); } +/* ---------- COLLATION CACHE ---------- */ + +/* + * get_collation_name + * Returns the name of a given pg_collation entry. + * + * Returns a palloc'd copy of the string, or NULL if no such constraint. + * + * NOTE: since collation name is not unique, be wary of code that uses this + * for anything except preparing error messages. + */ +char * +get_collation_name(Oid colloid) +{ + HeapTuple tp; + + tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(colloid)); + if (HeapTupleIsValid(tp)) + { + Form_pg_collation colltup = (Form_pg_collation) GETSTRUCT(tp); + char *result; + + result = pstrdup(NameStr(colltup->collname)); + ReleaseSysCache(tp); + return result; + } + else + return NULL; +} + /* ---------- CONSTRAINT CACHE ---------- */ /* @@ -2523,6 +2581,42 @@ get_typmodout(Oid typid) } #endif /* NOT_USED */ +/* + * get_typcollation + * + * Given the type OID, return the type's typcollation attribute. + */ +Oid +get_typcollation(Oid typid) +{ + HeapTuple tp; + + tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid)); + if (HeapTupleIsValid(tp)) + { + Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp); + Oid result; + + result = typtup->typcollation; + ReleaseSysCache(tp); + return result; + } + else + return InvalidOid; +} + + +/* + * type_is_collatable + * + * Return whether the type cares about collations + */ +bool +type_is_collatable(Oid typid) +{ + return OidIsValid(get_typcollation(typid)); +} + /* ---------- STATISTICS CACHE ---------- */ diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 3b40acf4df..90464fd066 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -976,9 +976,11 @@ RelationInitIndexAccessInfo(Relation relation) { HeapTuple tuple; Form_pg_am aform; + Datum indcollDatum; Datum indclassDatum; Datum indoptionDatum; bool isnull; + oidvector *indcoll; oidvector *indclass; int2vector *indoption; MemoryContext indexcxt; @@ -1061,10 +1063,26 @@ RelationInitIndexAccessInfo(Relation relation) relation->rd_supportinfo = NULL; } + relation->rd_indcollation = (Oid *) + MemoryContextAllocZero(indexcxt, natts * sizeof(Oid)); + relation->rd_indoption = (int16 *) MemoryContextAllocZero(indexcxt, natts * sizeof(int16)); /* + * indcollation cannot be referenced directly through the C struct, because it + * comes after the variable-width indkey field. Must extract the datum + * the hard way... + */ + indcollDatum = fastgetattr(relation->rd_indextuple, + Anum_pg_index_indcollation, + GetPgIndexDescriptor(), + &isnull); + Assert(!isnull); + indcoll = (oidvector *) DatumGetPointer(indcollDatum); + memcpy(relation->rd_indcollation, indcoll->values, natts * sizeof(Oid)); + + /* * indclass cannot be referenced directly through the C struct, because it * comes after the variable-width indkey field. Must extract the datum * the hard way... @@ -3988,6 +4006,7 @@ load_relcache_init_file(bool shared) RegProcedure *support; int nsupport; int16 *indoption; + Oid *indcollation; /* Count nailed indexes to ensure we have 'em all */ if (rel->rd_isnailed) @@ -4054,6 +4073,16 @@ load_relcache_init_file(bool shared) rel->rd_support = support; + /* next, read the vector of collation OIDs */ + if (fread(&len, 1, sizeof(len), fp) != sizeof(len)) + goto read_failed; + + indcollation = (Oid *) MemoryContextAlloc(indexcxt, len); + if (fread(indcollation, 1, len, fp) != len) + goto read_failed; + + rel->rd_indcollation = indcollation; + /* finally, read the vector of indoption values */ if (fread(&len, 1, sizeof(len), fp) != sizeof(len)) goto read_failed; @@ -4087,6 +4116,7 @@ load_relcache_init_file(bool shared) Assert(rel->rd_support == NULL); Assert(rel->rd_supportinfo == NULL); Assert(rel->rd_indoption == NULL); + Assert(rel->rd_indcollation == NULL); } /* @@ -4305,6 +4335,11 @@ write_relcache_init_file(bool shared) relform->relnatts * (am->amsupport * sizeof(RegProcedure)), fp); + /* next, write the vector of collation OIDs */ + write_item(rel->rd_indcollation, + relform->relnatts * sizeof(Oid), + fp); + /* finally, write the vector of indoption values */ write_item(rel->rd_indoption, relform->relnatts * sizeof(int16), diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c index 191953b972..715341f842 100644 --- a/src/backend/utils/cache/syscache.c +++ b/src/backend/utils/cache/syscache.c @@ -28,6 +28,7 @@ #include "catalog/pg_auth_members.h" #include "catalog/pg_authid.h" #include "catalog/pg_cast.h" +#include "catalog/pg_collation.h" #include "catalog/pg_constraint.h" #include "catalog/pg_conversion.h" #include "catalog/pg_database.h" @@ -267,6 +268,28 @@ static const struct cachedesc cacheinfo[] = { }, 64 }, + {CollationRelationId, /* COLLNAMEENCNSP */ + CollationNameEncNspIndexId, + 3, + { + Anum_pg_collation_collname, + Anum_pg_collation_collencoding, + Anum_pg_collation_collnamespace, + 0 + }, + 256 + }, + {CollationRelationId, /* COLLOID */ + CollationOidIndexId, + 1, + { + ObjectIdAttributeNumber, + 0, + 0, + 0 + }, + 256 + }, {ConversionRelationId, /* CONDEFAULT */ ConversionDefaultIndexId, 4, diff --git a/src/backend/utils/errcodes.txt b/src/backend/utils/errcodes.txt index 6f1d766859..0315f6b6f0 100644 --- a/src/backend/utils/errcodes.txt +++ b/src/backend/utils/errcodes.txt @@ -310,6 +310,8 @@ Section: Class 42 - Syntax Error or Access Rule Violation 42939 E ERRCODE_RESERVED_NAME reserved_name 42804 E ERRCODE_DATATYPE_MISMATCH datatype_mismatch 42P18 E ERRCODE_INDETERMINATE_DATATYPE indeterminate_datatype +42P21 E ERRCODE_COLLATION_MISMATCH collation_mismatch +42P22 E ERRCODE_INDETERMINATE_COLLATION indeterminate_collation 42809 E ERRCODE_WRONG_OBJECT_TYPE wrong_object_type # Note: for ERRCODE purposes, we divide namable objects into these categories: diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c index 54d50e9637..d05e4d2dd8 100644 --- a/src/backend/utils/fmgr/fmgr.c +++ b/src/backend/utils/fmgr/fmgr.c @@ -192,6 +192,7 @@ fmgr_info_cxt_security(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt, * elogs. */ finfo->fn_oid = InvalidOid; + finfo->fn_collation = InvalidOid; finfo->fn_extra = NULL; finfo->fn_mcxt = mcxt; finfo->fn_expr = NULL; /* caller may set this later */ @@ -420,6 +421,25 @@ fmgr_info_other_lang(Oid functionId, FmgrInfo *finfo, HeapTuple procedureTuple) } /* + * Initialize the fn_collation field + */ +void +fmgr_info_collation(Oid collationId, FmgrInfo *finfo) +{ + finfo->fn_collation = collationId; +} + +/* + * Initialize the fn_expr field and set the collation based on it + */ +void +fmgr_info_expr(Node *expr, FmgrInfo *finfo) +{ + finfo->fn_expr = expr; + finfo->fn_collation = exprCollation(expr); +} + +/* * Fetch and validate the information record for the given external function. * The function is specified by a handle for the containing library * (obtained from load_external_function) as well as the function name. @@ -1273,6 +1293,52 @@ DirectFunctionCall9(PGFunction func, Datum arg1, Datum arg2, return result; } +Datum +DirectFunctionCall1WithCollation(PGFunction func, Oid collation, Datum arg1) +{ + FunctionCallInfoData fcinfo; + FmgrInfo flinfo; + Datum result; + + InitFunctionCallInfoData(fcinfo, &flinfo, 1, NULL, NULL); + fcinfo.flinfo->fn_collation = collation; + + fcinfo.arg[0] = arg1; + fcinfo.argnull[0] = false; + + result = (*func) (&fcinfo); + + /* Check for null result, since caller is clearly not expecting one */ + if (fcinfo.isnull) + elog(ERROR, "function %p returned NULL", (void *) func); + + return result; +} + +Datum +DirectFunctionCall2WithCollation(PGFunction func, Oid collation, Datum arg1, Datum arg2) +{ + FunctionCallInfoData fcinfo; + FmgrInfo flinfo; + Datum result; + + InitFunctionCallInfoData(fcinfo, &flinfo, 2, NULL, NULL); + fcinfo.flinfo->fn_collation = collation; + + fcinfo.arg[0] = arg1; + fcinfo.arg[1] = arg2; + fcinfo.argnull[0] = false; + fcinfo.argnull[1] = false; + + result = (*func) (&fcinfo); + + /* Check for null result, since caller is clearly not expecting one */ + if (fcinfo.isnull) + elog(ERROR, "function %p returned NULL", (void *) func); + + return result; +} + /* * These are for invocation of a previously-looked-up function with a diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c index e32c716392..321b4e7f8f 100644 --- a/src/backend/utils/fmgr/funcapi.c +++ b/src/backend/utils/fmgr/funcapi.c @@ -468,7 +468,6 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, /* If nothing found, parser messed up */ if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type)) return false; - /* If needed, deduce one polymorphic type from the other */ if (have_anyelement_result && !OidIsValid(anyelement_type)) anyelement_type = resolve_generic_type(ANYELEMENTOID, @@ -511,6 +510,9 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, default: break; } + /* Set collation based on actual argument types */ + TupleDescInitEntryCollation(tupdesc, i + 1, + exprCollation(call_expr)); } return true; diff --git a/src/backend/utils/mb/mbutils.c b/src/backend/utils/mb/mbutils.c index a04181286a..5ee74f747d 100644 --- a/src/backend/utils/mb/mbutils.c +++ b/src/backend/utils/mb/mbutils.c @@ -629,7 +629,7 @@ perform_default_encoding_conversion(const char *src, int len, bool is_client_to_ * zero-terminated. The output will be zero-terminated iff there is room. */ size_t -wchar2char(char *to, const wchar_t *from, size_t tolen) +wchar2char(char *to, const wchar_t *from, size_t tolen, Oid collation) { size_t result; @@ -660,7 +660,7 @@ wchar2char(char *to, const wchar_t *from, size_t tolen) else #endif /* WIN32 */ { - Assert(!lc_ctype_is_c()); + Assert(!lc_ctype_is_c(collation)); result = wcstombs(to, from, tolen); } return result; @@ -676,7 +676,7 @@ wchar2char(char *to, const wchar_t *from, size_t tolen) * The output will be zero-terminated iff there is room. */ size_t -char2wchar(wchar_t *to, size_t tolen, const char *from, size_t fromlen) +char2wchar(wchar_t *to, size_t tolen, const char *from, size_t fromlen, Oid collation) { size_t result; @@ -711,7 +711,7 @@ char2wchar(wchar_t *to, size_t tolen, const char *from, size_t fromlen) /* mbstowcs requires ending '\0' */ char *str = pnstrdup(from, fromlen); - Assert(!lc_ctype_is_c()); + Assert(!lc_ctype_is_c(collation)); result = mbstowcs(to, str, tolen); pfree(str); } @@ -983,7 +983,7 @@ GetPlatformEncoding(void) if (PlatformEncoding == NULL) { /* try to determine encoding of server's environment locale */ - int encoding = pg_get_encoding_from_locale(""); + int encoding = pg_get_encoding_from_locale("", true); if (encoding < 0) encoding = PG_SQL_ASCII; diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 216236b529..55cbf757b4 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -55,6 +55,7 @@ #include "postmaster/postmaster.h" #include "postmaster/syslogger.h" #include "postmaster/walwriter.h" +#include "replication/walreceiver.h" #include "replication/walsender.h" #include "storage/bufmgr.h" #include "storage/standby.h" @@ -1278,6 +1279,15 @@ static struct config_bool ConfigureNamesBool[] = }, { + {"hot_standby_feedback", PGC_SIGHUP, WAL_STANDBY_SERVERS, + gettext_noop("Allows feedback from a hot standby primary that will avoid query conflicts."), + NULL + }, + &hot_standby_feedback, + false, NULL, NULL + }, + + { {"allow_system_table_mods", PGC_POSTMASTER, DEVELOPER_OPTIONS, gettext_noop("Allows modifications of the structure of system tables."), NULL, @@ -1441,6 +1451,16 @@ static struct config_int ConfigureNamesInt[] = }, { + {"wal_receiver_status_interval", PGC_SIGHUP, WAL_STANDBY_SERVERS, + gettext_noop("Sets the maximum interval between WAL receiver status reports to the master."), + NULL, + GUC_UNIT_S + }, + &wal_receiver_status_interval, + 10, 0, INT_MAX/1000, NULL, NULL + }, + + { {"max_connections", PGC_POSTMASTER, CONN_AUTH_SETTINGS, gettext_noop("Sets the maximum number of concurrent connections."), NULL @@ -1714,10 +1734,10 @@ static struct config_int ConfigureNamesInt[] = }, { - {"max_predicate_locks_per_transaction", PGC_POSTMASTER, LOCK_MANAGEMENT, + {"max_pred_locks_per_transaction", PGC_POSTMASTER, LOCK_MANAGEMENT, gettext_noop("Sets the maximum number of predicate locks per transaction."), gettext_noop("The shared predicate lock table is sized on the assumption that " - "at most max_predicate_locks_per_transaction * max_connections distinct " + "at most max_pred_locks_per_transaction * max_connections distinct " "objects will need to be locked at any one time.") }, &max_predicate_locks_per_xact, diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index fe80c4dc23..6726733235 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -196,12 +196,14 @@ #hot_standby = off # "on" allows queries during recovery # (change requires restart) +#hot_standby_feedback = off # info from standby to prevent query conflicts #max_standby_archive_delay = 30s # max delay before canceling queries # when reading WAL from archive; # -1 allows indefinite delay #max_standby_streaming_delay = 30s # max delay before canceling queries # when reading streaming WAL; # -1 allows indefinite delay +#wal_receiver_status_interval = 10s # replies at least this often, 0 disables #------------------------------------------------------------------------------ @@ -502,7 +504,7 @@ # Note: Each lock table slot uses ~270 bytes of shared memory, and there are # max_locks_per_transaction * (max_connections + max_prepared_transactions) # lock table slots. -#max_predicate_locks_per_transaction = 64 # min 10 +#max_pred_locks_per_transaction = 64 # min 10 # (change requires restart) #------------------------------------------------------------------------------ diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c index d20a3b3739..f2449ea6b1 100644 --- a/src/backend/utils/sort/tuplesort.c +++ b/src/backend/utils/sort/tuplesort.c @@ -582,7 +582,7 @@ tuplesort_begin_common(int workMem, bool randomAccess) Tuplesortstate * tuplesort_begin_heap(TupleDesc tupDesc, int nkeys, AttrNumber *attNums, - Oid *sortOperators, bool *nullsFirstFlags, + Oid *sortOperators, Oid *collations, bool *nullsFirstFlags, int workMem, bool randomAccess) { Tuplesortstate *state = tuplesort_begin_common(workMem, randomAccess); @@ -640,6 +640,10 @@ tuplesort_begin_heap(TupleDesc tupDesc, sortFunction, (Datum) 0); + if (collations) + ScanKeyEntryInitializeCollation(&state->scanKeys[i], + collations[i]); + /* However, we use btree's conventions for encoding directionality */ if (reverse) state->scanKeys[i].sk_flags |= SK_BT_DESC; @@ -791,7 +795,7 @@ tuplesort_begin_index_hash(Relation indexRel, Tuplesortstate * tuplesort_begin_datum(Oid datumType, - Oid sortOperator, bool nullsFirstFlag, + Oid sortOperator, Oid sortCollation, bool nullsFirstFlag, int workMem, bool randomAccess) { Tuplesortstate *state = tuplesort_begin_common(workMem, randomAccess); @@ -832,6 +836,7 @@ tuplesort_begin_datum(Oid datumType, elog(ERROR, "operator %u is not a valid ordering operator", sortOperator); fmgr_info(sortFunction, &state->sortOpFn); + fmgr_info_collation(sortCollation, &state->sortOpFn); /* set ordering flags */ state->sortFnFlags = reverse ? SK_BT_DESC : 0; diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index b903b7b057..bac167ab47 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -167,6 +167,7 @@ static void get_set_pwd(void); static void setup_depend(void); static void setup_sysviews(void); static void setup_description(void); +static void setup_collation(void); static void setup_conversion(void); static void setup_dictionary(void); static void setup_privileges(void); @@ -226,6 +227,12 @@ do { \ output_failed = true, output_errno = errno; \ } while (0) +#define PG_CMD_PRINTF3(fmt, arg1, arg2, arg3) \ +do { \ + if (fprintf(cmdfd, fmt, arg1, arg2, arg3) < 0 || fflush(cmdfd) < 0) \ + output_failed = true, output_errno = errno; \ +} while (0) + #ifndef WIN32 #define QUOTE_PATH "" #define DIR_SEP "/" @@ -1492,6 +1499,183 @@ setup_description(void) check_ok(); } +#ifdef HAVE_LOCALE_T +/* + * "Normalize" a locale name, stripping off encoding tags such as + * ".utf8" (e.g., "en_US.utf8" -> "en_US", but "br_FR.iso885915@euro" + * -> "br_FR@euro"). Return true if a new, different name was + * generated. + */ +static bool +normalize_locale_name(char *new, const char *old) +{ + char *n = new; + const char *o = old; + bool changed = false; + + while (*o) + { + if (*o == '.') + { + /* skip over encoding tag such as ".utf8" or ".UTF-8" */ + o++; + while ((*o >= 'A' && *o <= 'Z') + || (*o >= 'a' && *o <= 'z') + || (*o >= '0' && *o <= '9') + || (*o == '-')) + o++; + changed = true; + } + else + *n++ = *o++; + } + *n = '\0'; + + return changed; +} +#endif /* HAVE_LOCALE_T */ + +/* + * populate pg_collation + */ +static void +setup_collation(void) +{ +#ifdef HAVE_LOCALE_T + int i; + FILE *locale_a_handle; + char localebuf[NAMEDATALEN]; + int skipped = 0; + PG_CMD_DECL; +#endif + + fputs(_("creating collations ... "), stdout); + fflush(stdout); + +#ifdef HAVE_LOCALE_T + snprintf(cmd, sizeof(cmd), + "\"%s\" %s template1 >%s", + backend_exec, backend_options, + DEVNULL); + + locale_a_handle = popen_check("locale -a", "r"); + if (!locale_a_handle) + return; + + PG_CMD_OPEN; + + PG_CMD_PUTS("CREATE TEMP TABLE tmp_pg_collation ( " + " collname name, " + " locale name, " + " encoding int) WITHOUT OIDS;\n"); + + while (fgets(localebuf, sizeof(localebuf), locale_a_handle)) + { + size_t len; + int enc; + bool skip; + char alias[NAMEDATALEN]; + + len = strlen(localebuf); + + if (localebuf[len - 1] != '\n') + { + if (debug) + fprintf(stderr, _("%s: locale name too long, skipped: %s\n"), + progname, localebuf); + skipped++; + continue; + } + localebuf[len - 1] = '\0'; + + /* + * Some systems have locale names that don't consist entirely + * of ASCII letters (such as "bokmål" or + * "français"). This is pretty silly, since we need + * the locale itself to interpret the non-ASCII characters. + * We can't do much with those, so we filter them out. + */ + skip = false; + for (i = 0; i < len; i++) + if (IS_HIGHBIT_SET(localebuf[i])) + { + if (debug) + fprintf(stderr, _("%s: locale name has non-ASCII characters, skipped: %s\n"), + progname, localebuf); + skipped++; + skip = true; + break; + } + if (skip) + continue; + + enc = pg_get_encoding_from_locale(localebuf, debug); + if (enc < 0) + { + skipped++; + continue; /* error message printed by pg_get_encoding_from_locale() */ + } + if (enc == PG_SQL_ASCII) + continue; /* SQL_ASCII is handled separately */ + + PG_CMD_PRINTF2("INSERT INTO tmp_pg_collation (locale, encoding) VALUES ('%s', %d);", + escape_quotes(localebuf), enc); + + /* + * Generate aliases such as "en_US" in addition to + * "en_US.utf8" for ease of use. Note that collation names + * are unique per encoding only, so this doesn't clash with + * "en_US" for LATIN1, say. + */ + if (normalize_locale_name(alias, localebuf)) + PG_CMD_PRINTF3("INSERT INTO tmp_pg_collation (collname, locale, encoding) VALUES ('%s', '%s', %d);", + escape_quotes(alias), escape_quotes(localebuf), enc); + } + + for (i = PG_SQL_ASCII; i <= PG_ENCODING_BE_LAST; i++) + PG_CMD_PRINTF2("INSERT INTO tmp_pg_collation (locale, encoding) VALUES ('C', %d), ('POSIX', %d);", + i, i); + + /* Add an SQL-standard name */ + PG_CMD_PRINTF1("INSERT INTO tmp_pg_collation (collname, locale, encoding) VALUES ('ucs_basic', 'C', %d);", PG_UTF8); + + /* + * When copying collations to the final location, eliminate + * aliases that conflict with an existing locale name for the same + * encoding. For example, "br_FR.iso88591" is normalized to + * "br_FR", both for encoding LATIN1. But the unnormalized locale + * "br_FR" already exists for LATIN1. Prefer the collation that + * matches the OS locale name, else the first name by sort order + * (arbitrary choice to be deterministic). + */ + PG_CMD_PUTS("INSERT INTO pg_collation (collname, collnamespace, collowner, collencoding, collcollate, collctype) " + " SELECT DISTINCT ON (final_collname, collnamespace, encoding)" + " COALESCE(collname, locale) AS final_collname, " + " (SELECT oid FROM pg_namespace WHERE nspname = 'pg_catalog') AS collnamespace, " + " (SELECT relowner FROM pg_class WHERE relname = 'pg_collation') AS collowner, " + " encoding, " + " locale, locale " + " FROM tmp_pg_collation" + " ORDER BY final_collname, collnamespace, encoding, (collname = locale) DESC, locale;\n"); + + pclose(locale_a_handle); + PG_CMD_CLOSE; + + check_ok(); + if (skipped && !debug) + { + printf(ngettext("%d system locale has been omitted because it cannot supported by PostgreSQL.\n", + "%d system locales have been omitted because they cannot be supported by PostgreSQL.\n", + skipped), + skipped); + printf(_("Use the option \"--debug\" to see details.\n")); + } +#else /* not HAVE_LOCALE_T */ + printf(_("not supported on this platform\n")); + fflush(stdout); +#endif /* not HAVE_LOCALE_T */ +} + /* * load conversion functions */ @@ -2021,7 +2205,7 @@ check_locale_encoding(const char *locale, int user_enc) { int locale_enc; - locale_enc = pg_get_encoding_from_locale(locale); + locale_enc = pg_get_encoding_from_locale(locale, true); /* See notes in createdb() to understand these tests */ if (!(locale_enc == user_enc || @@ -2675,7 +2859,7 @@ main(int argc, char *argv[]) { int ctype_enc; - ctype_enc = pg_get_encoding_from_locale(lc_ctype); + ctype_enc = pg_get_encoding_from_locale(lc_ctype, true); if (ctype_enc == -1) { @@ -2952,6 +3136,8 @@ main(int argc, char *argv[]) setup_description(); + setup_collation(); + setup_conversion(); setup_dictionary(); diff --git a/src/bin/pg_basebackup/pg_basebackup.c b/src/bin/pg_basebackup/pg_basebackup.c index 98414a99c6..61aa1d382c 100644 --- a/src/bin/pg_basebackup/pg_basebackup.c +++ b/src/bin/pg_basebackup/pg_basebackup.c @@ -777,11 +777,12 @@ BaseBackup() * Start the actual backup */ PQescapeStringConn(conn, escaped_label, label, sizeof(escaped_label), &i); - snprintf(current_path, sizeof(current_path), "BASE_BACKUP LABEL '%s' %s %s %s", + snprintf(current_path, sizeof(current_path), "BASE_BACKUP LABEL '%s' %s %s %s %s", escaped_label, showprogress ? "PROGRESS" : "", includewal ? "WAL" : "", - fastcheckpoint ? "FAST" : ""); + fastcheckpoint ? "FAST" : "", + includewal ? "NOWAIT" : ""); if (PQsendQuery(conn, current_path) == 0) { diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c index 6c87f158f3..2fab5c98fd 100644 --- a/src/bin/pg_ctl/pg_ctl.c +++ b/src/bin/pg_ctl/pg_ctl.c @@ -62,6 +62,7 @@ typedef enum START_COMMAND, STOP_COMMAND, RESTART_COMMAND, + PROMOTE_COMMAND, RELOAD_COMMAND, STATUS_COMMAND, KILL_COMMAND, @@ -96,6 +97,7 @@ static char postopts_file[MAXPGPATH]; static char pid_file[MAXPGPATH]; static char backup_file[MAXPGPATH]; static char recovery_file[MAXPGPATH]; +static char promote_file[MAXPGPATH]; #if defined(WIN32) || defined(__CYGWIN__) static DWORD pgctl_start_type = SERVICE_AUTO_START; @@ -124,6 +126,7 @@ static void do_init(void); static void do_start(void); static void do_stop(void); static void do_restart(void); +static void do_promote(void); static void do_reload(void); static void do_status(void); static void do_kill(pgpid_t pid); @@ -872,7 +875,7 @@ do_stop(void) /* - * restart/reload routines + * restart/promote/reload routines */ static void @@ -965,6 +968,66 @@ do_restart(void) do_start(); } +static void +do_promote(void) +{ + FILE *prmfile; + pgpid_t pid; + struct stat statbuf; + + pid = get_pgpid(); + + if (pid == 0) /* no pid file */ + { + write_stderr(_("%s: PID file \"%s\" does not exist\n"), progname, pid_file); + write_stderr(_("Is server running?\n")); + exit(1); + } + else if (pid < 0) /* standalone backend, not postmaster */ + { + pid = -pid; + write_stderr(_("%s: cannot promote server; " + "single-user server is running (PID: %ld)\n"), + progname, pid); + exit(1); + } + + /* If recovery.conf doesn't exist, the server is not in standby mode */ + if (stat(recovery_file, &statbuf) != 0) + { + write_stderr(_("%s: cannot promote server; " + "server is not in standby mode\n"), + progname); + exit(1); + } + + if ((prmfile = fopen(promote_file, "w")) == NULL) + { + write_stderr(_("%s: could not create promote signal file \"%s\": %s\n"), + progname, promote_file, strerror(errno)); + exit(1); + } + if (fclose(prmfile)) + { + write_stderr(_("%s: could not write promote signal file \"%s\": %s\n"), + progname, promote_file, strerror(errno)); + exit(1); + } + + sig = SIGUSR1; + if (kill((pid_t) pid, sig) != 0) + { + write_stderr(_("%s: could not send promote signal (PID: %ld): %s\n"), + progname, pid, strerror(errno)); + if (unlink(promote_file) != 0) + write_stderr(_("%s: could not remove promote signal file \"%s\": %s\n"), + progname, promote_file, strerror(errno)); + exit(1); + } + + print_msg(_("server promoting\n")); +} + static void do_reload(void) @@ -1617,7 +1680,7 @@ do_advice(void) static void do_help(void) { - printf(_("%s is a utility to start, stop, restart, reload configuration files,\n" + printf(_("%s is a utility to start, stop, restart, promote, reload configuration files,\n" "report the status of a PostgreSQL server, or signal a PostgreSQL process.\n\n"), progname); printf(_("Usage:\n")); printf(_(" %s init[db] [-D DATADIR] [-s] [-o \"OPTIONS\"]\n"), progname); @@ -1625,6 +1688,7 @@ do_help(void) printf(_(" %s stop [-W] [-t SECS] [-D DATADIR] [-s] [-m SHUTDOWN-MODE]\n"), progname); printf(_(" %s restart [-w] [-t SECS] [-D DATADIR] [-s] [-m SHUTDOWN-MODE]\n" " [-o \"OPTIONS\"]\n"), progname); + printf(_(" %s promote [-D DATADIR] [-s]\n"), progname); printf(_(" %s reload [-D DATADIR] [-s]\n"), progname); printf(_(" %s status [-D DATADIR]\n"), progname); printf(_(" %s kill SIGNALNAME PID\n"), progname); @@ -1950,6 +2014,8 @@ main(int argc, char **argv) ctl_command = STOP_COMMAND; else if (strcmp(argv[optind], "restart") == 0) ctl_command = RESTART_COMMAND; + else if (strcmp(argv[optind], "promote") == 0) + ctl_command = PROMOTE_COMMAND; else if (strcmp(argv[optind], "reload") == 0) ctl_command = RELOAD_COMMAND; else if (strcmp(argv[optind], "status") == 0) @@ -2036,6 +2102,7 @@ main(int argc, char **argv) snprintf(pid_file, MAXPGPATH, "%s/postmaster.pid", pg_data); snprintf(backup_file, MAXPGPATH, "%s/backup_label", pg_data); snprintf(recovery_file, MAXPGPATH, "%s/recovery.conf", pg_data); + snprintf(promote_file, MAXPGPATH, "%s/promote", pg_data); } switch (ctl_command) @@ -2055,6 +2122,9 @@ main(int argc, char **argv) case RESTART_COMMAND: do_restart(); break; + case PROMOTE_COMMAND: + do_promote(); + break; case RELOAD_COMMAND: do_reload(); break; diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c index 55cc3126e1..12b22bc256 100644 --- a/src/bin/pg_dump/common.c +++ b/src/bin/pg_dump/common.c @@ -79,6 +79,7 @@ TableInfo * getSchemaData(int *numTablesPtr) { NamespaceInfo *nsinfo; + ExtensionInfo *extinfo; AggInfo *agginfo; InhInfo *inhinfo; RuleInfo *ruleinfo; @@ -86,6 +87,7 @@ getSchemaData(int *numTablesPtr) CastInfo *castinfo; OpclassInfo *opcinfo; OpfamilyInfo *opfinfo; + CollInfo *collinfo; ConvInfo *convinfo; TSParserInfo *prsinfo; TSTemplateInfo *tmplinfo; @@ -95,6 +97,7 @@ getSchemaData(int *numTablesPtr) ForeignServerInfo *srvinfo; DefaultACLInfo *daclinfo; int numNamespaces; + int numExtensions; int numAggregates; int numInherits; int numRules; @@ -102,6 +105,7 @@ getSchemaData(int *numTablesPtr) int numCasts; int numOpclasses; int numOpfamilies; + int numCollations; int numConversions; int numTSParsers; int numTSTemplates; @@ -116,6 +120,10 @@ getSchemaData(int *numTablesPtr) nsinfo = getNamespaces(&numNamespaces); if (g_verbose) + write_msg(NULL, "reading extensions\n"); + extinfo = getExtensions(&numExtensions); + + if (g_verbose) write_msg(NULL, "reading user-defined functions\n"); funinfo = getFuncs(&numFuncs); funinfoindex = buildIndexArray(funinfo, numFuncs, sizeof(FuncInfo)); @@ -145,6 +153,10 @@ getSchemaData(int *numTablesPtr) opcinfo = getOpclasses(&numOpclasses); if (g_verbose) + write_msg(NULL, "reading user-defined operator families\n"); + opfinfo = getOpfamilies(&numOpfamilies); + + if (g_verbose) write_msg(NULL, "reading user-defined text search parsers\n"); prsinfo = getTSParsers(&numTSParsers); @@ -173,14 +185,18 @@ getSchemaData(int *numTablesPtr) daclinfo = getDefaultACLs(&numDefaultACLs); if (g_verbose) - write_msg(NULL, "reading user-defined operator families\n"); - opfinfo = getOpfamilies(&numOpfamilies); + write_msg(NULL, "reading user-defined collations\n"); + collinfo = getCollations(&numCollations); if (g_verbose) write_msg(NULL, "reading user-defined conversions\n"); convinfo = getConversions(&numConversions); if (g_verbose) + write_msg(NULL, "reading type casts\n"); + castinfo = getCasts(&numCasts); + + if (g_verbose) write_msg(NULL, "reading user-defined tables\n"); tblinfo = getTables(&numTables); tblinfoindex = buildIndexArray(tblinfo, numTables, sizeof(TableInfo)); @@ -193,9 +209,14 @@ getSchemaData(int *numTablesPtr) write_msg(NULL, "reading rewrite rules\n"); ruleinfo = getRules(&numRules); + /* + * Identify extension member objects and mark them as not to be dumped. + * This must happen after reading all objects that can be direct members + * of extensions, but before we begin to process table subsidiary objects. + */ if (g_verbose) - write_msg(NULL, "reading type casts\n"); - castinfo = getCasts(&numCasts); + write_msg(NULL, "finding extension members\n"); + getExtensionMembership(extinfo, numExtensions); /* Link tables to parents, mark parents of target tables interesting */ if (g_verbose) @@ -448,6 +469,7 @@ AssignDumpId(DumpableObject *dobj) dobj->name = NULL; /* must be set later */ dobj->namespace = NULL; /* may be set later */ dobj->dump = true; /* default assumption */ + dobj->ext_member = false; /* default assumption */ dobj->dependencies = NULL; dobj->nDeps = 0; dobj->allocDeps = 0; @@ -518,9 +540,9 @@ findObjectByDumpId(DumpId dumpId) * Returns NULL for unknown ID * * We use binary search in a sorted list that is built on first call. - * If AssignDumpId() and findObjectByCatalogId() calls were intermixed, + * If AssignDumpId() and findObjectByCatalogId() calls were freely intermixed, * the code would work, but possibly be very slow. In the current usage - * pattern that does not happen, indeed we only need to build the list once. + * pattern that does not happen, indeed we build the list at most twice. */ DumpableObject * findObjectByCatalogId(CatalogId catalogId) diff --git a/src/bin/pg_dump/pg_backup_archiver.c b/src/bin/pg_dump/pg_backup_archiver.c index 930ce9d29e..480264e911 100644 --- a/src/bin/pg_dump/pg_backup_archiver.c +++ b/src/bin/pg_dump/pg_backup_archiver.c @@ -2777,7 +2777,8 @@ _getObjectDescription(PQExpBuffer buf, TocEntry *te, ArchiveHandle *AH) type = "TABLE"; /* objects named by a schema and name */ - if (strcmp(type, "CONVERSION") == 0 || + if (strcmp(type, "COLLATION") == 0 || + strcmp(type, "CONVERSION") == 0 || strcmp(type, "DOMAIN") == 0 || strcmp(type, "TABLE") == 0 || strcmp(type, "TYPE") == 0 || @@ -2961,6 +2962,7 @@ _printTocEntry(ArchiveHandle *AH, TocEntry *te, RestoreOptions *ropt, bool isDat { if (strcmp(te->desc, "AGGREGATE") == 0 || strcmp(te->desc, "BLOB") == 0 || + strcmp(te->desc, "COLLATION") == 0 || strcmp(te->desc, "CONVERSION") == 0 || strcmp(te->desc, "DATABASE") == 0 || strcmp(te->desc, "DOMAIN") == 0 || diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 8baf79f1c6..05a73c5a76 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -161,6 +161,7 @@ static int findSecLabels(Archive *fout, Oid classoid, Oid objoid, static int collectSecLabels(Archive *fout, SecLabelItem **items); static void dumpDumpableObject(Archive *fout, DumpableObject *dobj); static void dumpNamespace(Archive *fout, NamespaceInfo *nspinfo); +static void dumpExtension(Archive *fout, ExtensionInfo *extinfo); static void dumpType(Archive *fout, TypeInfo *tyinfo); static void dumpBaseType(Archive *fout, TypeInfo *tyinfo); static void dumpEnumType(Archive *fout, TypeInfo *tyinfo); @@ -174,6 +175,7 @@ static void dumpCast(Archive *fout, CastInfo *cast); static void dumpOpr(Archive *fout, OprInfo *oprinfo); static void dumpOpclass(Archive *fout, OpclassInfo *opcinfo); static void dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo); +static void dumpCollation(Archive *fout, CollInfo *convinfo); static void dumpConversion(Archive *fout, ConvInfo *convinfo); static void dumpRule(Archive *fout, RuleInfo *rinfo); static void dumpAgg(Archive *fout, AggInfo *agginfo); @@ -204,6 +206,7 @@ static void dumpACL(Archive *fout, CatalogId objCatId, DumpId objDumpId, static void getDependencies(void); static void getDomainConstraints(TypeInfo *tyinfo); static void getTableData(TableInfo *tblinfo, int numTables, bool oids); +static void makeTableDataInfo(TableInfo *tbinfo, bool oids); static void getTableDataFKConstraints(void); static char *format_function_arguments(FuncInfo *finfo, char *funcargs); static char *format_function_arguments_old(FuncInfo *finfo, int nallargs, @@ -232,6 +235,9 @@ static bool binary_upgrade_set_type_oids_by_rel_oid( PQExpBuffer upgrade_buffer, Oid pg_rel_oid); static void binary_upgrade_set_pg_class_oids(PQExpBuffer upgrade_buffer, Oid pg_class_oid, bool is_index); +static void binary_upgrade_extension_member(PQExpBuffer upgrade_buffer, + DumpableObject *dobj, + const char *objlabel); static const char *getAttrName(int attrnum, TableInfo *tblInfo); static const char *fmtCopyColumnList(const TableInfo *ti); static void do_sql_command(PGconn *conn, const char *query); @@ -1232,6 +1238,23 @@ dumpTableData_copy(Archive *fout, void *dcontext) classname), column_list); } + else if (tdinfo->filtercond) + { + /* Note: this syntax is only supported in 8.2 and up */ + appendPQExpBufferStr(q, "COPY (SELECT "); + /* klugery to get rid of parens in column list */ + if (strlen(column_list) > 2) + { + appendPQExpBufferStr(q, column_list + 1); + q->data[q->len - 1] = ' '; + } + else + appendPQExpBufferStr(q, "* "); + appendPQExpBuffer(q, "FROM %s %s) TO stdout;", + fmtQualifiedId(tbinfo->dobj.namespace->dobj.name, + classname), + tdinfo->filtercond); + } else { appendPQExpBuffer(q, "COPY %s %s TO stdout;", @@ -1356,6 +1379,8 @@ dumpTableData_insert(Archive *fout, void *dcontext) fmtQualifiedId(tbinfo->dobj.namespace->dobj.name, classname)); } + if (tdinfo->filtercond) + appendPQExpBuffer(q, " %s", tdinfo->filtercond); res = PQexec(g_conn, q->data); check_sql_result(res, g_conn, q->data, PGRES_COMMAND_OK); @@ -1539,30 +1564,38 @@ getTableData(TableInfo *tblinfo, int numTables, bool oids) && no_unlogged_table_data) continue; - if (tblinfo[i].dobj.dump) - { - TableDataInfo *tdinfo; + if (tblinfo[i].dobj.dump && tblinfo[i].dataObj == NULL) + makeTableDataInfo(&(tblinfo[i]), oids); + } +} - tdinfo = (TableDataInfo *) malloc(sizeof(TableDataInfo)); +/* + * Make a dumpable object for the data of this specific table + */ +static void +makeTableDataInfo(TableInfo *tbinfo, bool oids) +{ + TableDataInfo *tdinfo; - tdinfo->dobj.objType = DO_TABLE_DATA; + tdinfo = (TableDataInfo *) malloc(sizeof(TableDataInfo)); - /* - * Note: use tableoid 0 so that this object won't be mistaken for - * something that pg_depend entries apply to. - */ - tdinfo->dobj.catId.tableoid = 0; - tdinfo->dobj.catId.oid = tblinfo[i].dobj.catId.oid; - AssignDumpId(&tdinfo->dobj); - tdinfo->dobj.name = tblinfo[i].dobj.name; - tdinfo->dobj.namespace = tblinfo[i].dobj.namespace; - tdinfo->tdtable = &(tblinfo[i]); - tdinfo->oids = oids; - addObjectDependency(&tdinfo->dobj, tblinfo[i].dobj.dumpId); - - tblinfo[i].dataObj = tdinfo; - } - } + tdinfo->dobj.objType = DO_TABLE_DATA; + + /* + * Note: use tableoid 0 so that this object won't be mistaken for + * something that pg_depend entries apply to. + */ + tdinfo->dobj.catId.tableoid = 0; + tdinfo->dobj.catId.oid = tbinfo->dobj.catId.oid; + AssignDumpId(&tdinfo->dobj); + tdinfo->dobj.name = tbinfo->dobj.name; + tdinfo->dobj.namespace = tbinfo->dobj.namespace; + tdinfo->tdtable = tbinfo; + tdinfo->oids = oids; + tdinfo->filtercond = NULL; /* might get set later */ + addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId); + + tbinfo->dataObj = tdinfo; } /* @@ -2438,6 +2471,47 @@ binary_upgrade_set_pg_class_oids(PQExpBuffer upgrade_buffer, Oid pg_class_oid, } /* + * If the DumpableObject is a member of an extension, add a suitable + * ALTER EXTENSION ADD command to the creation commands in upgrade_buffer. + */ +static void +binary_upgrade_extension_member(PQExpBuffer upgrade_buffer, + DumpableObject *dobj, + const char *objlabel) +{ + DumpableObject *extobj = NULL; + int i; + + if (!dobj->ext_member) + return; + + /* + * Find the parent extension. We could avoid this search if we wanted + * to add a link field to DumpableObject, but the space costs of that + * would be considerable. We assume that member objects could only have + * a direct dependency on their own extension, not any others. + */ + for (i = 0; i < dobj->nDeps; i++) + { + extobj = findObjectByDumpId(dobj->dependencies[i]); + if (extobj && extobj->objType == DO_EXTENSION) + break; + extobj = NULL; + } + if (extobj == NULL) + { + write_msg(NULL, "failed to find parent extension for %s", objlabel); + exit_nicely(); + } + + appendPQExpBuffer(upgrade_buffer, + "\n-- For binary upgrade, handle extension membership the hard way\n"); + appendPQExpBuffer(upgrade_buffer, "ALTER EXTENSION %s ADD %s;\n", + fmtId(extobj->name), + objlabel); +} + +/* * getNamespaces: * read all namespaces in the system catalogs and return them in the * NamespaceInfo* structure @@ -2585,6 +2659,87 @@ findNamespace(Oid nsoid, Oid objoid) } /* + * getExtensions: + * read all extensions in the system catalogs and return them in the + * ExtensionInfo* structure + * + * numExtensions is set to the number of extensions read in + */ +ExtensionInfo * +getExtensions(int *numExtensions) +{ + PGresult *res; + int ntups; + int i; + PQExpBuffer query; + ExtensionInfo *extinfo; + int i_tableoid; + int i_oid; + int i_extname; + int i_nspname; + int i_extrelocatable; + int i_extversion; + int i_extconfig; + int i_extcondition; + + /* + * Before 9.1, there are no extensions. + */ + if (g_fout->remoteVersion < 90100) + { + *numExtensions = 0; + return NULL; + } + + query = createPQExpBuffer(); + + /* Make sure we are in proper schema */ + selectSourceSchema("pg_catalog"); + + appendPQExpBuffer(query, "SELECT x.tableoid, x.oid, " + "x.extname, n.nspname, x.extrelocatable, x.extversion, x.extconfig, x.extcondition " + "FROM pg_extension x " + "JOIN pg_namespace n ON n.oid = x.extnamespace"); + + res = PQexec(g_conn, query->data); + check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK); + + ntups = PQntuples(res); + + extinfo = (ExtensionInfo *) malloc(ntups * sizeof(ExtensionInfo)); + + i_tableoid = PQfnumber(res, "tableoid"); + i_oid = PQfnumber(res, "oid"); + i_extname = PQfnumber(res, "extname"); + i_nspname = PQfnumber(res, "nspname"); + i_extrelocatable = PQfnumber(res, "extrelocatable"); + i_extversion = PQfnumber(res, "extversion"); + i_extconfig = PQfnumber(res, "extconfig"); + i_extcondition = PQfnumber(res, "extcondition"); + + for (i = 0; i < ntups; i++) + { + extinfo[i].dobj.objType = DO_EXTENSION; + extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid)); + extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); + AssignDumpId(&extinfo[i].dobj); + extinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_extname)); + extinfo[i].namespace = strdup(PQgetvalue(res, i, i_nspname)); + extinfo[i].relocatable = *(PQgetvalue(res, i, i_extrelocatable)) == 't'; + extinfo[i].extversion = strdup(PQgetvalue(res, i, i_extversion)); + extinfo[i].extconfig = strdup(PQgetvalue(res, i, i_extconfig)); + extinfo[i].extcondition = strdup(PQgetvalue(res, i, i_extcondition)); + } + + PQclear(res); + destroyPQExpBuffer(query); + + *numExtensions = ntups; + + return extinfo; +} + +/* * getTypes: * read all types in the system catalogs and return them in the * TypeInfo* structure @@ -2941,6 +3096,84 @@ getOperators(int *numOprs) } /* + * getCollations: + * read all collations in the system catalogs and return them in the + * CollInfo* structure + * + * numCollations is set to the number of collations read in + */ +CollInfo * +getCollations(int *numCollations) +{ + PGresult *res; + int ntups; + int i; + PQExpBuffer query = createPQExpBuffer(); + CollInfo *collinfo; + int i_tableoid; + int i_oid; + int i_collname; + int i_collnamespace; + int i_rolname; + + /* Collations didn't exist pre-9.1 */ + if (g_fout->remoteVersion < 90100) + { + *numCollations = 0; + return NULL; + } + + /* + * find all collations, including builtin collations; we filter out + * system-defined collations at dump-out time. + */ + + /* Make sure we are in proper schema */ + selectSourceSchema("pg_catalog"); + + appendPQExpBuffer(query, "SELECT tableoid, oid, collname, " + "collnamespace, " + "(%s collowner) AS rolname " + "FROM pg_collation", + username_subquery); + + res = PQexec(g_conn, query->data); + check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK); + + ntups = PQntuples(res); + *numCollations = ntups; + + collinfo = (CollInfo *) malloc(ntups * sizeof(CollInfo)); + + i_tableoid = PQfnumber(res, "tableoid"); + i_oid = PQfnumber(res, "oid"); + i_collname = PQfnumber(res, "collname"); + i_collnamespace = PQfnumber(res, "collnamespace"); + i_rolname = PQfnumber(res, "rolname"); + + for (i = 0; i < ntups; i++) + { + collinfo[i].dobj.objType = DO_COLLATION; + collinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid)); + collinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid)); + AssignDumpId(&collinfo[i].dobj); + collinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_collname)); + collinfo[i].dobj.namespace = findNamespace(atooid(PQgetvalue(res, i, i_collnamespace)), + collinfo[i].dobj.catId.oid); + collinfo[i].rolname = strdup(PQgetvalue(res, i, i_rolname)); + + /* Decide whether we want to dump it */ + selectDumpableObject(&(collinfo[i].dobj)); + } + + PQclear(res); + + destroyPQExpBuffer(query); + + return collinfo; +} + +/* * getConversions: * read all conversions in the system catalogs and return them in the * ConvInfo* structure @@ -3229,7 +3462,10 @@ getAggregates(int *numAggs) /* Make sure we are in proper schema */ selectSourceSchema("pg_catalog"); - /* find all user-defined aggregates */ + /* + * Find all user-defined aggregates. See comment in getFuncs() for the + * rationale behind the filtering logic. + */ if (g_fout->remoteVersion >= 80200) { @@ -3238,11 +3474,20 @@ getAggregates(int *numAggs) "pronargs, proargtypes, " "(%s proowner) AS rolname, " "proacl AS aggacl " - "FROM pg_proc " - "WHERE proisagg " - "AND pronamespace != " - "(SELECT oid FROM pg_namespace WHERE nspname = 'pg_catalog')", + "FROM pg_proc p " + "WHERE proisagg AND (" + "pronamespace != " + "(SELECT oid FROM pg_namespace " + "WHERE nspname = 'pg_catalog')", username_subquery); + if (binary_upgrade && g_fout->remoteVersion >= 90100) + appendPQExpBuffer(query, + " OR EXISTS(SELECT 1 FROM pg_depend WHERE " + "classid = 'pg_proc'::regclass AND " + "objid = p.oid AND " + "refclassid = 'pg_extension'::regclass AND " + "deptype = 'e')"); + appendPQExpBuffer(query, ")"); } else if (g_fout->remoteVersion >= 70300) { @@ -3375,7 +3620,14 @@ getFuncs(int *numFuncs) /* Make sure we are in proper schema */ selectSourceSchema("pg_catalog"); - /* find all user-defined funcs */ + /* + * Find all user-defined functions. Normally we can exclude functions + * in pg_catalog, which is worth doing since there are several thousand + * of 'em. However, there are some extensions that create functions in + * pg_catalog. In normal dumps we can still ignore those --- but in + * binary-upgrade mode, we must dump the member objects of the extension, + * so be sure to fetch any such functions. + */ if (g_fout->remoteVersion >= 70300) { @@ -3384,12 +3636,20 @@ getFuncs(int *numFuncs) "pronargs, proargtypes, prorettype, proacl, " "pronamespace, " "(%s proowner) AS rolname " - "FROM pg_proc " - "WHERE NOT proisagg " - "AND pronamespace != " + "FROM pg_proc p " + "WHERE NOT proisagg AND (" + "pronamespace != " "(SELECT oid FROM pg_namespace " "WHERE nspname = 'pg_catalog')", username_subquery); + if (binary_upgrade && g_fout->remoteVersion >= 90100) + appendPQExpBuffer(query, + " OR EXISTS(SELECT 1 FROM pg_depend WHERE " + "classid = 'pg_proc'::regclass AND " + "objid = p.oid AND " + "refclassid = 'pg_extension'::regclass AND " + "deptype = 'e')"); + appendPQExpBuffer(query, ")"); } else if (g_fout->remoteVersion >= 70100) { @@ -5257,7 +5517,23 @@ getTableAttrs(TableInfo *tblinfo, int numTables) resetPQExpBuffer(q); - if (g_fout->remoteVersion >= 90000) + if (g_fout->remoteVersion >= 90100) + { + /* attcollation is new in 9.1 */ + appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, " + "a.attstattarget, a.attstorage, t.typstorage, " + "a.attnotnull, a.atthasdef, a.attisdropped, " + "a.attlen, a.attalign, a.attislocal, " + "pg_catalog.format_type(t.oid,a.atttypmod,a.attcollation) AS atttypname, " + "array_to_string(attoptions, ', ') AS attoptions " + "FROM pg_catalog.pg_attribute a LEFT JOIN pg_catalog.pg_type t " + "ON a.atttypid = t.oid " + "WHERE a.attrelid = '%u'::pg_catalog.oid " + "AND a.attnum > 0::pg_catalog.int2 " + "ORDER BY a.attrelid, a.attnum", + tbinfo->dobj.catId.oid); + } + else if (g_fout->remoteVersion >= 90000) { /* attoptions is new in 9.0 */ appendPQExpBuffer(q, "SELECT a.attnum, a.attname, a.atttypmod, " @@ -6590,6 +6866,9 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj) case DO_NAMESPACE: dumpNamespace(fout, (NamespaceInfo *) dobj); break; + case DO_EXTENSION: + dumpExtension(fout, (ExtensionInfo *) dobj); + break; case DO_TYPE: dumpType(fout, (TypeInfo *) dobj); break; @@ -6611,6 +6890,9 @@ dumpDumpableObject(Archive *fout, DumpableObject *dobj) case DO_OPFAMILY: dumpOpfamily(fout, (OpfamilyInfo *) dobj); break; + case DO_COLLATION: + dumpCollation(fout, (CollInfo *) dobj); + break; case DO_CONVERSION: dumpConversion(fout, (ConvInfo *) dobj); break; @@ -6691,6 +6973,7 @@ dumpNamespace(Archive *fout, NamespaceInfo *nspinfo) { PQExpBuffer q; PQExpBuffer delq; + PQExpBuffer labelq; char *qnspname; /* Skip if not to be dumped */ @@ -6703,6 +6986,7 @@ dumpNamespace(Archive *fout, NamespaceInfo *nspinfo) q = createPQExpBuffer(); delq = createPQExpBuffer(); + labelq = createPQExpBuffer(); qnspname = strdup(fmtId(nspinfo->dobj.name)); @@ -6710,6 +6994,11 @@ dumpNamespace(Archive *fout, NamespaceInfo *nspinfo) appendPQExpBuffer(q, "CREATE SCHEMA %s;\n", qnspname); + appendPQExpBuffer(labelq, "SCHEMA %s", qnspname); + + if (binary_upgrade) + binary_upgrade_extension_member(q, &nspinfo->dobj, labelq->data); + ArchiveEntry(fout, nspinfo->dobj.catId, nspinfo->dobj.dumpId, nspinfo->dobj.name, NULL, NULL, @@ -6720,12 +7009,10 @@ dumpNamespace(Archive *fout, NamespaceInfo *nspinfo) NULL, NULL); /* Dump Schema Comments and Security Labels */ - resetPQExpBuffer(q); - appendPQExpBuffer(q, "SCHEMA %s", qnspname); - dumpComment(fout, q->data, + dumpComment(fout, labelq->data, NULL, nspinfo->rolname, nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId); - dumpSecLabel(fout, q->data, + dumpSecLabel(fout, labelq->data, NULL, nspinfo->rolname, nspinfo->dobj.catId, 0, nspinfo->dobj.dumpId); @@ -6737,6 +7024,110 @@ dumpNamespace(Archive *fout, NamespaceInfo *nspinfo) destroyPQExpBuffer(q); destroyPQExpBuffer(delq); + destroyPQExpBuffer(labelq); +} + +/* + * dumpExtension + * writes out to fout the queries to recreate an extension + */ +static void +dumpExtension(Archive *fout, ExtensionInfo *extinfo) +{ + PQExpBuffer q; + PQExpBuffer delq; + PQExpBuffer labelq; + char *qextname; + + /* Skip if not to be dumped */ + if (!extinfo->dobj.dump || dataOnly) + return; + + q = createPQExpBuffer(); + delq = createPQExpBuffer(); + labelq = createPQExpBuffer(); + + qextname = strdup(fmtId(extinfo->dobj.name)); + + appendPQExpBuffer(delq, "DROP EXTENSION %s;\n", qextname); + + if (!binary_upgrade) + { + appendPQExpBuffer(q, "CREATE EXTENSION %s WITH SCHEMA %s;\n", + qextname, fmtId(extinfo->namespace)); + } + else + { + int i; + int n; + + appendPQExpBuffer(q, "-- For binary upgrade, create an empty extension and insert objects into it\n"); + appendPQExpBuffer(q, + "SELECT binary_upgrade.create_empty_extension("); + appendStringLiteralAH(q, extinfo->dobj.name, fout); + appendPQExpBuffer(q, ", "); + appendStringLiteralAH(q, extinfo->namespace, fout); + appendPQExpBuffer(q, ", "); + appendPQExpBuffer(q, "%s, ", extinfo->relocatable ? "true" : "false"); + appendStringLiteralAH(q, extinfo->extversion, fout); + appendPQExpBuffer(q, ", "); + /* + * Note that we're pushing extconfig (an OID array) back into + * pg_extension exactly as-is. This is OK because pg_class OIDs + * are preserved in binary upgrade. + */ + if (strlen(extinfo->extconfig) > 2) + appendStringLiteralAH(q, extinfo->extconfig, fout); + else + appendPQExpBuffer(q, "NULL"); + appendPQExpBuffer(q, ", "); + if (strlen(extinfo->extcondition) > 2) + appendStringLiteralAH(q, extinfo->extcondition, fout); + else + appendPQExpBuffer(q, "NULL"); + appendPQExpBuffer(q, ", "); + appendPQExpBuffer(q, "ARRAY["); + n = 0; + for (i = 0; i < extinfo->dobj.nDeps; i++) + { + DumpableObject *extobj; + + extobj = findObjectByDumpId(extinfo->dobj.dependencies[i]); + if (extobj && extobj->objType == DO_EXTENSION) + { + if (n++ > 0) + appendPQExpBuffer(q, ","); + appendStringLiteralAH(q, extobj->name, fout); + } + } + appendPQExpBuffer(q, "]::pg_catalog.text[]"); + appendPQExpBuffer(q, ");\n"); + } + + appendPQExpBuffer(labelq, "EXTENSION %s", qextname); + + ArchiveEntry(fout, extinfo->dobj.catId, extinfo->dobj.dumpId, + extinfo->dobj.name, + NULL, NULL, + "", + false, "EXTENSION", SECTION_PRE_DATA, + q->data, delq->data, NULL, + extinfo->dobj.dependencies, extinfo->dobj.nDeps, + NULL, NULL); + + /* Dump Extension Comments and Security Labels */ + dumpComment(fout, labelq->data, + NULL, "", + extinfo->dobj.catId, 0, extinfo->dobj.dumpId); + dumpSecLabel(fout, labelq->data, + NULL, "", + extinfo->dobj.catId, 0, extinfo->dobj.dumpId); + + free(qextname); + + destroyPQExpBuffer(q); + destroyPQExpBuffer(delq); + destroyPQExpBuffer(labelq); } /* @@ -6770,6 +7161,7 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo) { PQExpBuffer q = createPQExpBuffer(); PQExpBuffer delq = createPQExpBuffer(); + PQExpBuffer labelq = createPQExpBuffer(); PQExpBuffer query = createPQExpBuffer(); PGresult *res; int num, @@ -6844,13 +7236,18 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo) enum_oid); appendPQExpBuffer(q, "ALTER TYPE %s.", fmtId(tyinfo->dobj.namespace->dobj.name)); - appendPQExpBuffer(q, "%s ADD ", + appendPQExpBuffer(q, "%s ADD VALUE ", fmtId(tyinfo->dobj.name)); appendStringLiteralAH(q, label, fout); appendPQExpBuffer(q, ";\n\n"); } } + appendPQExpBuffer(labelq, "TYPE %s", fmtId(tyinfo->dobj.name)); + + if (binary_upgrade) + binary_upgrade_extension_member(q, &tyinfo->dobj, labelq->data); + ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, tyinfo->dobj.name, tyinfo->dobj.namespace->dobj.name, @@ -6862,19 +7259,17 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo) NULL, NULL); /* Dump Type Comments and Security Labels */ - resetPQExpBuffer(q); - - appendPQExpBuffer(q, "TYPE %s", fmtId(tyinfo->dobj.name)); - dumpComment(fout, q->data, + dumpComment(fout, labelq->data, tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); - dumpSecLabel(fout, q->data, + dumpSecLabel(fout, labelq->data, tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); PQclear(res); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); + destroyPQExpBuffer(labelq); destroyPQExpBuffer(query); } @@ -6887,6 +7282,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo) { PQExpBuffer q = createPQExpBuffer(); PQExpBuffer delq = createPQExpBuffer(); + PQExpBuffer labelq = createPQExpBuffer(); PQExpBuffer query = createPQExpBuffer(); PGresult *res; int ntups; @@ -7230,6 +7626,11 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo) appendPQExpBuffer(q, "\n);\n"); + appendPQExpBuffer(labelq, "TYPE %s", fmtId(tyinfo->dobj.name)); + + if (binary_upgrade) + binary_upgrade_extension_member(q, &tyinfo->dobj, labelq->data); + ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, tyinfo->dobj.name, tyinfo->dobj.namespace->dobj.name, @@ -7241,19 +7642,17 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo) NULL, NULL); /* Dump Type Comments and Security Labels */ - resetPQExpBuffer(q); - - appendPQExpBuffer(q, "TYPE %s", fmtId(tyinfo->dobj.name)); - dumpComment(fout, q->data, + dumpComment(fout, labelq->data, tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); - dumpSecLabel(fout, q->data, + dumpSecLabel(fout, labelq->data, tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); PQclear(res); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); + destroyPQExpBuffer(labelq); destroyPQExpBuffer(query); } @@ -7266,6 +7665,7 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) { PQExpBuffer q = createPQExpBuffer(); PQExpBuffer delq = createPQExpBuffer(); + PQExpBuffer labelq = createPQExpBuffer(); PQExpBuffer query = createPQExpBuffer(); PGresult *res; int ntups; @@ -7279,13 +7679,28 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) selectSourceSchema(tyinfo->dobj.namespace->dobj.name); /* Fetch domain specific details */ - /* We assume here that remoteVersion must be at least 70300 */ - appendPQExpBuffer(query, "SELECT typnotnull, " - "pg_catalog.format_type(typbasetype, typtypmod) AS typdefn, " - "pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, typdefault " - "FROM pg_catalog.pg_type " - "WHERE oid = '%u'::pg_catalog.oid", - tyinfo->dobj.catId.oid); + if (g_fout->remoteVersion >= 90100) + { + /* typcollation is new in 9.1 */ + appendPQExpBuffer(query, "SELECT typnotnull, " + "pg_catalog.format_type(typbasetype, typtypmod, typcollation) AS typdefn, " + "pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, " + "typdefault " + "FROM pg_catalog.pg_type t " + "WHERE t.oid = '%u'::pg_catalog.oid", + tyinfo->dobj.catId.oid); + } + else + { + /* We assume here that remoteVersion must be at least 70300 */ + appendPQExpBuffer(query, "SELECT typnotnull, " + "pg_catalog.format_type(typbasetype, typtypmod) AS typdefn, " + "pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) AS typdefaultbin, " + "typdefault " + "FROM pg_catalog.pg_type " + "WHERE oid = '%u'::pg_catalog.oid", + tyinfo->dobj.catId.oid); + } res = PQexec(g_conn, query->data); check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK); @@ -7357,6 +7772,11 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) appendPQExpBuffer(delq, "%s;\n", fmtId(tyinfo->dobj.name)); + appendPQExpBuffer(labelq, "DOMAIN %s", fmtId(tyinfo->dobj.name)); + + if (binary_upgrade) + binary_upgrade_extension_member(q, &tyinfo->dobj, labelq->data); + ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, tyinfo->dobj.name, tyinfo->dobj.namespace->dobj.name, @@ -7368,18 +7788,16 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo) NULL, NULL); /* Dump Domain Comments and Security Labels */ - resetPQExpBuffer(q); - - appendPQExpBuffer(q, "DOMAIN %s", fmtId(tyinfo->dobj.name)); - dumpComment(fout, q->data, + dumpComment(fout, labelq->data, tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); - dumpSecLabel(fout, q->data, + dumpSecLabel(fout, labelq->data, tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); + destroyPQExpBuffer(labelq); destroyPQExpBuffer(query); } @@ -7393,6 +7811,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo) { PQExpBuffer q = createPQExpBuffer(); PQExpBuffer delq = createPQExpBuffer(); + PQExpBuffer labelq = createPQExpBuffer(); PQExpBuffer query = createPQExpBuffer(); PGresult *res; int ntups; @@ -7459,6 +7878,11 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo) appendPQExpBuffer(delq, "%s;\n", fmtId(tyinfo->dobj.name)); + appendPQExpBuffer(labelq, "TYPE %s", fmtId(tyinfo->dobj.name)); + + if (binary_upgrade) + binary_upgrade_extension_member(q, &tyinfo->dobj, labelq->data); + ArchiveEntry(fout, tyinfo->dobj.catId, tyinfo->dobj.dumpId, tyinfo->dobj.name, tyinfo->dobj.namespace->dobj.name, @@ -7471,19 +7895,17 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo) /* Dump Type Comments and Security Labels */ - resetPQExpBuffer(q); - - appendPQExpBuffer(q, "TYPE %s", fmtId(tyinfo->dobj.name)); - dumpComment(fout, q->data, + dumpComment(fout, labelq->data, tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); - dumpSecLabel(fout, q->data, + dumpSecLabel(fout, labelq->data, tyinfo->dobj.namespace->dobj.name, tyinfo->rolname, tyinfo->dobj.catId, 0, tyinfo->dobj.dumpId); PQclear(res); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); + destroyPQExpBuffer(labelq); destroyPQExpBuffer(query); /* Dump any per-column comments */ @@ -7679,6 +8101,7 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang) { PQExpBuffer defqry; PQExpBuffer delqry; + PQExpBuffer labelq; bool useParams; char *qlanname; char *lanschema; @@ -7686,7 +8109,8 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang) FuncInfo *inlineInfo = NULL; FuncInfo *validatorInfo = NULL; - if (dataOnly) + /* Skip if not to be dumped */ + if (!plang->dobj.dump || dataOnly) return; /* @@ -7729,6 +8153,7 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang) defqry = createPQExpBuffer(); delqry = createPQExpBuffer(); + labelq = createPQExpBuffer(); qlanname = strdup(fmtId(plang->dobj.name)); @@ -7789,6 +8214,11 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang) } appendPQExpBuffer(defqry, ";\n"); + appendPQExpBuffer(labelq, "LANGUAGE %s", qlanname); + + if (binary_upgrade) + binary_upgrade_extension_member(defqry, &plang->dobj, labelq->data); + ArchiveEntry(fout, plang->dobj.catId, plang->dobj.dumpId, plang->dobj.name, lanschema, NULL, plang->lanowner, @@ -7798,12 +8228,10 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang) NULL, NULL); /* Dump Proc Lang Comments and Security Labels */ - resetPQExpBuffer(defqry); - appendPQExpBuffer(defqry, "LANGUAGE %s", qlanname); - dumpComment(fout, defqry->data, + dumpComment(fout, labelq->data, NULL, "", plang->dobj.catId, 0, plang->dobj.dumpId); - dumpSecLabel(fout, defqry->data, + dumpSecLabel(fout, labelq->data, NULL, "", plang->dobj.catId, 0, plang->dobj.dumpId); @@ -7817,6 +8245,7 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang) destroyPQExpBuffer(defqry); destroyPQExpBuffer(delqry); + destroyPQExpBuffer(labelq); } /* @@ -7952,6 +8381,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) PQExpBuffer query; PQExpBuffer q; PQExpBuffer delqry; + PQExpBuffer labelq; PQExpBuffer asPart; PGresult *res; char *funcsig; /* identity signature */ @@ -7991,6 +8421,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) query = createPQExpBuffer(); q = createPQExpBuffer(); delqry = createPQExpBuffer(); + labelq = createPQExpBuffer(); asPart = createPQExpBuffer(); /* Set proper schema search path so type references list correctly */ @@ -8351,6 +8782,11 @@ dumpFunc(Archive *fout, FuncInfo *finfo) appendPQExpBuffer(q, "\n %s;\n", asPart->data); + appendPQExpBuffer(labelq, "FUNCTION %s", funcsig); + + if (binary_upgrade) + binary_upgrade_extension_member(q, &finfo->dobj, labelq->data); + ArchiveEntry(fout, finfo->dobj.catId, finfo->dobj.dumpId, funcsig_tag, finfo->dobj.namespace->dobj.name, @@ -8362,12 +8798,10 @@ dumpFunc(Archive *fout, FuncInfo *finfo) NULL, NULL); /* Dump Function Comments and Security Labels */ - resetPQExpBuffer(q); - appendPQExpBuffer(q, "FUNCTION %s", funcsig); - dumpComment(fout, q->data, + dumpComment(fout, labelq->data, finfo->dobj.namespace->dobj.name, finfo->rolname, finfo->dobj.catId, 0, finfo->dobj.dumpId); - dumpSecLabel(fout, q->data, + dumpSecLabel(fout, labelq->data, finfo->dobj.namespace->dobj.name, finfo->rolname, finfo->dobj.catId, 0, finfo->dobj.dumpId); @@ -8381,6 +8815,7 @@ dumpFunc(Archive *fout, FuncInfo *finfo) destroyPQExpBuffer(query); destroyPQExpBuffer(q); destroyPQExpBuffer(delqry); + destroyPQExpBuffer(labelq); destroyPQExpBuffer(asPart); free(funcsig); free(funcsig_tag); @@ -8403,12 +8838,13 @@ dumpCast(Archive *fout, CastInfo *cast) { PQExpBuffer defqry; PQExpBuffer delqry; - PQExpBuffer castsig; + PQExpBuffer labelq; FuncInfo *funcInfo = NULL; TypeInfo *sourceInfo; TypeInfo *targetInfo; - if (dataOnly) + /* Skip if not to be dumped */ + if (!cast->dobj.dump || dataOnly) return; if (OidIsValid(cast->castfunc)) @@ -8466,7 +8902,7 @@ dumpCast(Archive *fout, CastInfo *cast) defqry = createPQExpBuffer(); delqry = createPQExpBuffer(); - castsig = createPQExpBuffer(); + labelq = createPQExpBuffer(); appendPQExpBuffer(delqry, "DROP CAST (%s AS %s);\n", getFormattedTypeName(cast->castsource, zeroAsNone), @@ -8505,12 +8941,15 @@ dumpCast(Archive *fout, CastInfo *cast) appendPQExpBuffer(defqry, " AS IMPLICIT"); appendPQExpBuffer(defqry, ";\n"); - appendPQExpBuffer(castsig, "CAST (%s AS %s)", + appendPQExpBuffer(labelq, "CAST (%s AS %s)", getFormattedTypeName(cast->castsource, zeroAsNone), getFormattedTypeName(cast->casttarget, zeroAsNone)); + if (binary_upgrade) + binary_upgrade_extension_member(defqry, &cast->dobj, labelq->data); + ArchiveEntry(fout, cast->dobj.catId, cast->dobj.dumpId, - castsig->data, + labelq->data, "pg_catalog", NULL, "", false, "CAST", SECTION_PRE_DATA, defqry->data, delqry->data, NULL, @@ -8518,17 +8957,13 @@ dumpCast(Archive *fout, CastInfo *cast) NULL, NULL); /* Dump Cast Comments */ - resetPQExpBuffer(defqry); - appendPQExpBuffer(defqry, "CAST (%s AS %s)", - getFormattedTypeName(cast->castsource, zeroAsNone), - getFormattedTypeName(cast->casttarget, zeroAsNone)); - dumpComment(fout, defqry->data, + dumpComment(fout, labelq->data, NULL, "", cast->dobj.catId, 0, cast->dobj.dumpId); destroyPQExpBuffer(defqry); destroyPQExpBuffer(delqry); - destroyPQExpBuffer(castsig); + destroyPQExpBuffer(labelq); } /* @@ -8541,6 +8976,7 @@ dumpOpr(Archive *fout, OprInfo *oprinfo) PQExpBuffer query; PQExpBuffer q; PQExpBuffer delq; + PQExpBuffer labelq; PQExpBuffer oprid; PQExpBuffer details; const char *name; @@ -8581,6 +9017,7 @@ dumpOpr(Archive *fout, OprInfo *oprinfo) query = createPQExpBuffer(); q = createPQExpBuffer(); delq = createPQExpBuffer(); + labelq = createPQExpBuffer(); oprid = createPQExpBuffer(); details = createPQExpBuffer(); @@ -8751,6 +9188,11 @@ dumpOpr(Archive *fout, OprInfo *oprinfo) appendPQExpBuffer(q, "CREATE OPERATOR %s (\n%s\n);\n", oprinfo->dobj.name, details->data); + appendPQExpBuffer(labelq, "OPERATOR %s", oprid->data); + + if (binary_upgrade) + binary_upgrade_extension_member(q, &oprinfo->dobj, labelq->data); + ArchiveEntry(fout, oprinfo->dobj.catId, oprinfo->dobj.dumpId, oprinfo->dobj.name, oprinfo->dobj.namespace->dobj.name, @@ -8762,9 +9204,7 @@ dumpOpr(Archive *fout, OprInfo *oprinfo) NULL, NULL); /* Dump Operator Comments */ - resetPQExpBuffer(q); - appendPQExpBuffer(q, "OPERATOR %s", oprid->data); - dumpComment(fout, q->data, + dumpComment(fout, labelq->data, oprinfo->dobj.namespace->dobj.name, oprinfo->rolname, oprinfo->dobj.catId, 0, oprinfo->dobj.dumpId); @@ -8773,6 +9213,7 @@ dumpOpr(Archive *fout, OprInfo *oprinfo) destroyPQExpBuffer(query); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); + destroyPQExpBuffer(labelq); destroyPQExpBuffer(oprid); destroyPQExpBuffer(details); } @@ -8929,6 +9370,7 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo) PQExpBuffer query; PQExpBuffer q; PQExpBuffer delq; + PQExpBuffer labelq; PGresult *res; int ntups; int i_opcintype; @@ -8977,6 +9419,7 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo) query = createPQExpBuffer(); q = createPQExpBuffer(); delq = createPQExpBuffer(); + labelq = createPQExpBuffer(); /* Make sure we are in proper schema so regoperator works correctly */ selectSourceSchema(opcinfo->dobj.namespace->dobj.name); @@ -9253,6 +9696,14 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo) appendPQExpBuffer(q, ";\n"); + appendPQExpBuffer(labelq, "OPERATOR CLASS %s", + fmtId(opcinfo->dobj.name)); + appendPQExpBuffer(labelq, " USING %s", + fmtId(amname)); + + if (binary_upgrade) + binary_upgrade_extension_member(q, &opcinfo->dobj, labelq->data); + ArchiveEntry(fout, opcinfo->dobj.catId, opcinfo->dobj.dumpId, opcinfo->dobj.name, opcinfo->dobj.namespace->dobj.name, @@ -9264,12 +9715,7 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo) NULL, NULL); /* Dump Operator Class Comments */ - resetPQExpBuffer(q); - appendPQExpBuffer(q, "OPERATOR CLASS %s", - fmtId(opcinfo->dobj.name)); - appendPQExpBuffer(q, " USING %s", - fmtId(amname)); - dumpComment(fout, q->data, + dumpComment(fout, labelq->data, NULL, opcinfo->rolname, opcinfo->dobj.catId, 0, opcinfo->dobj.dumpId); @@ -9277,6 +9723,7 @@ dumpOpclass(Archive *fout, OpclassInfo *opcinfo) destroyPQExpBuffer(query); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); + destroyPQExpBuffer(labelq); } /* @@ -9292,6 +9739,7 @@ dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo) PQExpBuffer query; PQExpBuffer q; PQExpBuffer delq; + PQExpBuffer labelq; PGresult *res; PGresult *res_ops; PGresult *res_procs; @@ -9335,6 +9783,7 @@ dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo) query = createPQExpBuffer(); q = createPQExpBuffer(); delq = createPQExpBuffer(); + labelq = createPQExpBuffer(); /* Make sure we are in proper schema so regoperator works correctly */ selectSourceSchema(opfinfo->dobj.namespace->dobj.name); @@ -9443,6 +9892,7 @@ dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo) destroyPQExpBuffer(query); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); + destroyPQExpBuffer(labelq); return; } @@ -9574,6 +10024,14 @@ dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo) appendPQExpBuffer(q, ";\n"); } + appendPQExpBuffer(labelq, "OPERATOR FAMILY %s", + fmtId(opfinfo->dobj.name)); + appendPQExpBuffer(labelq, " USING %s", + fmtId(amname)); + + if (binary_upgrade) + binary_upgrade_extension_member(q, &opfinfo->dobj, labelq->data); + ArchiveEntry(fout, opfinfo->dobj.catId, opfinfo->dobj.dumpId, opfinfo->dobj.name, opfinfo->dobj.namespace->dobj.name, @@ -9585,12 +10043,7 @@ dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo) NULL, NULL); /* Dump Operator Family Comments */ - resetPQExpBuffer(q); - appendPQExpBuffer(q, "OPERATOR FAMILY %s", - fmtId(opfinfo->dobj.name)); - appendPQExpBuffer(q, " USING %s", - fmtId(amname)); - dumpComment(fout, q->data, + dumpComment(fout, labelq->data, NULL, opfinfo->rolname, opfinfo->dobj.catId, 0, opfinfo->dobj.dumpId); @@ -9600,6 +10053,112 @@ dumpOpfamily(Archive *fout, OpfamilyInfo *opfinfo) destroyPQExpBuffer(query); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); + destroyPQExpBuffer(labelq); +} + +/* + * dumpCollation + * write out a single collation definition + */ +static void +dumpCollation(Archive *fout, CollInfo *collinfo) +{ + PQExpBuffer query; + PQExpBuffer q; + PQExpBuffer delq; + PQExpBuffer labelq; + PGresult *res; + int ntups; + int i_collname; + int i_collcollate; + int i_collctype; + const char *collname; + const char *collcollate; + const char *collctype; + + /* Skip if not to be dumped */ + if (!collinfo->dobj.dump || dataOnly) + return; + + query = createPQExpBuffer(); + q = createPQExpBuffer(); + delq = createPQExpBuffer(); + labelq = createPQExpBuffer(); + + /* Make sure we are in proper schema */ + selectSourceSchema(collinfo->dobj.namespace->dobj.name); + + /* Get conversion-specific details */ + appendPQExpBuffer(query, "SELECT collname, " + "collcollate, " + "collctype " + "FROM pg_catalog.pg_collation c " + "WHERE c.oid = '%u'::pg_catalog.oid", + collinfo->dobj.catId.oid); + + res = PQexec(g_conn, query->data); + check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK); + + /* Expecting a single result only */ + ntups = PQntuples(res); + if (ntups != 1) + { + write_msg(NULL, ngettext("query returned %d row instead of one: %s\n", + "query returned %d rows instead of one: %s\n", + ntups), + ntups, query->data); + exit_nicely(); + } + + i_collname = PQfnumber(res, "collname"); + i_collcollate = PQfnumber(res, "collcollate"); + i_collctype = PQfnumber(res, "collctype"); + + collname = PQgetvalue(res, 0, i_collname); + collcollate = PQgetvalue(res, 0, i_collcollate); + collctype = PQgetvalue(res, 0, i_collctype); + + /* + * DROP must be fully qualified in case same name appears in pg_catalog + */ + appendPQExpBuffer(delq, "DROP COLLATION %s", + fmtId(collinfo->dobj.namespace->dobj.name)); + appendPQExpBuffer(delq, ".%s;\n", + fmtId(collinfo->dobj.name)); + + appendPQExpBuffer(q, "CREATE COLLATION %s (lc_collate = ", + fmtId(collinfo->dobj.name)); + appendStringLiteralAH(q, collcollate, fout); + appendPQExpBuffer(q, ", lc_ctype = "); + appendStringLiteralAH(q, collctype, fout); + appendPQExpBuffer(q, ");\n"); + + appendPQExpBuffer(labelq, "COLLATION %s", fmtId(collinfo->dobj.name)); + + if (binary_upgrade) + binary_upgrade_extension_member(q, &collinfo->dobj, labelq->data); + + ArchiveEntry(fout, collinfo->dobj.catId, collinfo->dobj.dumpId, + collinfo->dobj.name, + collinfo->dobj.namespace->dobj.name, + NULL, + collinfo->rolname, + false, "COLLATION", SECTION_PRE_DATA, + q->data, delq->data, NULL, + collinfo->dobj.dependencies, collinfo->dobj.nDeps, + NULL, NULL); + + /* Dump Collation Comments */ + dumpComment(fout, labelq->data, + collinfo->dobj.namespace->dobj.name, collinfo->rolname, + collinfo->dobj.catId, 0, collinfo->dobj.dumpId); + + PQclear(res); + + destroyPQExpBuffer(query); + destroyPQExpBuffer(q); + destroyPQExpBuffer(delq); + destroyPQExpBuffer(labelq); } /* @@ -9612,7 +10171,7 @@ dumpConversion(Archive *fout, ConvInfo *convinfo) PQExpBuffer query; PQExpBuffer q; PQExpBuffer delq; - PQExpBuffer details; + PQExpBuffer labelq; PGresult *res; int ntups; int i_conname; @@ -9633,7 +10192,7 @@ dumpConversion(Archive *fout, ConvInfo *convinfo) query = createPQExpBuffer(); q = createPQExpBuffer(); delq = createPQExpBuffer(); - details = createPQExpBuffer(); + labelq = createPQExpBuffer(); /* Make sure we are in proper schema */ selectSourceSchema(convinfo->dobj.namespace->dobj.name); @@ -9690,6 +10249,11 @@ dumpConversion(Archive *fout, ConvInfo *convinfo) /* regproc is automatically quoted in 7.3 and above */ appendPQExpBuffer(q, " FROM %s;\n", conproc); + appendPQExpBuffer(labelq, "CONVERSION %s", fmtId(convinfo->dobj.name)); + + if (binary_upgrade) + binary_upgrade_extension_member(q, &convinfo->dobj, labelq->data); + ArchiveEntry(fout, convinfo->dobj.catId, convinfo->dobj.dumpId, convinfo->dobj.name, convinfo->dobj.namespace->dobj.name, @@ -9701,9 +10265,7 @@ dumpConversion(Archive *fout, ConvInfo *convinfo) NULL, NULL); /* Dump Conversion Comments */ - resetPQExpBuffer(q); - appendPQExpBuffer(q, "CONVERSION %s", fmtId(convinfo->dobj.name)); - dumpComment(fout, q->data, + dumpComment(fout, labelq->data, convinfo->dobj.namespace->dobj.name, convinfo->rolname, convinfo->dobj.catId, 0, convinfo->dobj.dumpId); @@ -9712,7 +10274,7 @@ dumpConversion(Archive *fout, ConvInfo *convinfo) destroyPQExpBuffer(query); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); - destroyPQExpBuffer(details); + destroyPQExpBuffer(labelq); } /* @@ -9765,6 +10327,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) PQExpBuffer query; PQExpBuffer q; PQExpBuffer delq; + PQExpBuffer labelq; PQExpBuffer details; char *aggsig; char *aggsig_tag; @@ -9790,6 +10353,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) query = createPQExpBuffer(); q = createPQExpBuffer(); delq = createPQExpBuffer(); + labelq = createPQExpBuffer(); details = createPQExpBuffer(); /* Make sure we are in proper schema */ @@ -9934,6 +10498,11 @@ dumpAgg(Archive *fout, AggInfo *agginfo) appendPQExpBuffer(q, "CREATE AGGREGATE %s (\n%s\n);\n", aggsig, details->data); + appendPQExpBuffer(labelq, "AGGREGATE %s", aggsig); + + if (binary_upgrade) + binary_upgrade_extension_member(q, &agginfo->aggfn.dobj, labelq->data); + ArchiveEntry(fout, agginfo->aggfn.dobj.catId, agginfo->aggfn.dobj.dumpId, aggsig_tag, agginfo->aggfn.dobj.namespace->dobj.name, @@ -9945,12 +10514,10 @@ dumpAgg(Archive *fout, AggInfo *agginfo) NULL, NULL); /* Dump Aggregate Comments */ - resetPQExpBuffer(q); - appendPQExpBuffer(q, "AGGREGATE %s", aggsig); - dumpComment(fout, q->data, + dumpComment(fout, labelq->data, agginfo->aggfn.dobj.namespace->dobj.name, agginfo->aggfn.rolname, agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId); - dumpSecLabel(fout, q->data, + dumpSecLabel(fout, labelq->data, agginfo->aggfn.dobj.namespace->dobj.name, agginfo->aggfn.rolname, agginfo->aggfn.dobj.catId, 0, agginfo->aggfn.dobj.dumpId); @@ -9979,6 +10546,7 @@ dumpAgg(Archive *fout, AggInfo *agginfo) destroyPQExpBuffer(query); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); + destroyPQExpBuffer(labelq); destroyPQExpBuffer(details); } @@ -9991,6 +10559,7 @@ dumpTSParser(Archive *fout, TSParserInfo *prsinfo) { PQExpBuffer q; PQExpBuffer delq; + PQExpBuffer labelq; /* Skip if not to be dumped */ if (!prsinfo->dobj.dump || dataOnly) @@ -9998,6 +10567,7 @@ dumpTSParser(Archive *fout, TSParserInfo *prsinfo) q = createPQExpBuffer(); delq = createPQExpBuffer(); + labelq = createPQExpBuffer(); /* Make sure we are in proper schema */ selectSourceSchema(prsinfo->dobj.namespace->dobj.name); @@ -10025,6 +10595,12 @@ dumpTSParser(Archive *fout, TSParserInfo *prsinfo) appendPQExpBuffer(delq, ".%s;\n", fmtId(prsinfo->dobj.name)); + appendPQExpBuffer(labelq, "TEXT SEARCH PARSER %s", + fmtId(prsinfo->dobj.name)); + + if (binary_upgrade) + binary_upgrade_extension_member(q, &prsinfo->dobj, labelq->data); + ArchiveEntry(fout, prsinfo->dobj.catId, prsinfo->dobj.dumpId, prsinfo->dobj.name, prsinfo->dobj.namespace->dobj.name, @@ -10036,15 +10612,13 @@ dumpTSParser(Archive *fout, TSParserInfo *prsinfo) NULL, NULL); /* Dump Parser Comments */ - resetPQExpBuffer(q); - appendPQExpBuffer(q, "TEXT SEARCH PARSER %s", - fmtId(prsinfo->dobj.name)); - dumpComment(fout, q->data, + dumpComment(fout, labelq->data, NULL, "", prsinfo->dobj.catId, 0, prsinfo->dobj.dumpId); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); + destroyPQExpBuffer(labelq); } /* @@ -10056,6 +10630,7 @@ dumpTSDictionary(Archive *fout, TSDictInfo *dictinfo) { PQExpBuffer q; PQExpBuffer delq; + PQExpBuffer labelq; PQExpBuffer query; PGresult *res; int ntups; @@ -10068,6 +10643,7 @@ dumpTSDictionary(Archive *fout, TSDictInfo *dictinfo) q = createPQExpBuffer(); delq = createPQExpBuffer(); + labelq = createPQExpBuffer(); query = createPQExpBuffer(); /* Fetch name and namespace of the dictionary's template */ @@ -10117,6 +10693,12 @@ dumpTSDictionary(Archive *fout, TSDictInfo *dictinfo) appendPQExpBuffer(delq, ".%s;\n", fmtId(dictinfo->dobj.name)); + appendPQExpBuffer(labelq, "TEXT SEARCH DICTIONARY %s", + fmtId(dictinfo->dobj.name)); + + if (binary_upgrade) + binary_upgrade_extension_member(q, &dictinfo->dobj, labelq->data); + ArchiveEntry(fout, dictinfo->dobj.catId, dictinfo->dobj.dumpId, dictinfo->dobj.name, dictinfo->dobj.namespace->dobj.name, @@ -10128,15 +10710,13 @@ dumpTSDictionary(Archive *fout, TSDictInfo *dictinfo) NULL, NULL); /* Dump Dictionary Comments */ - resetPQExpBuffer(q); - appendPQExpBuffer(q, "TEXT SEARCH DICTIONARY %s", - fmtId(dictinfo->dobj.name)); - dumpComment(fout, q->data, + dumpComment(fout, labelq->data, NULL, dictinfo->rolname, dictinfo->dobj.catId, 0, dictinfo->dobj.dumpId); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); + destroyPQExpBuffer(labelq); destroyPQExpBuffer(query); } @@ -10149,6 +10729,7 @@ dumpTSTemplate(Archive *fout, TSTemplateInfo *tmplinfo) { PQExpBuffer q; PQExpBuffer delq; + PQExpBuffer labelq; /* Skip if not to be dumped */ if (!tmplinfo->dobj.dump || dataOnly) @@ -10156,6 +10737,7 @@ dumpTSTemplate(Archive *fout, TSTemplateInfo *tmplinfo) q = createPQExpBuffer(); delq = createPQExpBuffer(); + labelq = createPQExpBuffer(); /* Make sure we are in proper schema */ selectSourceSchema(tmplinfo->dobj.namespace->dobj.name); @@ -10177,6 +10759,12 @@ dumpTSTemplate(Archive *fout, TSTemplateInfo *tmplinfo) appendPQExpBuffer(delq, ".%s;\n", fmtId(tmplinfo->dobj.name)); + appendPQExpBuffer(labelq, "TEXT SEARCH TEMPLATE %s", + fmtId(tmplinfo->dobj.name)); + + if (binary_upgrade) + binary_upgrade_extension_member(q, &tmplinfo->dobj, labelq->data); + ArchiveEntry(fout, tmplinfo->dobj.catId, tmplinfo->dobj.dumpId, tmplinfo->dobj.name, tmplinfo->dobj.namespace->dobj.name, @@ -10188,15 +10776,13 @@ dumpTSTemplate(Archive *fout, TSTemplateInfo *tmplinfo) NULL, NULL); /* Dump Template Comments */ - resetPQExpBuffer(q); - appendPQExpBuffer(q, "TEXT SEARCH TEMPLATE %s", - fmtId(tmplinfo->dobj.name)); - dumpComment(fout, q->data, + dumpComment(fout, labelq->data, NULL, "", tmplinfo->dobj.catId, 0, tmplinfo->dobj.dumpId); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); + destroyPQExpBuffer(labelq); } /* @@ -10208,6 +10794,7 @@ dumpTSConfig(Archive *fout, TSConfigInfo *cfginfo) { PQExpBuffer q; PQExpBuffer delq; + PQExpBuffer labelq; PQExpBuffer query; PGresult *res; char *nspname; @@ -10223,6 +10810,7 @@ dumpTSConfig(Archive *fout, TSConfigInfo *cfginfo) q = createPQExpBuffer(); delq = createPQExpBuffer(); + labelq = createPQExpBuffer(); query = createPQExpBuffer(); /* Fetch name and namespace of the config's parser */ @@ -10310,6 +10898,12 @@ dumpTSConfig(Archive *fout, TSConfigInfo *cfginfo) appendPQExpBuffer(delq, ".%s;\n", fmtId(cfginfo->dobj.name)); + appendPQExpBuffer(labelq, "TEXT SEARCH CONFIGURATION %s", + fmtId(cfginfo->dobj.name)); + + if (binary_upgrade) + binary_upgrade_extension_member(q, &cfginfo->dobj, labelq->data); + ArchiveEntry(fout, cfginfo->dobj.catId, cfginfo->dobj.dumpId, cfginfo->dobj.name, cfginfo->dobj.namespace->dobj.name, @@ -10321,15 +10915,13 @@ dumpTSConfig(Archive *fout, TSConfigInfo *cfginfo) NULL, NULL); /* Dump Configuration Comments */ - resetPQExpBuffer(q); - appendPQExpBuffer(q, "TEXT SEARCH CONFIGURATION %s", - fmtId(cfginfo->dobj.name)); - dumpComment(fout, q->data, + dumpComment(fout, labelq->data, NULL, cfginfo->rolname, cfginfo->dobj.catId, 0, cfginfo->dobj.dumpId); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); + destroyPQExpBuffer(labelq); destroyPQExpBuffer(query); } @@ -10342,7 +10934,8 @@ dumpForeignDataWrapper(Archive *fout, FdwInfo *fdwinfo) { PQExpBuffer q; PQExpBuffer delq; - char *namecopy; + PQExpBuffer labelq; + char *qfdwname; /* Skip if not to be dumped */ if (!fdwinfo->dobj.dump || dataOnly) @@ -10350,9 +10943,12 @@ dumpForeignDataWrapper(Archive *fout, FdwInfo *fdwinfo) q = createPQExpBuffer(); delq = createPQExpBuffer(); + labelq = createPQExpBuffer(); + + qfdwname = strdup(fmtId(fdwinfo->dobj.name)); appendPQExpBuffer(q, "CREATE FOREIGN DATA WRAPPER %s", - fmtId(fdwinfo->dobj.name)); + qfdwname); if (fdwinfo->fdwvalidator && strcmp(fdwinfo->fdwvalidator, "-") != 0) appendPQExpBuffer(q, " VALIDATOR %s", @@ -10369,7 +10965,13 @@ dumpForeignDataWrapper(Archive *fout, FdwInfo *fdwinfo) appendPQExpBuffer(q, ";\n"); appendPQExpBuffer(delq, "DROP FOREIGN DATA WRAPPER %s;\n", - fmtId(fdwinfo->dobj.name)); + qfdwname); + + appendPQExpBuffer(labelq, "FOREIGN DATA WRAPPER %s", + qfdwname); + + if (binary_upgrade) + binary_upgrade_extension_member(q, &fdwinfo->dobj, labelq->data); ArchiveEntry(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId, fdwinfo->dobj.name, @@ -10382,16 +10984,17 @@ dumpForeignDataWrapper(Archive *fout, FdwInfo *fdwinfo) NULL, NULL); /* Handle the ACL */ - namecopy = strdup(fmtId(fdwinfo->dobj.name)); dumpACL(fout, fdwinfo->dobj.catId, fdwinfo->dobj.dumpId, "FOREIGN DATA WRAPPER", - namecopy, NULL, fdwinfo->dobj.name, + qfdwname, NULL, fdwinfo->dobj.name, NULL, fdwinfo->rolname, fdwinfo->fdwacl); - free(namecopy); + + free(qfdwname); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); + destroyPQExpBuffer(labelq); } /* @@ -10403,10 +11006,11 @@ dumpForeignServer(Archive *fout, ForeignServerInfo *srvinfo) { PQExpBuffer q; PQExpBuffer delq; + PQExpBuffer labelq; PQExpBuffer query; PGresult *res; int ntups; - char *namecopy; + char *qsrvname; char *fdwname; /* Skip if not to be dumped */ @@ -10415,8 +11019,11 @@ dumpForeignServer(Archive *fout, ForeignServerInfo *srvinfo) q = createPQExpBuffer(); delq = createPQExpBuffer(); + labelq = createPQExpBuffer(); query = createPQExpBuffer(); + qsrvname = strdup(fmtId(srvinfo->dobj.name)); + /* look up the foreign-data wrapper */ selectSourceSchema("pg_catalog"); appendPQExpBuffer(query, "SELECT fdwname " @@ -10436,7 +11043,7 @@ dumpForeignServer(Archive *fout, ForeignServerInfo *srvinfo) } fdwname = PQgetvalue(res, 0, 0); - appendPQExpBuffer(q, "CREATE SERVER %s", fmtId(srvinfo->dobj.name)); + appendPQExpBuffer(q, "CREATE SERVER %s", qsrvname); if (srvinfo->srvtype && strlen(srvinfo->srvtype) > 0) { appendPQExpBuffer(q, " TYPE "); @@ -10457,7 +11064,12 @@ dumpForeignServer(Archive *fout, ForeignServerInfo *srvinfo) appendPQExpBuffer(q, ";\n"); appendPQExpBuffer(delq, "DROP SERVER %s;\n", - fmtId(srvinfo->dobj.name)); + qsrvname); + + appendPQExpBuffer(labelq, "SERVER %s", qsrvname); + + if (binary_upgrade) + binary_upgrade_extension_member(q, &srvinfo->dobj, labelq->data); ArchiveEntry(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId, srvinfo->dobj.name, @@ -10470,13 +11082,11 @@ dumpForeignServer(Archive *fout, ForeignServerInfo *srvinfo) NULL, NULL); /* Handle the ACL */ - namecopy = strdup(fmtId(srvinfo->dobj.name)); dumpACL(fout, srvinfo->dobj.catId, srvinfo->dobj.dumpId, "FOREIGN SERVER", - namecopy, NULL, srvinfo->dobj.name, + qsrvname, NULL, srvinfo->dobj.name, NULL, srvinfo->rolname, srvinfo->srvacl); - free(namecopy); /* Dump user mappings */ dumpUserMappings(fout, @@ -10484,8 +11094,11 @@ dumpForeignServer(Archive *fout, ForeignServerInfo *srvinfo) srvinfo->rolname, srvinfo->dobj.catId, srvinfo->dobj.dumpId); + free(qsrvname); + destroyPQExpBuffer(q); destroyPQExpBuffer(delq); + destroyPQExpBuffer(labelq); } /* @@ -11080,6 +11693,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) PQExpBuffer query = createPQExpBuffer(); PQExpBuffer q = createPQExpBuffer(); PQExpBuffer delq = createPQExpBuffer(); + PQExpBuffer labelq = createPQExpBuffer(); PGresult *res; int numParents; TableInfo **parents; @@ -11160,6 +11774,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBuffer(q, "CREATE VIEW %s AS\n %s\n", fmtId(tbinfo->dobj.name), viewdef); + appendPQExpBuffer(labelq, "VIEW %s", + fmtId(tbinfo->dobj.name)); + PQclear(res); } else @@ -11205,6 +11822,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) appendPQExpBuffer(delq, "%s;\n", fmtId(tbinfo->dobj.name)); + appendPQExpBuffer(labelq, "%s %s", reltypename, + fmtId(tbinfo->dobj.name)); + if (binary_upgrade) binary_upgrade_set_pg_class_oids(q, tbinfo->dobj.catId.oid, false); @@ -11542,6 +12162,9 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) } } + if (binary_upgrade) + binary_upgrade_extension_member(q, &tbinfo->dobj, labelq->data); + ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId, tbinfo->dobj.name, tbinfo->dobj.namespace->dobj.name, @@ -11574,6 +12197,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo) destroyPQExpBuffer(query); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); + destroyPQExpBuffer(labelq); } /* @@ -11673,12 +12297,17 @@ dumpIndex(Archive *fout, IndxInfo *indxinfo) TableInfo *tbinfo = indxinfo->indextable; PQExpBuffer q; PQExpBuffer delq; + PQExpBuffer labelq; if (dataOnly) return; q = createPQExpBuffer(); delq = createPQExpBuffer(); + labelq = createPQExpBuffer(); + + appendPQExpBuffer(labelq, "INDEX %s", + fmtId(indxinfo->dobj.name)); /* * If there's an associated constraint, don't dump the index per se, but @@ -11723,16 +12352,14 @@ dumpIndex(Archive *fout, IndxInfo *indxinfo) } /* Dump Index Comments */ - resetPQExpBuffer(q); - appendPQExpBuffer(q, "INDEX %s", - fmtId(indxinfo->dobj.name)); - dumpComment(fout, q->data, + dumpComment(fout, labelq->data, tbinfo->dobj.namespace->dobj.name, tbinfo->rolname, indxinfo->dobj.catId, 0, indxinfo->dobj.dumpId); destroyPQExpBuffer(q); destroyPQExpBuffer(delq); + destroyPQExpBuffer(labelq); } /* @@ -11976,19 +12603,19 @@ static void dumpTableConstraintComment(Archive *fout, ConstraintInfo *coninfo) { TableInfo *tbinfo = coninfo->contable; - PQExpBuffer q = createPQExpBuffer(); + PQExpBuffer labelq = createPQExpBuffer(); - appendPQExpBuffer(q, "CONSTRAINT %s ", + appendPQExpBuffer(labelq, "CONSTRAINT %s ", fmtId(coninfo->dobj.name)); - appendPQExpBuffer(q, "ON %s", + appendPQExpBuffer(labelq, "ON %s", fmtId(tbinfo->dobj.name)); - dumpComment(fout, q->data, + dumpComment(fout, labelq->data, tbinfo->dobj.namespace->dobj.name, tbinfo->rolname, coninfo->dobj.catId, 0, coninfo->separate ? coninfo->dobj.dumpId : tbinfo->dobj.dumpId); - destroyPQExpBuffer(q); + destroyPQExpBuffer(labelq); } /* @@ -12082,6 +12709,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) called; PQExpBuffer query = createPQExpBuffer(); PQExpBuffer delqry = createPQExpBuffer(); + PQExpBuffer labelq = createPQExpBuffer(); /* Make sure we are in proper schema */ selectSourceSchema(tbinfo->dobj.namespace->dobj.name); @@ -12169,8 +12797,6 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) */ if (!dataOnly) { - resetPQExpBuffer(delqry); - /* * DROP must be fully qualified in case same name appears in * pg_catalog @@ -12223,8 +12849,14 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) appendPQExpBuffer(query, ";\n"); + appendPQExpBuffer(labelq, "SEQUENCE %s", fmtId(tbinfo->dobj.name)); + /* binary_upgrade: no need to clear TOAST table oid */ + if (binary_upgrade) + binary_upgrade_extension_member(query, &tbinfo->dobj, + labelq->data); + ArchiveEntry(fout, tbinfo->dobj.catId, tbinfo->dobj.dumpId, tbinfo->dobj.name, tbinfo->dobj.namespace->dobj.name, @@ -12274,12 +12906,10 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) } /* Dump Sequence Comments and Security Labels */ - resetPQExpBuffer(query); - appendPQExpBuffer(query, "SEQUENCE %s", fmtId(tbinfo->dobj.name)); - dumpComment(fout, query->data, + dumpComment(fout, labelq->data, tbinfo->dobj.namespace->dobj.name, tbinfo->rolname, tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId); - dumpSecLabel(fout, query->data, + dumpSecLabel(fout, labelq->data, tbinfo->dobj.namespace->dobj.name, tbinfo->rolname, tbinfo->dobj.catId, 0, tbinfo->dobj.dumpId); } @@ -12307,6 +12937,7 @@ dumpSequence(Archive *fout, TableInfo *tbinfo) destroyPQExpBuffer(query); destroyPQExpBuffer(delqry); + destroyPQExpBuffer(labelq); } static void @@ -12315,6 +12946,7 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo) TableInfo *tbinfo = tginfo->tgtable; PQExpBuffer query; PQExpBuffer delqry; + PQExpBuffer labelq; char *tgargs; size_t lentgargs; const char *p; @@ -12325,6 +12957,7 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo) query = createPQExpBuffer(); delqry = createPQExpBuffer(); + labelq = createPQExpBuffer(); /* * DROP must be fully qualified in case same name appears in pg_catalog @@ -12485,6 +13118,11 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo) fmtId(tginfo->dobj.name)); } + appendPQExpBuffer(labelq, "TRIGGER %s ", + fmtId(tginfo->dobj.name)); + appendPQExpBuffer(labelq, "ON %s", + fmtId(tbinfo->dobj.name)); + ArchiveEntry(fout, tginfo->dobj.catId, tginfo->dobj.dumpId, tginfo->dobj.name, tbinfo->dobj.namespace->dobj.name, @@ -12495,18 +13133,13 @@ dumpTrigger(Archive *fout, TriggerInfo *tginfo) tginfo->dobj.dependencies, tginfo->dobj.nDeps, NULL, NULL); - resetPQExpBuffer(query); - appendPQExpBuffer(query, "TRIGGER %s ", - fmtId(tginfo->dobj.name)); - appendPQExpBuffer(query, "ON %s", - fmtId(tbinfo->dobj.name)); - - dumpComment(fout, query->data, + dumpComment(fout, labelq->data, tbinfo->dobj.namespace->dobj.name, tbinfo->rolname, tginfo->dobj.catId, 0, tginfo->dobj.dumpId); destroyPQExpBuffer(query); destroyPQExpBuffer(delqry); + destroyPQExpBuffer(labelq); } /* @@ -12520,6 +13153,7 @@ dumpRule(Archive *fout, RuleInfo *rinfo) PQExpBuffer query; PQExpBuffer cmd; PQExpBuffer delcmd; + PQExpBuffer labelq; PGresult *res; /* Skip if not to be dumped */ @@ -12541,6 +13175,7 @@ dumpRule(Archive *fout, RuleInfo *rinfo) query = createPQExpBuffer(); cmd = createPQExpBuffer(); delcmd = createPQExpBuffer(); + labelq = createPQExpBuffer(); if (g_fout->remoteVersion >= 70300) { @@ -12605,6 +13240,11 @@ dumpRule(Archive *fout, RuleInfo *rinfo) appendPQExpBuffer(delcmd, "%s;\n", fmtId(tbinfo->dobj.name)); + appendPQExpBuffer(labelq, "RULE %s", + fmtId(rinfo->dobj.name)); + appendPQExpBuffer(labelq, " ON %s", + fmtId(tbinfo->dobj.name)); + ArchiveEntry(fout, rinfo->dobj.catId, rinfo->dobj.dumpId, rinfo->dobj.name, tbinfo->dobj.namespace->dobj.name, @@ -12616,12 +13256,7 @@ dumpRule(Archive *fout, RuleInfo *rinfo) NULL, NULL); /* Dump rule comments */ - resetPQExpBuffer(query); - appendPQExpBuffer(query, "RULE %s", - fmtId(rinfo->dobj.name)); - appendPQExpBuffer(query, " ON %s", - fmtId(tbinfo->dobj.name)); - dumpComment(fout, query->data, + dumpComment(fout, labelq->data, tbinfo->dobj.namespace->dobj.name, tbinfo->rolname, rinfo->dobj.catId, 0, rinfo->dobj.dumpId); @@ -12631,6 +13266,168 @@ dumpRule(Archive *fout, RuleInfo *rinfo) destroyPQExpBuffer(query); destroyPQExpBuffer(cmd); destroyPQExpBuffer(delcmd); + destroyPQExpBuffer(labelq); +} + +/* + * getExtensionMembership --- obtain extension membership data + */ +void +getExtensionMembership(ExtensionInfo extinfo[], int numExtensions) +{ + PQExpBuffer query; + PGresult *res; + int ntups, + i; + int i_classid, + i_objid, + i_refclassid, + i_refobjid; + DumpableObject *dobj, + *refdobj; + + /* Nothing to do if no extensions */ + if (numExtensions == 0) + return; + + /* Make sure we are in proper schema */ + selectSourceSchema("pg_catalog"); + + query = createPQExpBuffer(); + + /* refclassid constraint is redundant but may speed the search */ + appendPQExpBuffer(query, "SELECT " + "classid, objid, refclassid, refobjid " + "FROM pg_depend " + "WHERE refclassid = 'pg_extension'::regclass " + "AND deptype = 'e' " + "ORDER BY 3,4"); + + res = PQexec(g_conn, query->data); + check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK); + + ntups = PQntuples(res); + + i_classid = PQfnumber(res, "classid"); + i_objid = PQfnumber(res, "objid"); + i_refclassid = PQfnumber(res, "refclassid"); + i_refobjid = PQfnumber(res, "refobjid"); + + /* + * Since we ordered the SELECT by referenced ID, we can expect that + * multiple entries for the same extension will appear together; this + * saves on searches. + */ + refdobj = NULL; + + for (i = 0; i < ntups; i++) + { + CatalogId objId; + CatalogId refobjId; + + objId.tableoid = atooid(PQgetvalue(res, i, i_classid)); + objId.oid = atooid(PQgetvalue(res, i, i_objid)); + refobjId.tableoid = atooid(PQgetvalue(res, i, i_refclassid)); + refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid)); + + if (refdobj == NULL || + refdobj->catId.tableoid != refobjId.tableoid || + refdobj->catId.oid != refobjId.oid) + refdobj = findObjectByCatalogId(refobjId); + + /* + * Failure to find objects mentioned in pg_depend is not unexpected, + * since for example we don't collect info about TOAST tables. + */ + if (refdobj == NULL) + { +#ifdef NOT_USED + fprintf(stderr, "no referenced object %u %u\n", + refobjId.tableoid, refobjId.oid); +#endif + continue; + } + + dobj = findObjectByCatalogId(objId); + + if (dobj == NULL) + { +#ifdef NOT_USED + fprintf(stderr, "no referencing object %u %u\n", + objId.tableoid, objId.oid); +#endif + continue; + } + + /* Record dependency so that getDependencies needn't repeat this */ + addObjectDependency(dobj, refdobj->dumpId); + + dobj->ext_member = true; + + /* + * Normally, mark the member object as not to be dumped. But in + * binary upgrades, we still dump the members individually, since + * the idea is to exactly reproduce the database contents rather + * than replace the extension contents with something different. + */ + if (!binary_upgrade) + dobj->dump = false; + else + dobj->dump = refdobj->dump; + } + + PQclear(res); + + /* + * Now identify extension configuration tables and create TableDataInfo + * objects for them, ensuring their data will be dumped even though the + * tables themselves won't be. + * + * Note that we create TableDataInfo objects even in schemaOnly mode, + * ie, user data in a configuration table is treated like schema data. + * This seems appropriate since system data in a config table would + * get reloaded by CREATE EXTENSION. + */ + for (i = 0; i < numExtensions; i++) + { + char *extconfig = extinfo[i].extconfig; + char *extcondition = extinfo[i].extcondition; + char **extconfigarray = NULL; + char **extconditionarray = NULL; + int nconfigitems; + int nconditionitems; + + if (parsePGArray(extconfig, &extconfigarray, &nconfigitems) && + parsePGArray(extcondition, &extconditionarray, &nconditionitems) && + nconfigitems == nconditionitems) + { + int j; + + for (j = 0; j < nconfigitems; j++) + { + TableInfo *configtbl; + + configtbl = findTableByOid(atooid(extconfigarray[j])); + if (configtbl && configtbl->dataObj == NULL) + { + /* + * Note: config tables are dumped without OIDs regardless + * of the --oids setting. This is because row filtering + * conditions aren't compatible with dumping OIDs. + */ + makeTableDataInfo(configtbl, false); + if (strlen(extconditionarray[j]) > 0) + configtbl->dataObj->filtercond = strdup(extconditionarray[j]); + } + } + } + if (extconfigarray) + free(extconfigarray); + if (extconditionarray) + free(extconditionarray); + } + + destroyPQExpBuffer(query); } /* @@ -12663,10 +13460,14 @@ getDependencies(void) query = createPQExpBuffer(); + /* + * PIN dependencies aren't interesting, and EXTENSION dependencies were + * already processed by getExtensionMembership. + */ appendPQExpBuffer(query, "SELECT " "classid, objid, refclassid, refobjid, deptype " "FROM pg_depend " - "WHERE deptype != 'p' " + "WHERE deptype != 'p' AND deptype != 'e' " "ORDER BY 1,2"); res = PQexec(g_conn, query->data); diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index c630a1d06f..cc076860ab 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -89,6 +89,7 @@ typedef enum { /* When modifying this enum, update priority tables in pg_dump_sort.c! */ DO_NAMESPACE, + DO_EXTENSION, DO_TYPE, DO_SHELL_TYPE, DO_FUNC, @@ -116,7 +117,8 @@ typedef enum DO_FOREIGN_SERVER, DO_DEFAULT_ACL, DO_BLOB, - DO_BLOB_DATA + DO_BLOB_DATA, + DO_COLLATION } DumpableObjectType; typedef struct _dumpableObject @@ -127,6 +129,7 @@ typedef struct _dumpableObject char *name; /* object name (should never be NULL) */ struct _namespaceInfo *namespace; /* containing namespace, or NULL */ bool dump; /* true if we want to dump this object */ + bool ext_member; /* true if object is member of extension */ DumpId *dependencies; /* dumpIds of objects this one depends on */ int nDeps; /* number of valid dependencies */ int allocDeps; /* allocated size of dependencies[] */ @@ -139,6 +142,16 @@ typedef struct _namespaceInfo char *nspacl; } NamespaceInfo; +typedef struct _extensionInfo +{ + DumpableObject dobj; + char *namespace; /* schema containing extension's objects */ + bool relocatable; + char *extversion; + char *extconfig; /* info about configuration tables */ + char *extcondition; +} ExtensionInfo; + typedef struct _typeInfo { DumpableObject dobj; @@ -205,6 +218,12 @@ typedef struct _opfamilyInfo char *rolname; } OpfamilyInfo; +typedef struct _collInfo +{ + DumpableObject dobj; + char *rolname; +} CollInfo; + typedef struct _convInfo { DumpableObject dobj; @@ -288,6 +307,7 @@ typedef struct _tableDataInfo DumpableObject dobj; TableInfo *tdtable; /* link to table to dump */ bool oids; /* include OIDs in data? */ + char *filtercond; /* WHERE condition to limit rows dumped */ } TableDataInfo; typedef struct _indxInfo @@ -514,12 +534,14 @@ extern void sortDumpableObjectsByTypeOid(DumpableObject **objs, int numObjs); * version specific routines */ extern NamespaceInfo *getNamespaces(int *numNamespaces); +extern ExtensionInfo *getExtensions(int *numExtensions); extern TypeInfo *getTypes(int *numTypes); extern FuncInfo *getFuncs(int *numFuncs); extern AggInfo *getAggregates(int *numAggregates); extern OprInfo *getOperators(int *numOperators); extern OpclassInfo *getOpclasses(int *numOpclasses); extern OpfamilyInfo *getOpfamilies(int *numOpfamilies); +extern CollInfo *getCollations(int *numCollations); extern ConvInfo *getConversions(int *numConversions); extern TableInfo *getTables(int *numTables); extern InhInfo *getInherits(int *numInherits); @@ -537,5 +559,6 @@ extern TSConfigInfo *getTSConfigurations(int *numTSConfigs); extern FdwInfo *getForeignDataWrappers(int *numForeignDataWrappers); extern ForeignServerInfo *getForeignServers(int *numForeignServers); extern DefaultACLInfo *getDefaultACLs(int *numDefaultACLs); +extern void getExtensionMembership(ExtensionInfo extinfo[], int numExtensions); #endif /* PG_DUMP_H */ diff --git a/src/bin/pg_dump/pg_dump_sort.c b/src/bin/pg_dump/pg_dump_sort.c index fe5cf56e69..daabd5e856 100644 --- a/src/bin/pg_dump/pg_dump_sort.c +++ b/src/bin/pg_dump/pg_dump_sort.c @@ -22,13 +22,14 @@ static const char *modulename = gettext_noop("sorter"); * Sort priority for object types when dumping a pre-7.3 database. * Objects are sorted by priority levels, and within an equal priority level * by OID. (This is a relatively crude hack to provide semi-reasonable - * behavior for old databases without full dependency info.) Note: text - * search, foreign-data, and default ACL objects can't really happen here, - * so the rather bogus priorities for them don't matter. + * behavior for old databases without full dependency info.) Note: collations, + * extensions, text search, foreign-data, and default ACL objects can't really + * happen here, so the rather bogus priorities for them don't matter. */ static const int oldObjectTypePriority[] = { 1, /* DO_NAMESPACE */ + 1, /* DO_EXTENSION */ 2, /* DO_TYPE */ 2, /* DO_SHELL_TYPE */ 2, /* DO_FUNC */ @@ -56,7 +57,8 @@ static const int oldObjectTypePriority[] = 4, /* DO_FOREIGN_SERVER */ 17, /* DO_DEFAULT_ACL */ 9, /* DO_BLOB */ - 11 /* DO_BLOB_DATA */ + 11, /* DO_BLOB_DATA */ + 2 /* DO_COLLATION */ }; /* @@ -66,34 +68,36 @@ static const int oldObjectTypePriority[] = static const int newObjectTypePriority[] = { 1, /* DO_NAMESPACE */ - 3, /* DO_TYPE */ - 3, /* DO_SHELL_TYPE */ - 4, /* DO_FUNC */ - 5, /* DO_AGG */ - 6, /* DO_OPERATOR */ - 7, /* DO_OPCLASS */ - 7, /* DO_OPFAMILY */ - 9, /* DO_CONVERSION */ - 16, /* DO_TABLE */ - 18, /* DO_ATTRDEF */ - 23, /* DO_INDEX */ - 24, /* DO_RULE */ - 25, /* DO_TRIGGER */ - 22, /* DO_CONSTRAINT */ - 26, /* DO_FK_CONSTRAINT */ + 3, /* DO_EXTENSION */ + 4, /* DO_TYPE */ + 4, /* DO_SHELL_TYPE */ + 5, /* DO_FUNC */ + 6, /* DO_AGG */ + 7, /* DO_OPERATOR */ + 8, /* DO_OPCLASS */ + 8, /* DO_OPFAMILY */ + 10, /* DO_CONVERSION */ + 17, /* DO_TABLE */ + 19, /* DO_ATTRDEF */ + 24, /* DO_INDEX */ + 25, /* DO_RULE */ + 26, /* DO_TRIGGER */ + 23, /* DO_CONSTRAINT */ + 27, /* DO_FK_CONSTRAINT */ 2, /* DO_PROCLANG */ - 8, /* DO_CAST */ - 20, /* DO_TABLE_DATA */ - 17, /* DO_DUMMY_TYPE */ - 10, /* DO_TSPARSER */ - 12, /* DO_TSDICT */ - 11, /* DO_TSTEMPLATE */ - 13, /* DO_TSCONFIG */ - 14, /* DO_FDW */ - 15, /* DO_FOREIGN_SERVER */ - 27, /* DO_DEFAULT_ACL */ - 19, /* DO_BLOB */ - 21 /* DO_BLOB_DATA */ + 9, /* DO_CAST */ + 21, /* DO_TABLE_DATA */ + 18, /* DO_DUMMY_TYPE */ + 11, /* DO_TSPARSER */ + 13, /* DO_TSDICT */ + 12, /* DO_TSTEMPLATE */ + 14, /* DO_TSCONFIG */ + 15, /* DO_FDW */ + 16, /* DO_FOREIGN_SERVER */ + 28, /* DO_DEFAULT_ACL */ + 20, /* DO_BLOB */ + 22, /* DO_BLOB_DATA */ + 3 /* DO_COLLATION */ }; @@ -1023,6 +1027,11 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize) "SCHEMA %s (ID %d OID %u)", obj->name, obj->dumpId, obj->catId.oid); return; + case DO_EXTENSION: + snprintf(buf, bufsize, + "EXTENSION %s (ID %d OID %u)", + obj->name, obj->dumpId, obj->catId.oid); + return; case DO_TYPE: snprintf(buf, bufsize, "TYPE %s (ID %d OID %u)", @@ -1058,6 +1067,11 @@ describeDumpableObject(DumpableObject *obj, char *buf, int bufsize) "OPERATOR FAMILY %s (ID %d OID %u)", obj->name, obj->dumpId, obj->catId.oid); return; + case DO_COLLATION: + snprintf(buf, bufsize, + "COLLATION %s (ID %d OID %u)", + obj->name, obj->dumpId, obj->catId.oid); + return; case DO_CONVERSION: snprintf(buf, bufsize, "CONVERSION %s (ID %d OID %u)", diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 301dc11bcc..d1268848d5 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -425,6 +425,9 @@ exec_command(const char *cmd, case 'o': success = describeOperators(pattern, show_system); break; + case 'O': + success = listCollations(pattern, show_verbose, show_system); + break; case 'p': success = permissionsList(pattern); break; @@ -495,6 +498,12 @@ exec_command(const char *cmd, break; } break; + case 'x': /* Extensions */ + if (show_verbose) + success = listExtensionContents(pattern); + else + success = listExtensions(pattern); + break; default: status = PSQL_CMD_UNKNOWN; } diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 57718eb9b7..719782f6c5 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -38,6 +38,7 @@ static bool describeOneTSConfig(const char *oid, const char *nspname, const char *cfgname, const char *pnspname, const char *prsname); static void printACLColumn(PQExpBuffer buf, const char *colname); +static bool listOneExtensionContents(const char *extname, const char *oid); /*---------------- @@ -626,7 +627,7 @@ listAllDbs(bool verbose) appendPQExpBuffer(&buf, " d.datcollate as \"%s\",\n" " d.datctype as \"%s\",\n", - gettext_noop("Collation"), + gettext_noop("Collate"), gettext_noop("Ctype")); appendPQExpBuffer(&buf, " "); printACLColumn(&buf, "d.datacl"); @@ -1274,6 +1275,10 @@ describeOneTableDetails(const char *schemaname, "\n FROM pg_catalog.pg_attrdef d" "\n WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef)," "\n a.attnotnull, a.attnum"); + if (pset.sversion >= 90100) + appendPQExpBuffer(&buf, ",\n (SELECT collname FROM pg_collation WHERE oid = a.attcollation AND collname <> 'default') AS attcollation"); + else + appendPQExpBuffer(&buf, ",\n NULL AS attcollation"); if (tableinfo.relkind == 'i') appendPQExpBuffer(&buf, ",\n pg_catalog.pg_get_indexdef(a.attrelid, a.attnum, TRUE) AS indexdef"); if (verbose) @@ -1396,12 +1401,25 @@ describeOneTableDetails(const char *schemaname, /* Type */ printTableAddCell(&cont, PQgetvalue(res, i, 1), false, false); - /* Modifiers: not null and default */ + /* Modifiers: collate, not null, default */ if (show_modifiers) { resetPQExpBuffer(&tmpbuf); + + if (!PQgetisnull(res, i, 5)) + { + if (tmpbuf.len > 0) + appendPQExpBufferStr(&tmpbuf, " "); + appendPQExpBuffer(&tmpbuf, _("collate %s"), + PQgetvalue(res, i, 5)); + } + if (strcmp(PQgetvalue(res, i, 3), "t") == 0) + { + if (tmpbuf.len > 0) + appendPQExpBufferStr(&tmpbuf, " "); appendPQExpBufferStr(&tmpbuf, _("not null")); + } /* handle "default" here */ /* (note: above we cut off the 'default' string at 128) */ @@ -1424,12 +1442,12 @@ describeOneTableDetails(const char *schemaname, /* Expression for index column */ if (tableinfo.relkind == 'i') - printTableAddCell(&cont, PQgetvalue(res, i, 5), false, false); + printTableAddCell(&cont, PQgetvalue(res, i, 6), false, false); /* Storage and Description */ if (verbose) { - int firstvcol = (tableinfo.relkind == 'i' ? 6 : 5); + int firstvcol = (tableinfo.relkind == 'i' ? 7 : 6); char *storage = PQgetvalue(res, i, firstvcol); /* these strings are literal in our syntax, so not translated. */ @@ -1714,8 +1732,10 @@ describeOneTableDetails(const char *schemaname, { printfPQExpBuffer(&buf, "SELECT conname,\n" - " pg_catalog.pg_get_constraintdef(r.oid, true) as condef\n" - "FROM pg_catalog.pg_constraint r\n" + " pg_catalog.pg_get_constraintdef(r.oid, true) as condef\n"); + if (pset.sversion >= 90100) + appendPQExpBuffer(&buf, " ,convalidated\n"); + appendPQExpBuffer(&buf, "FROM pg_catalog.pg_constraint r\n" "WHERE r.conrelid = '%s' AND r.contype = 'f' ORDER BY 1", oid); result = PSQLexec(buf.data, false); @@ -1734,6 +1754,9 @@ describeOneTableDetails(const char *schemaname, PQgetvalue(result, i, 0), PQgetvalue(result, i, 1)); + if (pset.sversion >= 90100 && strcmp(PQgetvalue(result, i, 2), "f") == 0) + appendPQExpBuffer(&buf, " NOT VALID"); + printTableAddFooter(&cont, buf.data); } } @@ -2834,6 +2857,66 @@ listCasts(const char *pattern) } /* + * \dO + * + * Describes collations + */ +bool +listCollations(const char *pattern, bool verbose, bool showSystem) +{ + PQExpBufferData buf; + PGresult *res; + printQueryOpt myopt = pset.popt; + static const bool translate_columns[] = {false, false, false, false, false}; + + initPQExpBuffer(&buf); + + printfPQExpBuffer(&buf, + "SELECT n.nspname AS \"%s\",\n" + " c.collname AS \"%s\",\n" + " c.collcollate AS \"%s\",\n" + " c.collctype AS \"%s\"", + gettext_noop("Schema"), + gettext_noop("Name"), + gettext_noop("Collate"), + gettext_noop("Ctype")); + + if (verbose) + appendPQExpBuffer(&buf, + ",\n pg_catalog.obj_description(c.oid, 'pg_collation') AS \"%s\"", + gettext_noop("Description")); + + appendPQExpBuffer(&buf, + "FROM pg_catalog.pg_collation c, pg_catalog.pg_namespace n\n" + "WHERE n.oid = c.collnamespace\n"); + + if (!showSystem && !pattern) + appendPQExpBuffer(&buf, " AND n.nspname <> 'pg_catalog'\n" + " AND n.nspname <> 'information_schema'\n"); + + processSQLNamePattern(pset.db, &buf, pattern, true, false, + "n.nspname", "c.collname", NULL, + "pg_catalog.pg_collation_is_visible(c.oid)"); + + appendPQExpBuffer(&buf, "ORDER BY 1, 2;"); + + res = PSQLexec(buf.data, false); + termPQExpBuffer(&buf); + if (!res) + return false; + + myopt.nullPrint = NULL; + myopt.title = _("List of collations"); + myopt.translate_header = true; + myopt.translate_columns = translate_columns; + + printQuery(res, &myopt, pset.queryFout, pset.logfile); + + PQclear(res); + return true; +} + +/* * \dn * * Describes schemas (namespaces) @@ -3651,7 +3734,7 @@ listForeignTables(const char *pattern, bool verbose) if (pset.sversion < 90100) { - fprintf(stderr, _("The server (version %d.%d) does not support foreign table.\n"), + fprintf(stderr, _("The server (version %d.%d) does not support foreign tables.\n"), pset.sversion / 10000, (pset.sversion / 100) % 100); return true; } @@ -3699,6 +3782,167 @@ listForeignTables(const char *pattern, bool verbose) } /* + * \dx + * + * Briefly describes installed extensions. + */ +bool +listExtensions(const char *pattern) +{ + PQExpBufferData buf; + PGresult *res; + printQueryOpt myopt = pset.popt; + + if (pset.sversion < 90100) + { + fprintf(stderr, _("The server (version %d.%d) does not support extensions.\n"), + pset.sversion / 10000, (pset.sversion / 100) % 100); + return true; + } + + initPQExpBuffer(&buf); + printfPQExpBuffer(&buf, + "SELECT e.extname AS \"%s\", " + "e.extversion AS \"%s\", n.nspname AS \"%s\", c.description AS \"%s\"\n" + "FROM pg_catalog.pg_extension e " + "LEFT JOIN pg_catalog.pg_namespace n ON n.oid = e.extnamespace " + "LEFT JOIN pg_catalog.pg_description c ON c.objoid = e.oid " + "AND c.classoid = 'pg_catalog.pg_extension'::pg_catalog.regclass\n", + gettext_noop("Name"), + gettext_noop("Version"), + gettext_noop("Schema"), + gettext_noop("Description")); + + processSQLNamePattern(pset.db, &buf, pattern, + false, false, + NULL, "e.extname", NULL, + NULL); + + appendPQExpBuffer(&buf, "ORDER BY 1;"); + + res = PSQLexec(buf.data, false); + termPQExpBuffer(&buf); + if (!res) + return false; + + myopt.nullPrint = NULL; + myopt.title = _("List of installed extensions"); + myopt.translate_header = true; + + printQuery(res, &myopt, pset.queryFout, pset.logfile); + + PQclear(res); + return true; +} + +/* + * \dx+ + * + * List contents of installed extensions. + */ +bool +listExtensionContents(const char *pattern) +{ + PQExpBufferData buf; + PGresult *res; + int i; + + if (pset.sversion < 90100) + { + fprintf(stderr, _("The server (version %d.%d) does not support extensions.\n"), + pset.sversion / 10000, (pset.sversion / 100) % 100); + return true; + } + + initPQExpBuffer(&buf); + printfPQExpBuffer(&buf, + "SELECT e.extname, e.oid\n" + "FROM pg_catalog.pg_extension e\n"); + + processSQLNamePattern(pset.db, &buf, pattern, + false, false, + NULL, "e.extname", NULL, + NULL); + + appendPQExpBuffer(&buf, "ORDER BY 1;"); + + res = PSQLexec(buf.data, false); + termPQExpBuffer(&buf); + if (!res) + return false; + + if (PQntuples(res) == 0) + { + if (!pset.quiet) + { + if (pattern) + fprintf(stderr, _("Did not find any extension named \"%s\".\n"), + pattern); + else + fprintf(stderr, _("Did not find any extensions.\n")); + } + PQclear(res); + return false; + } + + for (i = 0; i < PQntuples(res); i++) + { + const char *extname; + const char *oid; + + extname = PQgetvalue(res, i, 0); + oid = PQgetvalue(res, i, 1); + + if (!listOneExtensionContents(extname, oid)) + { + PQclear(res); + return false; + } + if (cancel_pressed) + { + PQclear(res); + return false; + } + } + + PQclear(res); + return true; +} + +static bool +listOneExtensionContents(const char *extname, const char *oid) +{ + PQExpBufferData buf; + PGresult *res; + char title[1024]; + printQueryOpt myopt = pset.popt; + + initPQExpBuffer(&buf); + printfPQExpBuffer(&buf, + "SELECT pg_catalog.pg_describe_object(classid, objid, 0) AS \"%s\"\n" + "FROM pg_catalog.pg_depend\n" + "WHERE refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass AND refobjid = '%s' AND deptype = 'e'\n" + "ORDER BY 1;", + gettext_noop("Object Description"), + oid); + + res = PSQLexec(buf.data, false); + termPQExpBuffer(&buf); + if (!res) + return false; + + myopt.nullPrint = NULL; + snprintf(title, sizeof(title), _("Objects in extension \"%s\""), extname); + myopt.title = title; + myopt.translate_header = true; + + printQuery(res, &myopt, pset.queryFout, pset.logfile); + + PQclear(res); + return true; +} + +/* * printACLColumn * * Helper function for consistently formatting ACL (privilege) columns. diff --git a/src/bin/psql/describe.h b/src/bin/psql/describe.h index 4e80bcf41f..fb86d1e487 100644 --- a/src/bin/psql/describe.h +++ b/src/bin/psql/describe.h @@ -69,6 +69,9 @@ extern bool listConversions(const char *pattern, bool showSystem); /* \dC */ extern bool listCasts(const char *pattern); +/* \dO */ +extern bool listCollations(const char *pattern, bool verbose, bool showSystem); + /* \dn */ extern bool listSchemas(const char *pattern, bool verbose, bool showSystem); @@ -87,4 +90,10 @@ extern bool listForeignTables(const char *pattern, bool verbose); /* \dL */ extern bool listLanguages(const char *pattern, bool verbose, bool showSystem); +/* \dx */ +extern bool listExtensions(const char *pattern); + +/* \dx+ */ +extern bool listExtensionContents(const char *pattern); + #endif /* DESCRIBE_H */ diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c index 2647b1081f..ac5edca65d 100644 --- a/src/bin/psql/help.c +++ b/src/bin/psql/help.c @@ -214,6 +214,7 @@ slashUsage(unsigned short int pager) fprintf(output, _(" \\dL[S+] [PATTERN] list procedural languages\n")); fprintf(output, _(" \\dn[S+] [PATTERN] list schemas\n")); fprintf(output, _(" \\do[S] [PATTERN] list operators\n")); + fprintf(output, _(" \\dO[S+] [PATTERN] list collations\n")); fprintf(output, _(" \\dp [PATTERN] list table, view, and sequence access privileges\n")); fprintf(output, _(" \\drds [PATRN1 [PATRN2]] list per-database role settings\n")); fprintf(output, _(" \\ds[S+] [PATTERN] list sequences\n")); @@ -222,6 +223,7 @@ slashUsage(unsigned short int pager) fprintf(output, _(" \\du[+] [PATTERN] list roles\n")); fprintf(output, _(" \\dv[S+] [PATTERN] list views\n")); fprintf(output, _(" \\dE[S+] [PATTERN] list foreign tables\n")); + fprintf(output, _(" \\dx[+] [PATTERN] list extensions\n")); fprintf(output, _(" \\l[+] list all databases\n")); fprintf(output, _(" \\sf[+] FUNCNAME show a function's definition\n")); fprintf(output, _(" \\z [PATTERN] same as \\dp\n")); diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c index 4f3815a28a..10713e9b15 100644 --- a/src/bin/psql/startup.c +++ b/src/bin/psql/startup.c @@ -224,8 +224,12 @@ main(int argc, char *argv[]) if (options.action == ACT_LIST_DB) { - int success = listAllDbs(false); + int success; + if (!options.no_psqlrc) + process_psqlrc(argv[0]); + + success = listAllDbs(false); PQfinish(pset.db); exit(success ? EXIT_SUCCESS : EXIT_FAILURE); } diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 84c68a7bff..70aa3fa81e 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -578,6 +578,16 @@ static const SchemaQuery Query_for_list_of_views = { " FROM pg_catalog.pg_proc "\ " WHERE proname='%s'" +#define Query_for_list_of_extensions \ +" SELECT pg_catalog.quote_ident(extname) "\ +" FROM pg_catalog.pg_extension "\ +" WHERE substring(pg_catalog.quote_ident(extname),1,%d)='%s'" + +#define Query_for_list_of_available_extensions \ +" SELECT pg_catalog.quote_ident(name) "\ +" FROM pg_catalog.pg_available_extensions "\ +" WHERE substring(pg_catalog.quote_ident(name),1,%d)='%s' AND installed_version IS NULL" + /* * This is a list of all "things" in Pgsql, which can show up after CREATE or * DROP; and there is also a query to get a list of them. @@ -596,6 +606,7 @@ static const pgsql_thing_t words_after_create[] = { {"AGGREGATE", NULL, &Query_for_list_of_aggregates}, {"CAST", NULL, NULL}, /* Casts have complex structures for names, so * skip it */ + {"COLLATION", "SELECT pg_catalog.quote_ident(collname) FROM pg_catalog.pg_collation WHERE collencoding = pg_char_to_encoding(getdatabaseencoding()) AND substring(pg_catalog.quote_ident(collname),1,%d)='%s'"}, /* * CREATE CONSTRAINT TRIGGER is not supported here because it is designed @@ -606,6 +617,7 @@ static const pgsql_thing_t words_after_create[] = { {"DATABASE", Query_for_list_of_databases}, {"DICTIONARY", Query_for_list_of_ts_dictionaries, NULL, true}, {"DOMAIN", NULL, &Query_for_list_of_domains}, + {"EXTENSION", Query_for_list_of_extensions}, {"FOREIGN DATA WRAPPER", NULL, NULL}, {"FOREIGN TABLE", NULL, NULL}, {"FUNCTION", NULL, &Query_for_list_of_functions}, @@ -750,6 +762,17 @@ psql_completion(char *text, int start, int end) if (text[0] == '\\') COMPLETE_WITH_LIST(backslash_commands); + /* Variable interpolation */ + else if (text[0] == ':' && text[1] != ':') + { + if (text[1] == '\'') + matches = complete_from_variables(text, ":'", "'"); + else if (text[1] == '"') + matches = complete_from_variables(text, ":\"", "\""); + else + matches = complete_from_variables(text, ":", ""); + } + /* If no previous word, suggest one of the basic sql commands */ else if (!prev_wd) COMPLETE_WITH_LIST(sql_commands); @@ -775,9 +798,12 @@ psql_completion(char *text, int start, int end) pg_strcasecmp(prev3_wd, "TABLE") != 0) { static const char *const list_ALTER[] = - {"AGGREGATE", "CONVERSION", "DATABASE", "DEFAULT PRIVILEGES", "DOMAIN", "FOREIGN DATA WRAPPER", "FOREIGN TABLE", "FUNCTION", - "GROUP", "INDEX", "LANGUAGE", "LARGE OBJECT", "OPERATOR", "ROLE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE", - "TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE", "USER", "USER MAPPING FOR", "VIEW", NULL}; + {"AGGREGATE", "COLLATION", "CONVERSION", "DATABASE", "DEFAULT PRIVILEGES", "DOMAIN", + "EXTENSION", "FOREIGN DATA WRAPPER", "FOREIGN TABLE", "FUNCTION", + "GROUP", "INDEX", "LANGUAGE", "LARGE OBJECT", "OPERATOR", + "ROLE", "SCHEMA", "SERVER", "SEQUENCE", "TABLE", + "TABLESPACE", "TEXT SEARCH", "TRIGGER", "TYPE", + "USER", "USER MAPPING FOR", "VIEW", NULL}; COMPLETE_WITH_LIST(list_ALTER); } @@ -818,6 +844,16 @@ psql_completion(char *text, int start, int end) COMPLETE_WITH_LIST(list_ALTERGEN); } + /* ALTER COLLATION <name> */ + else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && + pg_strcasecmp(prev2_wd, "COLLATION") == 0) + { + static const char *const list_ALTERGEN[] = + {"OWNER TO", "RENAME TO", "SET SCHEMA", NULL}; + + COMPLETE_WITH_LIST(list_ALTERGEN); + } + /* ALTER CONVERSION <name> */ else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && pg_strcasecmp(prev2_wd, "CONVERSION") == 0) @@ -838,6 +874,16 @@ psql_completion(char *text, int start, int end) COMPLETE_WITH_LIST(list_ALTERDATABASE); } + /* ALTER EXTENSION <name> */ + else if (pg_strcasecmp(prev3_wd, "ALTER") == 0 && + pg_strcasecmp(prev2_wd, "EXTENSION") == 0) + { + static const char *const list_ALTEREXTENSION[] = + {"ADD", "DROP", "UPDATE", "SET SCHEMA", NULL}; + + COMPLETE_WITH_LIST(list_ALTEREXTENSION); + } + /* ALTER FOREIGN */ else if (pg_strcasecmp(prev2_wd, "ALTER") == 0 && pg_strcasecmp(prev_wd, "FOREIGN") == 0) @@ -1486,7 +1532,7 @@ psql_completion(char *text, int start, int end) pg_strcasecmp(prev_wd, "ON") == 0) { static const char *const list_COMMENT[] = - {"CAST", "CONVERSION", "DATABASE", "FOREIGN TABLE", "INDEX", "LANGUAGE", "RULE", "SCHEMA", + {"CAST", "COLLATION", "CONVERSION", "DATABASE", "FOREIGN TABLE", "INDEX", "LANGUAGE", "RULE", "SCHEMA", "SEQUENCE", "TABLE", "TYPE", "VIEW", "COLUMN", "AGGREGATE", "FUNCTION", "OPERATOR", "TRIGGER", "CONSTRAINT", "DOMAIN", "LARGE OBJECT", "TABLESPACE", "TEXT SEARCH", "ROLE", NULL}; @@ -1579,6 +1625,16 @@ psql_completion(char *text, int start, int end) pg_strcasecmp(prev_wd, "TEMPLATE") == 0) COMPLETE_WITH_QUERY(Query_for_list_of_template_databases); + /* CREATE EXTENSION */ + /* Complete with available extensions rather than installed ones. */ + else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 && + pg_strcasecmp(prev_wd, "EXTENSION") == 0) + COMPLETE_WITH_QUERY(Query_for_list_of_available_extensions); + /* CREATE EXTENSION <name> */ + else if (pg_strcasecmp(prev3_wd, "CREATE") == 0 && + pg_strcasecmp(prev2_wd, "EXTENSION") == 0) + COMPLETE_WITH_CONST("WITH SCHEMA"); + /* CREATE FOREIGN */ else if (pg_strcasecmp(prev2_wd, "CREATE") == 0 && pg_strcasecmp(prev_wd, "FOREIGN") == 0) @@ -1920,8 +1976,10 @@ psql_completion(char *text, int start, int end) /* DROP object with CASCADE / RESTRICT */ else if ((pg_strcasecmp(prev3_wd, "DROP") == 0 && - (pg_strcasecmp(prev2_wd, "CONVERSION") == 0 || + (pg_strcasecmp(prev2_wd, "COLLATION") == 0 || + pg_strcasecmp(prev2_wd, "CONVERSION") == 0 || pg_strcasecmp(prev2_wd, "DOMAIN") == 0 || + pg_strcasecmp(prev2_wd, "EXTENSION") == 0 || pg_strcasecmp(prev2_wd, "FUNCTION") == 0 || pg_strcasecmp(prev2_wd, "INDEX") == 0 || pg_strcasecmp(prev2_wd, "LANGUAGE") == 0 || @@ -2737,17 +2795,6 @@ psql_completion(char *text, int start, int end) ) matches = completion_matches(text, filename_completion_function); -/* Variable interpolation */ - else if (text[0] == ':' && text[1] != ':') - { - if (text[1] == '\'') - matches = complete_from_variables(text, ":'", "'"); - else if (text[1] == '"') - matches = complete_from_variables(text, ":\"", "\""); - else - matches = complete_from_variables(text, ":", ""); - } - /* * Finally, we look through the list of "things", such as TABLE, INDEX and * check if that was the previous word. If so, execute the query to get a diff --git a/src/include/access/skey.h b/src/include/access/skey.h index 5a498956c9..3d2956c935 100644 --- a/src/include/access/skey.h +++ b/src/include/access/skey.h @@ -152,5 +152,7 @@ extern void ScanKeyEntryInitializeWithInfo(ScanKey entry, Oid subtype, FmgrInfo *finfo, Datum argument); +extern void ScanKeyEntryInitializeCollation(ScanKey entry, + Oid collation); #endif /* SKEY_H */ diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h index 3b0710e2dc..fb8ee0ad4f 100644 --- a/src/include/access/tupdesc.h +++ b/src/include/access/tupdesc.h @@ -114,8 +114,12 @@ extern void TupleDescInitEntry(TupleDesc desc, int32 typmod, int attdim); +extern void TupleDescInitEntryCollation(TupleDesc desc, + AttrNumber attributeNumber, + Oid collationid); + extern TupleDesc BuildDescForRelation(List *schema); -extern TupleDesc BuildDescFromLists(List *names, List *types, List *typmods); +extern TupleDesc BuildDescFromLists(List *names, List *types, List *typmods, List *collations); #endif /* TUPDESC_H */ diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 122e96b5d1..7e9bad6e3a 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -184,7 +184,8 @@ typedef enum { RECOVERY_TARGET_UNSET, RECOVERY_TARGET_XID, - RECOVERY_TARGET_TIME + RECOVERY_TARGET_TIME, + RECOVERY_TARGET_NAME } RecoveryTargetType; extern XLogRecPtr XactLastRecEnd; @@ -288,8 +289,10 @@ extern void xlog_desc(StringInfo buf, uint8 xl_info, char *rec); extern void issue_xlog_fsync(int fd, uint32 log, uint32 seg); extern bool RecoveryInProgress(void); +extern bool HotStandbyActive(void); extern bool XLogInsertAllowed(void); extern void GetXLogReceiptTime(TimestampTz *rtime, bool *fromStream); +extern XLogRecPtr GetXLogReplayRecPtr(void); extern void UpdateControlFile(void); extern uint64 GetSystemIdentifier(void); @@ -302,6 +305,7 @@ extern void InitXLOGAccess(void); extern void CreateCheckPoint(int flags); extern bool CreateRestartPoint(int flags); extern void XLogPutNextOid(Oid nextOid); +extern XLogRecPtr XLogRestorePoint(const char *rpName); extern XLogRecPtr GetRedoRecPtr(void); extern XLogRecPtr GetInsertRecPtr(void); extern XLogRecPtr GetFlushRecPtr(void); @@ -310,13 +314,14 @@ extern TimeLineID GetRecoveryTargetTLI(void); extern void HandleStartupProcInterrupts(void); extern void StartupProcessMain(void); +extern bool CheckPromoteSignal(void); extern void WakeupRecovery(void); /* * Starting/stopping a base backup */ extern XLogRecPtr do_pg_start_backup(const char *backupidstr, bool fast, char **labelfile); -extern XLogRecPtr do_pg_stop_backup(char *labelfile); +extern XLogRecPtr do_pg_stop_backup(char *labelfile, bool waitforarchive); extern void do_pg_abort_backup(void); /* File path names (all relative to $PGDATA) */ diff --git a/src/include/access/xlog_internal.h b/src/include/access/xlog_internal.h index ba640359cc..eeccdce31d 100644 --- a/src/include/access/xlog_internal.h +++ b/src/include/access/xlog_internal.h @@ -267,6 +267,7 @@ extern XLogRecPtr RequestXLogSwitch(void); extern Datum pg_start_backup(PG_FUNCTION_ARGS); extern Datum pg_stop_backup(PG_FUNCTION_ARGS); extern Datum pg_switch_xlog(PG_FUNCTION_ARGS); +extern Datum pg_create_restore_point(PG_FUNCTION_ARGS); extern Datum pg_current_xlog_location(PG_FUNCTION_ARGS); extern Datum pg_current_xlog_insert_location(PG_FUNCTION_ARGS); extern Datum pg_last_xlog_receive_location(PG_FUNCTION_ARGS); @@ -275,5 +276,8 @@ extern Datum pg_last_xact_replay_timestamp(PG_FUNCTION_ARGS); extern Datum pg_xlogfile_name_offset(PG_FUNCTION_ARGS); extern Datum pg_xlogfile_name(PG_FUNCTION_ARGS); extern Datum pg_is_in_recovery(PG_FUNCTION_ARGS); +extern Datum pg_xlog_replay_pause(PG_FUNCTION_ARGS); +extern Datum pg_xlog_replay_resume(PG_FUNCTION_ARGS); +extern Datum pg_is_xlog_replay_paused(PG_FUNCTION_ARGS); #endif /* XLOG_INTERNAL_H */ diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 799fd5019e..9d702674b8 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 201102081 +#define CATALOG_VERSION_NO 201102161 #endif diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h index c6ab313edf..582294c6b3 100644 --- a/src/include/catalog/dependency.h +++ b/src/include/catalog/dependency.h @@ -50,6 +50,12 @@ * Example: a trigger that's created to enforce a foreign-key constraint * is made internally dependent on the constraint's pg_constraint entry. * + * DEPENDENCY_EXTENSION ('e'): the dependent object is a member of the + * extension that is the referenced object. The dependent object can be + * dropped only via DROP EXTENSION on the referenced object. Functionally + * this dependency type acts the same as an internal dependency, but it's + * kept separate for clarity and to simplify pg_dump. + * * DEPENDENCY_PIN ('p'): there is no dependent object; this type of entry * is a signal that the system itself depends on the referenced object, * and so that object must never be deleted. Entries of this type are @@ -64,6 +70,7 @@ typedef enum DependencyType DEPENDENCY_NORMAL = 'n', DEPENDENCY_AUTO = 'a', DEPENDENCY_INTERNAL = 'i', + DEPENDENCY_EXTENSION = 'e', DEPENDENCY_PIN = 'p' } DependencyType; @@ -114,6 +121,7 @@ typedef enum ObjectClass OCLASS_PROC, /* pg_proc */ OCLASS_TYPE, /* pg_type */ OCLASS_CAST, /* pg_cast */ + OCLASS_COLLATION, /* pg_collation */ OCLASS_CONSTRAINT, /* pg_constraint */ OCLASS_CONVERSION, /* pg_conversion */ OCLASS_DEFAULT, /* pg_attrdef */ @@ -137,8 +145,8 @@ typedef enum ObjectClass OCLASS_FDW, /* pg_foreign_data_wrapper */ OCLASS_FOREIGN_SERVER, /* pg_foreign_server */ OCLASS_USER_MAPPING, /* pg_user_mapping */ - OCLASS_FOREIGN_TABLE, /* pg_foreign_table */ OCLASS_DEFACL, /* pg_default_acl */ + OCLASS_EXTENSION, /* pg_extension */ MAX_OCLASS /* MUST BE LAST */ } ObjectClass; @@ -193,12 +201,20 @@ extern void recordMultipleDependencies(const ObjectAddress *depender, int nreferenced, DependencyType behavior); -extern long deleteDependencyRecordsFor(Oid classId, Oid objectId); +extern void recordDependencyOnCurrentExtension(const ObjectAddress *object); + +extern long deleteDependencyRecordsFor(Oid classId, Oid objectId, + bool skipExtensionDeps); + +extern long deleteDependencyRecordsForClass(Oid classId, Oid objectId, + Oid refclassId, char deptype); extern long changeDependencyFor(Oid classId, Oid objectId, Oid refClassId, Oid oldRefObjectId, Oid newRefObjectId); +extern Oid getExtensionOfObject(Oid classId, Oid objectId); + extern bool sequenceIsOwned(Oid seqId, Oid *tableId, int32 *colId); extern void markSequenceUnowned(Oid seqId); diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h index 60387cca01..2ce6806e50 100644 --- a/src/include/catalog/index.h +++ b/src/include/catalog/index.h @@ -39,6 +39,7 @@ extern Oid index_create(Relation heapRelation, List *indexColNames, Oid accessMethodObjectId, Oid tableSpaceId, + Oid *collationObjectId, Oid *classObjectId, int16 *coloptions, Datum reloptions, diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h index a3fb916903..4118e64542 100644 --- a/src/include/catalog/indexing.h +++ b/src/include/catalog/indexing.h @@ -107,6 +107,11 @@ DECLARE_UNIQUE_INDEX(pg_class_oid_index, 2662, on pg_class using btree(oid oid_o DECLARE_UNIQUE_INDEX(pg_class_relname_nsp_index, 2663, on pg_class using btree(relname name_ops, relnamespace oid_ops)); #define ClassNameNspIndexId 2663 +DECLARE_UNIQUE_INDEX(pg_collation_name_enc_nsp_index, 3164, on pg_collation using btree(collname name_ops, collencoding int4_ops, collnamespace oid_ops)); +#define CollationNameEncNspIndexId 3164 +DECLARE_UNIQUE_INDEX(pg_collation_oid_index, 3085, on pg_collation using btree(oid oid_ops)); +#define CollationOidIndexId 3085 + /* This following index is not used for a cache and is not unique */ DECLARE_INDEX(pg_constraint_conname_nsp_index, 2664, on pg_constraint using btree(conname name_ops, connamespace oid_ops)); #define ConstraintNameNspIndexId 2664 @@ -289,6 +294,12 @@ DECLARE_UNIQUE_INDEX(pg_db_role_setting_databaseid_rol_index, 2965, on pg_db_rol DECLARE_UNIQUE_INDEX(pg_seclabel_object_index, 3597, on pg_seclabel using btree(objoid oid_ops, classoid oid_ops, objsubid int4_ops, provider text_ops)); #define SecLabelObjectIndexId 3597 +DECLARE_UNIQUE_INDEX(pg_extension_oid_index, 3080, on pg_extension using btree(oid oid_ops)); +#define ExtensionOidIndexId 3080 + +DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3081, on pg_extension using btree(extname name_ops)); +#define ExtensionNameIndexId 3081 + /* last step of initialization script: build the indexes declared above */ BUILD_INDICES diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h index d2381fbadb..e72d82eb16 100644 --- a/src/include/catalog/namespace.h +++ b/src/include/catalog/namespace.h @@ -71,6 +71,9 @@ extern bool OpclassIsVisible(Oid opcid); extern Oid OpfamilynameGetOpfid(Oid amid, const char *opfname); extern bool OpfamilyIsVisible(Oid opfid); +extern Oid CollationGetCollid(const char *collname); +extern bool CollationIsVisible(Oid collid); + extern Oid ConversionGetConid(const char *conname); extern bool ConversionIsVisible(Oid conid); @@ -114,6 +117,7 @@ extern OverrideSearchPath *GetOverrideSearchPath(MemoryContext context); extern void PushOverrideSearchPath(OverrideSearchPath *newpath); extern void PopOverrideSearchPath(void); +extern Oid get_collation_oid(List *collname, bool missing_ok); extern Oid get_conversion_oid(List *conname, bool missing_ok); extern Oid FindDefaultConversionProc(int4 for_encoding, int4 to_encoding); diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h index 1845c8d37c..409d6ea3e7 100644 --- a/src/include/catalog/pg_attribute.h +++ b/src/include/catalog/pg_attribute.h @@ -142,6 +142,9 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK /* Number of times inherited from direct parent relation(s) */ int4 attinhcount; + /* attribute's collation */ + Oid attcollation; + /* * VARIABLE LENGTH FIELDS start here. These fields may be NULL, too. * @@ -159,10 +162,10 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK * ATTRIBUTE_FIXED_PART_SIZE is the size of the fixed-layout, * guaranteed-not-null part of a pg_attribute row. This is in fact as much * of the row as gets copied into tuple descriptors, so don't expect you - * can access fields beyond attinhcount except in a real tuple! + * can access fields beyond attcollation except in a real tuple! */ #define ATTRIBUTE_FIXED_PART_SIZE \ - (offsetof(FormData_pg_attribute,attinhcount) + sizeof(int4)) + (offsetof(FormData_pg_attribute,attcollation) + sizeof(Oid)) /* ---------------- * Form_pg_attribute corresponds to a pointer to a tuple with @@ -176,7 +179,7 @@ typedef FormData_pg_attribute *Form_pg_attribute; * ---------------- */ -#define Natts_pg_attribute 19 +#define Natts_pg_attribute 20 #define Anum_pg_attribute_attrelid 1 #define Anum_pg_attribute_attname 2 #define Anum_pg_attribute_atttypid 3 @@ -194,8 +197,9 @@ typedef FormData_pg_attribute *Form_pg_attribute; #define Anum_pg_attribute_attisdropped 15 #define Anum_pg_attribute_attislocal 16 #define Anum_pg_attribute_attinhcount 17 -#define Anum_pg_attribute_attacl 18 -#define Anum_pg_attribute_attoptions 19 +#define Anum_pg_attribute_attcollation 18 +#define Anum_pg_attribute_attacl 19 +#define Anum_pg_attribute_attoptions 20 /* ---------------- diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h index 33d34d55a4..42bc8635a2 100644 --- a/src/include/catalog/pg_class.h +++ b/src/include/catalog/pg_class.h @@ -130,9 +130,9 @@ typedef FormData_pg_class *Form_pg_class; */ /* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */ -DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 28 0 t f f f f 3 _null_ _null_ )); +DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 29 0 t f f f f 3 _null_ _null_ )); DESCR(""); -DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 19 0 f f f f f 3 _null_ _null_ )); +DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 20 0 f f f f f 3 _null_ _null_ )); DESCR(""); DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f p r 25 0 t f f f f 3 _null_ _null_ )); DESCR(""); diff --git a/src/include/catalog/pg_collation.h b/src/include/catalog/pg_collation.h new file mode 100644 index 0000000000..42a70e8f25 --- /dev/null +++ b/src/include/catalog/pg_collation.h @@ -0,0 +1,64 @@ +/*------------------------------------------------------------------------- + * + * pg_collation.h + * definition of the system "collation" relation (pg_collation) + * along with the relation's initial contents. + * + * + * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * $PostgreSQL$ + * + * NOTES + * the genbki.pl script reads this file and generates .bki + * information from the DATA() statements. + * + *------------------------------------------------------------------------- + */ +#ifndef PG_COLLATION_H +#define PG_COLLATION_H + +#include "catalog/genbki.h" + +/* ---------------- + * pg_collation definition. cpp turns this into + * typedef struct FormData_pg_collation + * ---------------- + */ +#define CollationRelationId 3456 + +CATALOG(pg_collation,3456) +{ + NameData collname; /* collation name */ + Oid collnamespace; /* OID of namespace containing this collation */ + Oid collowner; + int4 collencoding; /* encoding that this collation applies to */ + NameData collcollate; /* LC_COLLATE setting */ + NameData collctype; /* LC_CTYPE setting */ +} FormData_pg_collation; + +/* ---------------- + * Form_pg_collation corresponds to a pointer to a row with + * the format of pg_collation relation. + * ---------------- + */ +typedef FormData_pg_collation *Form_pg_collation; + +/* ---------------- + * compiler constants for pg_collation + * ---------------- + */ +#define Natts_pg_collation 6 +#define Anum_pg_collation_collname 1 +#define Anum_pg_collation_collnamespace 2 +#define Anum_pg_collation_collowner 3 +#define Anum_pg_collation_collencoding 4 +#define Anum_pg_collation_collcollate 5 +#define Anum_pg_collation_collctype 6 + +DATA(insert OID = 100 ( default PGNSP PGUID 0 "" "" )); +DESCR("placeholder for default collation"); +#define DEFAULT_COLLATION_OID 100 + +#endif /* PG_COLLATION_H */ diff --git a/src/include/catalog/pg_collation_fn.h b/src/include/catalog/pg_collation_fn.h new file mode 100644 index 0000000000..63a9cf2d63 --- /dev/null +++ b/src/include/catalog/pg_collation_fn.h @@ -0,0 +1,23 @@ +/*------------------------------------------------------------------------- + * + * pg_collation_fn.h + * prototypes for functions in catalog/pg_collation.c + * + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/catalog/pg_collation_fn.h + * + *------------------------------------------------------------------------- + */ +#ifndef PG_COLLATION_FN_H +#define PG_COLLATION_FN_H + +extern Oid CollationCreate(const char *collname, Oid collnamespace, + Oid collowner, + int32 collencoding, + const char *collcollate, const char *collctype); +extern void RemoveCollationById(Oid collationOid); + +#endif /* PG_COLLATION_FN_H */ diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h index 26dbcace4f..a7967ab5c6 100644 --- a/src/include/catalog/pg_constraint.h +++ b/src/include/catalog/pg_constraint.h @@ -45,6 +45,7 @@ CATALOG(pg_constraint,2606) char contype; /* constraint type; see codes below */ bool condeferrable; /* deferrable constraint? */ bool condeferred; /* deferred by default? */ + bool convalidated; /* constraint has been validated? */ /* * conrelid and conkey are only meaningful if the constraint applies to a @@ -148,29 +149,30 @@ typedef FormData_pg_constraint *Form_pg_constraint; * compiler constants for pg_constraint * ---------------- */ -#define Natts_pg_constraint 22 +#define Natts_pg_constraint 23 #define Anum_pg_constraint_conname 1 #define Anum_pg_constraint_connamespace 2 #define Anum_pg_constraint_contype 3 #define Anum_pg_constraint_condeferrable 4 #define Anum_pg_constraint_condeferred 5 -#define Anum_pg_constraint_conrelid 6 -#define Anum_pg_constraint_contypid 7 -#define Anum_pg_constraint_conindid 8 -#define Anum_pg_constraint_confrelid 9 -#define Anum_pg_constraint_confupdtype 10 -#define Anum_pg_constraint_confdeltype 11 -#define Anum_pg_constraint_confmatchtype 12 -#define Anum_pg_constraint_conislocal 13 -#define Anum_pg_constraint_coninhcount 14 -#define Anum_pg_constraint_conkey 15 -#define Anum_pg_constraint_confkey 16 -#define Anum_pg_constraint_conpfeqop 17 -#define Anum_pg_constraint_conppeqop 18 -#define Anum_pg_constraint_conffeqop 19 -#define Anum_pg_constraint_conexclop 20 -#define Anum_pg_constraint_conbin 21 -#define Anum_pg_constraint_consrc 22 +#define Anum_pg_constraint_convalidated 6 +#define Anum_pg_constraint_conrelid 7 +#define Anum_pg_constraint_contypid 8 +#define Anum_pg_constraint_conindid 9 +#define Anum_pg_constraint_confrelid 10 +#define Anum_pg_constraint_confupdtype 11 +#define Anum_pg_constraint_confdeltype 12 +#define Anum_pg_constraint_confmatchtype 13 +#define Anum_pg_constraint_conislocal 14 +#define Anum_pg_constraint_coninhcount 15 +#define Anum_pg_constraint_conkey 16 +#define Anum_pg_constraint_confkey 17 +#define Anum_pg_constraint_conpfeqop 18 +#define Anum_pg_constraint_conppeqop 19 +#define Anum_pg_constraint_conffeqop 20 +#define Anum_pg_constraint_conexclop 21 +#define Anum_pg_constraint_conbin 22 +#define Anum_pg_constraint_consrc 23 /* Valid values for contype */ @@ -205,6 +207,7 @@ extern Oid CreateConstraintEntry(const char *constraintName, char constraintType, bool isDeferrable, bool isDeferred, + bool isValidated, Oid relId, const int16 *constraintKey, int constraintNKeys, @@ -228,6 +231,7 @@ extern Oid CreateConstraintEntry(const char *constraintName, extern void RemoveConstraintById(Oid conId); extern void RenameConstraintById(Oid conId, const char *newname); +extern void SetValidatedConstraintById(Oid conId); extern bool ConstraintNameIsUsed(ConstraintCategory conCat, Oid objId, Oid objNamespace, const char *conname); diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h index ddf139857d..fb458acf83 100644 --- a/src/include/catalog/pg_control.h +++ b/src/include/catalog/pg_control.h @@ -59,6 +59,7 @@ typedef struct CheckPoint #define XLOG_SWITCH 0x40 #define XLOG_BACKUP_END 0x50 #define XLOG_PARAMETER_CHANGE 0x60 +#define XLOG_RESTORE_POINT 0x70 /* diff --git a/src/include/catalog/pg_extension.h b/src/include/catalog/pg_extension.h new file mode 100644 index 0000000000..63a1a0711f --- /dev/null +++ b/src/include/catalog/pg_extension.h @@ -0,0 +1,74 @@ +/*------------------------------------------------------------------------- + * + * pg_extension.h + * definition of the system "extension" relation (pg_extension) + * along with the relation's initial contents. + * + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/catalog/pg_extension.h + * + * NOTES + * the genbki.pl script reads this file and generates .bki + * information from the DATA() statements. + * + *------------------------------------------------------------------------- + */ +#ifndef PG_EXTENSION_H +#define PG_EXTENSION_H + +#include "catalog/genbki.h" + +/* ---------------- + * pg_extension definition. cpp turns this into + * typedef struct FormData_pg_extension + * ---------------- + */ +#define ExtensionRelationId 3079 + +CATALOG(pg_extension,3079) +{ + NameData extname; /* extension name */ + Oid extowner; /* extension owner */ + Oid extnamespace; /* namespace of contained objects */ + bool extrelocatable; /* if true, allow ALTER EXTENSION SET SCHEMA */ + + /* + * VARIABLE LENGTH FIELDS start here. + * + * extversion should never be null, but the others can be. + */ + text extversion; /* extension version name */ + Oid extconfig[1]; /* dumpable configuration tables */ + text extcondition[1]; /* WHERE clauses for config tables */ +} FormData_pg_extension; + +/* ---------------- + * Form_pg_extension corresponds to a pointer to a tuple with + * the format of pg_extension relation. + * ---------------- + */ +typedef FormData_pg_extension *Form_pg_extension; + +/* ---------------- + * compiler constants for pg_extension + * ---------------- + */ + +#define Natts_pg_extension 7 +#define Anum_pg_extension_extname 1 +#define Anum_pg_extension_extowner 2 +#define Anum_pg_extension_extnamespace 3 +#define Anum_pg_extension_extrelocatable 4 +#define Anum_pg_extension_extversion 5 +#define Anum_pg_extension_extconfig 6 +#define Anum_pg_extension_extcondition 7 + +/* ---------------- + * pg_extension has no initial contents + * ---------------- + */ + +#endif /* PG_EXTENSION_H */ diff --git a/src/include/catalog/pg_index.h b/src/include/catalog/pg_index.h index 0dcae6963c..d8142e12bc 100644 --- a/src/include/catalog/pg_index.h +++ b/src/include/catalog/pg_index.h @@ -44,6 +44,7 @@ CATALOG(pg_index,2610) BKI_WITHOUT_OIDS BKI_SCHEMA_MACRO /* VARIABLE LENGTH FIELDS: */ int2vector indkey; /* column numbers of indexed cols, or 0 */ + oidvector indcollation; /* collation identifiers */ oidvector indclass; /* opclass identifiers */ int2vector indoption; /* per-column flags (AM-specific meanings) */ pg_node_tree indexprs; /* expression trees for index attributes that @@ -64,7 +65,7 @@ typedef FormData_pg_index *Form_pg_index; * compiler constants for pg_index * ---------------- */ -#define Natts_pg_index 16 +#define Natts_pg_index 17 #define Anum_pg_index_indexrelid 1 #define Anum_pg_index_indrelid 2 #define Anum_pg_index_indnatts 3 @@ -77,10 +78,11 @@ typedef FormData_pg_index *Form_pg_index; #define Anum_pg_index_indcheckxmin 10 #define Anum_pg_index_indisready 11 #define Anum_pg_index_indkey 12 -#define Anum_pg_index_indclass 13 -#define Anum_pg_index_indoption 14 -#define Anum_pg_index_indexprs 15 -#define Anum_pg_index_indpred 16 +#define Anum_pg_index_indcollation 13 +#define Anum_pg_index_indclass 14 +#define Anum_pg_index_indoption 15 +#define Anum_pg_index_indexprs 16 +#define Anum_pg_index_indpred 17 /* * Index AMs that support ordered scans must support these two indoption diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 05fc57c633..7df3471366 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -1386,8 +1386,8 @@ DATA(insert OID = 1078 ( bpcharcmp PGNSP PGUID 12 1 0 0 f f f t f i 2 0 23 DESCR("less-equal-greater"); DATA(insert OID = 1080 ( hashbpchar PGNSP PGUID 12 1 0 0 f f f t f i 1 0 23 "1042" _null_ _null_ _null_ _null_ hashbpchar _null_ _null_ _null_ )); DESCR("hash"); -DATA(insert OID = 1081 ( format_type PGNSP PGUID 12 1 0 0 f f f f f s 2 0 25 "26 23" _null_ _null_ _null_ _null_ format_type _null_ _null_ _null_ )); -DESCR("format a type oid and atttypmod to canonical SQL"); +DATA(insert OID = 1081 ( format_type PGNSP PGUID 12 1 0 0 f f f f f s 3 0 25 "26 23 26" _null_ _null_ _null_ _null_ format_type _null_ _null_ _null_ )); +DESCR("format a type OID, atttypmod, and collation OID to canonical SQL"); DATA(insert OID = 1084 ( date_in PGNSP PGUID 12 1 0 0 f f f t f s 1 0 1082 "2275" _null_ _null_ _null_ _null_ date_in _null_ _null_ _null_ )); DESCR("I/O"); DATA(insert OID = 1085 ( date_out PGNSP PGUID 12 1 0 0 f f f t f s 1 0 2275 "1082" _null_ _null_ _null_ _null_ date_out _null_ _null_ _null_ )); @@ -3075,7 +3075,7 @@ DATA(insert OID = 1936 ( pg_stat_get_backend_idset PGNSP PGUID 12 1 100 0 f f DESCR("statistics: currently active backend IDs"); DATA(insert OID = 2022 ( pg_stat_get_activity PGNSP PGUID 12 1 100 0 f f f f t s 1 0 2249 "23" "{23,26,23,26,25,25,16,1184,1184,1184,869,23}" "{i,o,o,o,o,o,o,o,o,o,o,o}" "{pid,datid,procpid,usesysid,application_name,current_query,waiting,xact_start,query_start,backend_start,client_addr,client_port}" _null_ pg_stat_get_activity _null_ _null_ _null_ )); DESCR("statistics: information about currently active backends"); -DATA(insert OID = 3099 ( pg_stat_get_wal_senders PGNSP PGUID 12 1 10 0 f f f f t s 0 0 2249 "" "{23,25,25}" "{o,o,o}" "{procpid,state,sent_location}" _null_ pg_stat_get_wal_senders _null_ _null_ _null_ )); +DATA(insert OID = 3099 ( pg_stat_get_wal_senders PGNSP PGUID 12 1 10 0 f f f f t s 0 0 2249 "" "{23,25,25,25,25,25}" "{o,o,o,o,o,o}" "{procpid,state,sent_location,write_location,flush_location,apply_location}" _null_ pg_stat_get_wal_senders _null_ _null_ _null_ )); DESCR("statistics: information about currently active replication"); DATA(insert OID = 2026 ( pg_backend_pid PGNSP PGUID 12 1 0 0 f f f t f s 0 0 23 "" _null_ _null_ _null_ _null_ pg_backend_pid _null_ _null_ _null_ )); DESCR("statistics: current backend PID"); @@ -3131,6 +3131,8 @@ DATA(insert OID = 3069 ( pg_stat_get_db_conflict_startup_deadlock PGNSP PGUID 1 DESCR("statistics: recovery conflicts in database caused by buffer deadlock"); DATA(insert OID = 3070 ( pg_stat_get_db_conflict_all PGNSP PGUID 12 1 0 0 f f f t f s 1 0 20 "26" _null_ _null_ _null_ _null_ pg_stat_get_db_conflict_all _null_ _null_ _null_ )); DESCR("statistics: recovery conflicts in database"); +DATA(insert OID = 3074 ( pg_stat_get_db_stat_reset_time PGNSP PGUID 12 1 0 0 f f f t f s 1 0 1184 "26" _null_ _null_ _null_ _null_ pg_stat_get_db_stat_reset_time _null_ _null_ _null_ )); +DESCR("statistics: last reset for a database"); DATA(insert OID = 2769 ( pg_stat_get_bgwriter_timed_checkpoints PGNSP PGUID 12 1 0 0 f f f t f s 0 0 20 "" _null_ _null_ _null_ _null_ pg_stat_get_bgwriter_timed_checkpoints _null_ _null_ _null_ )); DESCR("statistics: number of timed checkpoints started by the bgwriter"); DATA(insert OID = 2770 ( pg_stat_get_bgwriter_requested_checkpoints PGNSP PGUID 12 1 0 0 f f f t f s 0 0 20 "" _null_ _null_ _null_ _null_ pg_stat_get_bgwriter_requested_checkpoints _null_ _null_ _null_ )); @@ -3141,6 +3143,8 @@ DATA(insert OID = 2772 ( pg_stat_get_bgwriter_buf_written_clean PGNSP PGUID 12 1 DESCR("statistics: number of buffers written by the bgwriter for cleaning dirty buffers"); DATA(insert OID = 2773 ( pg_stat_get_bgwriter_maxwritten_clean PGNSP PGUID 12 1 0 0 f f f t f s 0 0 20 "" _null_ _null_ _null_ _null_ pg_stat_get_bgwriter_maxwritten_clean _null_ _null_ _null_ )); DESCR("statistics: number of times the bgwriter stopped processing when it had written too many buffers while cleaning"); +DATA(insert OID = 3075 ( pg_stat_get_bgwriter_stat_reset_time PGNSP PGUID 12 1 0 0 f f f t f s 0 0 1184 "" _null_ _null_ _null_ _null_ pg_stat_get_bgwriter_stat_reset_time _null_ _null_ _null_ )); +DESCR("statistics: last reset for the bgwriter"); DATA(insert OID = 2775 ( pg_stat_get_buf_written_backend PGNSP PGUID 12 1 0 0 f f f t f s 0 0 20 "" _null_ _null_ _null_ _null_ pg_stat_get_buf_written_backend _null_ _null_ _null_ )); DESCR("statistics: number of buffers written by backends"); DATA(insert OID = 3063 ( pg_stat_get_buf_fsync_backend PGNSP PGUID 12 1 0 0 f f f t f s 0 0 20 "" _null_ _null_ _null_ _null_ pg_stat_get_buf_fsync_backend _null_ _null_ _null_ )); @@ -3381,6 +3385,8 @@ DATA(insert OID = 3768 ( pg_ts_template_is_visible PGNSP PGUID 12 1 0 0 f f f t DESCR("is text search template visible in search path?"); DATA(insert OID = 3758 ( pg_ts_config_is_visible PGNSP PGUID 12 1 0 0 f f f t f s 1 0 16 "26" _null_ _null_ _null_ _null_ pg_ts_config_is_visible _null_ _null_ _null_ )); DESCR("is text search configuration visible in search path?"); +DATA(insert OID = 3815 ( pg_collation_is_visible PGNSP PGUID 12 1 0 0 f f f t f s 1 0 16 "26" _null_ _null_ _null_ _null_ pg_collation_is_visible _null_ _null_ _null_ )); +DESCR("is collation visible in search path?"); DATA(insert OID = 2854 ( pg_my_temp_schema PGNSP PGUID 12 1 0 0 f f f t f s 0 0 26 "" _null_ _null_ _null_ _null_ pg_my_temp_schema _null_ _null_ _null_ )); DESCR("get OID of current session's temp schema, if any"); @@ -3397,6 +3403,7 @@ DATA(insert OID = 2173 ( pg_stop_backup PGNSP PGUID 12 1 0 0 f f f t f v 0 0 2 DESCR("finish taking an online backup"); DATA(insert OID = 2848 ( pg_switch_xlog PGNSP PGUID 12 1 0 0 f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_switch_xlog _null_ _null_ _null_ )); DESCR("switch to new xlog file"); +DATA(insert OID = 3098 ( pg_create_restore_point PGNSP PGUID 12 1 0 0 f f f t f v 1 0 25 "25" _null_ _null_ _null_ _null_ pg_create_restore_point _null_ _null_ _null_ )); DATA(insert OID = 2849 ( pg_current_xlog_location PGNSP PGUID 12 1 0 0 f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_current_xlog_location _null_ _null_ _null_ )); DESCR("current xlog write location"); DATA(insert OID = 2852 ( pg_current_xlog_insert_location PGNSP PGUID 12 1 0 0 f f f t f v 0 0 25 "" _null_ _null_ _null_ _null_ pg_current_xlog_insert_location _null_ _null_ _null_ )); @@ -3416,6 +3423,13 @@ DESCR("last xlog replay location"); DATA(insert OID = 3830 ( pg_last_xact_replay_timestamp PGNSP PGUID 12 1 0 0 f f f t f v 0 0 1184 "" _null_ _null_ _null_ _null_ pg_last_xact_replay_timestamp _null_ _null_ _null_ )); DESCR("timestamp of last replay xact"); +DATA(insert OID = 3071 ( pg_xlog_replay_pause PGNSP PGUID 12 1 0 0 f f f t f v 0 0 2278 "" _null_ _null_ _null_ _null_ pg_xlog_replay_pause _null_ _null_ _null_ )); +DESCR("pauses xlog replay"); +DATA(insert OID = 3072 ( pg_xlog_replay_resume PGNSP PGUID 12 1 0 0 f f f t f v 0 0 2278 "" _null_ _null_ _null_ _null_ pg_xlog_replay_resume _null_ _null_ _null_ )); +DESCR("resumes xlog replay, if it was paused"); +DATA(insert OID = 3073 ( pg_is_xlog_replay_paused PGNSP PGUID 12 1 0 0 f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_is_xlog_replay_paused _null_ _null_ _null_ )); +DESCR("true if xlog replay is paused"); + DATA(insert OID = 2621 ( pg_reload_conf PGNSP PGUID 12 1 0 0 f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_reload_conf _null_ _null_ _null_ )); DESCR("reload configuration files"); DATA(insert OID = 2622 ( pg_rotate_logfile PGNSP PGUID 12 1 0 0 f f f t f v 0 0 16 "" _null_ _null_ _null_ _null_ pg_rotate_logfile _null_ _null_ _null_ )); @@ -4397,6 +4411,8 @@ DATA(insert OID = 2774 ( ginqueryarrayextract PGNSP PGUID 12 1 0 0 f f f t f i DESCR("GIN array support"); DATA(insert OID = 2744 ( ginarrayconsistent PGNSP PGUID 12 1 0 0 f f f t f i 8 0 16 "2281 21 2277 23 2281 2281 2281 2281" _null_ _null_ _null_ _null_ ginarrayconsistent _null_ _null_ _null_ )); DESCR("GIN array support"); +DATA(insert OID = 3076 ( ginarrayextract PGNSP PGUID 12 1 0 0 f f f t f i 2 0 2281 "2277 2281" _null_ _null_ _null_ _null_ ginarrayextract_2args _null_ _null_ _null_ )); +DESCR("GIN array support (obsolete)"); /* overlap/contains/contained */ DATA(insert OID = 2747 ( arrayoverlap PGNSP PGUID 12 1 0 0 f f f t f i 2 0 16 "2277 2277" _null_ _null_ _null_ _null_ arrayoverlap _null_ _null_ _null_ )); @@ -4656,6 +4672,12 @@ DATA(insert OID = 3724 ( gin_cmp_tslexeme PGNSP PGUID 12 1 0 0 f f f t f i 2 0 DESCR("GIN tsvector support"); DATA(insert OID = 2700 ( gin_cmp_prefix PGNSP PGUID 12 1 0 0 f f f t f i 4 0 23 "25 25 21 2281" _null_ _null_ _null_ _null_ gin_cmp_prefix _null_ _null_ _null_ )); DESCR("GIN tsvector support"); +DATA(insert OID = 3077 ( gin_extract_tsvector PGNSP PGUID 12 1 0 0 f f f t f i 2 0 2281 "3614 2281" _null_ _null_ _null_ _null_ gin_extract_tsvector_2args _null_ _null_ _null_ )); +DESCR("GIN tsvector support (obsolete)"); +DATA(insert OID = 3087 ( gin_extract_tsquery PGNSP PGUID 12 1 0 0 f f f t f i 5 0 2281 "3615 2281 21 2281 2281" _null_ _null_ _null_ _null_ gin_extract_tsquery_5args _null_ _null_ _null_ )); +DESCR("GIN tsvector support (obsolete)"); +DATA(insert OID = 3088 ( gin_tsquery_consistent PGNSP PGUID 12 1 0 0 f f f t f i 6 0 16 "2281 21 3615 23 2281 2281" _null_ _null_ _null_ _null_ gin_tsquery_consistent_6args _null_ _null_ _null_ )); +DESCR("GIN tsvector support (obsolete)"); DATA(insert OID = 3662 ( tsquery_lt PGNSP PGUID 12 1 0 0 f f f t f i 2 0 16 "3615 3615" _null_ _null_ _null_ _null_ tsquery_lt _null_ _null_ _null_ )); DESCR("less-than"); @@ -4867,6 +4889,16 @@ DESCR("record greater than or equal"); DATA(insert OID = 2987 ( btrecordcmp PGNSP PGUID 12 1 0 0 f f f t f i 2 0 23 "2249 2249" _null_ _null_ _null_ _null_ btrecordcmp _null_ _null_ _null_ )); DESCR("btree less-equal-greater"); +/* Extensions */ +DATA(insert OID = 3082 ( pg_available_extensions PGNSP PGUID 12 10 100 0 f f f t t s 0 0 2249 "" "{19,25,25}" "{o,o,o}" "{name,default_version,comment}" _null_ pg_available_extensions _null_ _null_ _null_ )); +DESCR("list available extensions"); +DATA(insert OID = 3083 ( pg_available_extension_versions PGNSP PGUID 12 10 100 0 f f f t t s 0 0 2249 "" "{19,25,16,19,1003,25}" "{o,o,o,o,o,o}" "{name,version,relocatable,schema,requires,comment}" _null_ pg_available_extension_versions _null_ _null_ _null_ )); +DESCR("list available extension versions"); +DATA(insert OID = 3084 ( pg_extension_update_paths PGNSP PGUID 12 10 100 0 f f f t t s 1 0 2249 "19" "{19,25,25,25}" "{i,o,o,o}" "{name,source,target,path}" _null_ pg_extension_update_paths _null_ _null_ _null_ )); +DESCR("list an extension's version update paths"); +DATA(insert OID = 3086 ( pg_extension_config_dump PGNSP PGUID 12 1 0 0 f f f t f v 2 0 2278 "2205 25" _null_ _null_ _null_ _null_ pg_extension_config_dump _null_ _null_ _null_ )); +DESCR("flag an extension's table contents to be emitted by pg_dump"); + /* SQL-spec window functions */ DATA(insert OID = 3100 ( row_number PGNSP PGUID 12 1 0 0 f t f f f i 0 0 20 "" _null_ _null_ _null_ _null_ window_row_number _null_ _null_ _null_ )); DESCR("row number within partition"); diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h index ee40705160..24a2884186 100644 --- a/src/include/catalog/pg_type.h +++ b/src/include/catalog/pg_type.h @@ -194,6 +194,13 @@ CATALOG(pg_type,1247) BKI_BOOTSTRAP BKI_ROWTYPE_OID(71) BKI_SCHEMA_MACRO int4 typndims; /* + * Collation: 0 if type cannot use collations, + * DEFAULT_COLLATION_OID for collatable base types, possibly other + * OID for domains + */ + Oid typcollation; + + /* * If typdefaultbin is not NULL, it is the nodeToString representation of * a default expression for the type. Currently this is only used for * domains. @@ -223,7 +230,7 @@ typedef FormData_pg_type *Form_pg_type; * compiler constants for pg_type * ---------------- */ -#define Natts_pg_type 28 +#define Natts_pg_type 29 #define Anum_pg_type_typname 1 #define Anum_pg_type_typnamespace 2 #define Anum_pg_type_typowner 3 @@ -250,8 +257,9 @@ typedef FormData_pg_type *Form_pg_type; #define Anum_pg_type_typbasetype 24 #define Anum_pg_type_typtypmod 25 #define Anum_pg_type_typndims 26 -#define Anum_pg_type_typdefaultbin 27 -#define Anum_pg_type_typdefault 28 +#define Anum_pg_type_typcollation 27 +#define Anum_pg_type_typdefaultbin 28 +#define Anum_pg_type_typdefault 29 /* ---------------- @@ -268,87 +276,87 @@ typedef FormData_pg_type *Form_pg_type; */ /* OIDS 1 - 99 */ -DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 0 _null_ _null_ )); DESCR("boolean, 'true'/'false'"); #define BOOLOID 16 -DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 0 _null_ _null_ )); DESCR("variable-length string, binary values escaped"); #define BYTEAOID 17 -DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 0 _null_ _null_ )); DESCR("single character"); #define CHAROID 18 -DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 0 _null_ _null_ )); DESCR("63-character type for storing system identifiers"); #define NAMEOID 19 -DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 0 _null_ _null_ )); DESCR("~18 digit integer, 8-byte storage"); #define INT8OID 20 -DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 0 _null_ _null_ )); DESCR("-32 thousand to 32 thousand, 2-byte storage"); #define INT2OID 21 -DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 0 _null_ _null_ )); DESCR("array of int2, used in system tables"); #define INT2VECTOROID 22 -DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 0 _null_ _null_ )); DESCR("-2 billion to 2 billion integer, 4-byte storage"); #define INT4OID 23 -DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 0 _null_ _null_ )); DESCR("registered procedure"); #define REGPROCOID 24 -DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 100 _null_ _null_ )); DESCR("variable-length string, no limit specified"); #define TEXTOID 25 -DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 0 _null_ _null_ )); DESCR("object identifier(oid), maximum 4 billion"); #define OIDOID 26 -DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 0 _null_ _null_ )); DESCR("(block, offset), physical location of tuple"); #define TIDOID 27 -DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 0 _null_ _null_ )); DESCR("transaction id"); #define XIDOID 28 -DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 0 _null_ _null_ )); DESCR("command identifier type, sequence in transaction id"); #define CIDOID 29 -DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 0 _null_ _null_ )); DESCR("array of oids, used in system tables"); #define OIDVECTOROID 30 /* hand-built rowtype entries for bootstrapped catalogs */ /* NB: OIDs assigned here must match the BKI_ROWTYPE_OID declarations */ -DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ )); /* OIDS 100 - 199 */ -DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 0 _null_ _null_ )); DESCR("XML content"); #define XMLOID 142 -DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); -DATA(insert OID = 194 ( pg_node_tree PGNSP PGUID -1 f b S f t \054 0 0 0 pg_node_tree_in pg_node_tree_out pg_node_tree_recv pg_node_tree_send - - - i x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 194 ( pg_node_tree PGNSP PGUID -1 f b S f t \054 0 0 0 pg_node_tree_in pg_node_tree_out pg_node_tree_recv pg_node_tree_send - - - i x f 0 -1 0 100 _null_ _null_ )); DESCR("string representing an internal node tree"); #define PGNODETREEOID 194 /* OIDS 200 - 299 */ -DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 0 _null_ _null_ )); DESCR("storage manager"); /* OIDS 300 - 399 */ @@ -358,231 +366,231 @@ DESCR("storage manager"); /* OIDS 500 - 599 */ /* OIDS 600 - 699 */ -DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 0 _null_ _null_ )); DESCR("geometric point '(x, y)'"); #define POINTOID 600 -DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 0 _null_ _null_ )); DESCR("geometric line segment '(pt1,pt2)'"); #define LSEGOID 601 -DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 0 _null_ _null_ )); DESCR("geometric path '(pt1,...)'"); #define PATHOID 602 -DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 0 _null_ _null_ )); DESCR("geometric box '(lower left,upper right)'"); #define BOXOID 603 -DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 0 _null_ _null_ )); DESCR("geometric polygon '(pt1,...)'"); #define POLYGONOID 604 -DATA(insert OID = 628 ( line PGNSP PGUID 32 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 628 ( line PGNSP PGUID 32 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 0 _null_ _null_ )); DESCR("geometric line (not implemented)"); #define LINEOID 628 -DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 0 _null_ _null_ )); DESCR(""); /* OIDS 700 - 799 */ -DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 0 _null_ _null_ )); DESCR("single-precision floating point number, 4-byte storage"); #define FLOAT4OID 700 -DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 0 _null_ _null_ )); DESCR("double-precision floating point number, 8-byte storage"); #define FLOAT8OID 701 -DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 0 _null_ _null_ )); DESCR("absolute, limited-range date and time (Unix system time)"); #define ABSTIMEOID 702 -DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 0 _null_ _null_ )); DESCR("relative, limited-range time interval (Unix delta time)"); #define RELTIMEOID 703 -DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 0 _null_ _null_ )); DESCR("(abstime,abstime), time interval"); #define TINTERVALOID 704 -DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f b X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f b X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 0 _null_ _null_ )); DESCR(""); #define UNKNOWNOID 705 -DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 0 _null_ _null_ )); DESCR("geometric circle '(center,radius)'"); #define CIRCLEOID 718 -DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 0 _null_ _null_ )); DESCR("monetary amounts, $d,ddd.cc"); #define CASHOID 790 -DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 0 _null_ _null_ )); /* OIDS 800 - 899 */ -DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 0 _null_ _null_ )); DESCR("XX:XX:XX:XX:XX:XX, MAC address"); #define MACADDROID 829 -DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 0 _null_ _null_ )); DESCR("IP address/netmask, host address, netmask optional"); #define INETOID 869 -DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 0 _null_ _null_ )); DESCR("network IP address/netmask, network address"); #define CIDROID 650 /* OIDS 900 - 999 */ /* OIDS 1000 - 1099 */ -DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); #define INT4ARRAYOID 1007 -DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 100 _null_ _null_ )); #define TEXTARRAYOID 1009 -DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout - i x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout - i x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout - i x f 0 -1 0 100 _null_ _null_ )); +DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout - i x f 0 -1 0 100 _null_ _null_ )); +DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); #define FLOAT4ARRAYOID 1021 -DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 0 _null_ _null_ )); DESCR("access control list"); #define ACLITEMOID 1033 -DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); #define CSTRINGARRAYOID 1263 -DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 100 _null_ _null_ )); DESCR("char(length), blank-padded string, fixed storage length"); #define BPCHAROID 1042 -DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 100 _null_ _null_ )); DESCR("varchar(length), non-blank-padded string, variable storage length"); #define VARCHAROID 1043 -DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 0 _null_ _null_ )); DESCR("date"); #define DATEOID 1082 -DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 0 _null_ _null_ )); DESCR("time of day"); #define TIMEOID 1083 /* OIDS 1100 - 1199 */ -DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 0 _null_ _null_ )); DESCR("date and time"); #define TIMESTAMPOID 1114 -DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout - d x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout - d x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout - d x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout - d x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 0 _null_ _null_ )); DESCR("date and time with time zone"); #define TIMESTAMPTZOID 1184 -DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout - d x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout - d x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 0 _null_ _null_ )); DESCR("@ <number> <units>, time interval"); #define INTERVALOID 1186 -DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout - d x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout - d x f 0 -1 0 0 _null_ _null_ )); /* OIDS 1200 - 1299 */ -DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout - i x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout - i x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 0 _null_ _null_ )); DESCR("time of day with time zone"); #define TIMETZOID 1266 -DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout - d x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout - d x f 0 -1 0 0 _null_ _null_ )); /* OIDS 1500 - 1599 */ -DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 0 _null_ _null_ )); DESCR("fixed-length bit string"); #define BITOID 1560 -DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout - i x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout - i x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 0 _null_ _null_ )); DESCR("variable-length bit string"); #define VARBITOID 1562 -DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout - i x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout - i x f 0 -1 0 0 _null_ _null_ )); /* OIDS 1600 - 1699 */ /* OIDS 1700 - 1799 */ -DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 0 _null_ _null_ )); DESCR("numeric(precision, decimal), arbitrary precision number"); #define NUMERICOID 1700 -DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 0 _null_ _null_ )); DESCR("reference to cursor (portal name)"); #define REFCURSOROID 1790 /* OIDS 2200 - 2299 */ -DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); -DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 0 _null_ _null_ )); DESCR("registered procedure (with args)"); #define REGPROCEDUREOID 2202 -DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 0 _null_ _null_ )); DESCR("registered operator"); #define REGOPEROID 2203 -DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 0 _null_ _null_ )); DESCR("registered operator (with args)"); #define REGOPERATOROID 2204 -DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 0 _null_ _null_ )); DESCR("registered class"); #define REGCLASSOID 2205 -DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 0 _null_ _null_ )); DESCR("registered type"); #define REGTYPEOID 2206 -DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); #define REGTYPEARRAYOID 2211 /* uuid */ -DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 0 _null_ _null_ )); DESCR("UUID datatype"); -DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); /* text search */ -DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 0 _null_ _null_ )); DESCR("text representation for text search"); #define TSVECTOROID 3614 -DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 0 _null_ _null_ )); DESCR("GiST index internal text representation for text search"); #define GTSVECTOROID 3642 -DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 0 _null_ _null_ )); DESCR("query representation for text search"); #define TSQUERYOID 3615 -DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 0 _null_ _null_ )); DESCR("registered text search configuration"); #define REGCONFIGOID 3734 -DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 0 _null_ _null_ )); DESCR("registered text search dictionary"); #define REGDICTIONARYOID 3769 -DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); -DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); +DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ )); -DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 0 _null_ _null_ )); DESCR("txid snapshot"); -DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 0 _null_ _null_ )); /* * pseudo-types @@ -597,31 +605,31 @@ DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 a * but there is now support for it in records and arrays. Perhaps we should * just treat it as a regular base type? */ -DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ )); #define RECORDOID 2249 -DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 0 _null_ _null_ )); #define RECORDARRAYOID 2287 -DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 0 _null_ _null_ )); #define CSTRINGOID 2275 -DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 0 _null_ _null_ )); #define ANYOID 2276 -DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 0 _null_ _null_ )); #define ANYARRAYOID 2277 -DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out - - - - - i p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out - - - - - i p f 0 -1 0 0 _null_ _null_ )); #define VOIDOID 2278 -DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ )); #define TRIGGEROID 2279 -DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ )); #define LANGUAGE_HANDLEROID 2280 -DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 0 _null_ _null_ )); #define INTERNALOID 2281 -DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 0 _null_ _null_ )); #define OPAQUEOID 2282 -DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 0 _null_ _null_ )); #define ANYELEMENTOID 2283 -DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 0 _null_ _null_ )); #define ANYNONARRAYOID 2776 -DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 0 _null_ _null_ )); #define ANYENUMOID 3500 DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_handler_in fdw_handler_out - - - - - i p f 0 -1 0 _null_ _null_ )); #define FDW_HANDLEROID 3115 diff --git a/src/include/catalog/pg_type_fn.h b/src/include/catalog/pg_type_fn.h index 836e216178..81e7d7fec3 100644 --- a/src/include/catalog/pg_type_fn.h +++ b/src/include/catalog/pg_type_fn.h @@ -50,7 +50,8 @@ extern Oid TypeCreate(Oid newTypeOid, char storage, int32 typeMod, int32 typNDims, - bool typeNotNull); + bool typeNotNull, + Oid typeCollation); extern void GenerateTypeDependencies(Oid typeNamespace, Oid typeObjectId, @@ -67,6 +68,7 @@ extern void GenerateTypeDependencies(Oid typeNamespace, Oid elementType, bool isImplicitArray, Oid baseType, + Oid typeCollation, Node *defaultExpr, bool rebuild); diff --git a/src/include/commands/alter.h b/src/include/commands/alter.h index 74d5132636..21731685f5 100644 --- a/src/include/commands/alter.h +++ b/src/include/commands/alter.h @@ -20,11 +20,11 @@ extern void ExecRenameStmt(RenameStmt *stmt); extern void ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt); -extern void AlterObjectNamespace(Relation rel, int cacheId, - Oid classId, Oid objid, Oid nspId, - int Anum_name, int Anum_namespace, int Anum_owner, - AclObjectKind acl_kind, - bool superuser_only); +extern Oid AlterObjectNamespace_oid(Oid classId, Oid objid, Oid nspOid); +extern Oid AlterObjectNamespace(Relation rel, int oidCacheId, int nameCacheId, + Oid objid, Oid nspOid, + int Anum_name, int Anum_namespace, int Anum_owner, + AclObjectKind acl_kind); extern void ExecAlterOwnerStmt(AlterOwnerStmt *stmt); #endif /* ALTER_H */ diff --git a/src/include/commands/collationcmds.h b/src/include/commands/collationcmds.h new file mode 100644 index 0000000000..60504694a5 --- /dev/null +++ b/src/include/commands/collationcmds.h @@ -0,0 +1,28 @@ +/*------------------------------------------------------------------------- + * + * collationcmds.h + * prototypes for collationcmds.c. + * + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/commands/collationcmds.h + * + *------------------------------------------------------------------------- + */ + +#ifndef COLLATIONCMDS_H +#define COLLATIONCMDS_H + +#include "nodes/parsenodes.h" + +extern void DefineCollation(List *names, List *parameters); +extern void DropCollationsCommand(DropStmt *drop); +extern void RenameCollation(List *name, const char *newname); +extern void AlterCollationOwner(List *name, Oid newOwnerId); +extern void AlterCollationOwner_oid(Oid collationOid, Oid newOwnerId); +extern void AlterCollationNamespace(List *name, const char *newschema); +extern Oid AlterCollationNamespace_oid(Oid collOid, Oid newNspOid); + +#endif /* COLLATIONCMDS_H */ diff --git a/src/include/commands/conversioncmds.h b/src/include/commands/conversioncmds.h index 6156c4a94e..f77023ffe3 100644 --- a/src/include/commands/conversioncmds.h +++ b/src/include/commands/conversioncmds.h @@ -23,5 +23,6 @@ extern void RenameConversion(List *name, const char *newname); extern void AlterConversionOwner(List *name, Oid newOwnerId); extern void AlterConversionOwner_oid(Oid conversionOid, Oid newOwnerId); extern void AlterConversionNamespace(List *name, const char *newschema); +extern Oid AlterConversionNamespace_oid(Oid convOid, Oid newNspOid); #endif /* CONVERSIONCMDS_H */ diff --git a/src/include/commands/copy.h b/src/include/commands/copy.h index 9e2bbe8d8e..afe4b5e450 100644 --- a/src/include/commands/copy.h +++ b/src/include/commands/copy.h @@ -14,12 +14,24 @@ #ifndef COPY_H #define COPY_H +#include "nodes/execnodes.h" #include "nodes/parsenodes.h" #include "tcop/dest.h" +typedef struct CopyStateData *CopyState; + extern uint64 DoCopy(const CopyStmt *stmt, const char *queryString); +extern CopyState BeginCopyFrom(Relation rel, const char *filename, + List *attnamelist, List *options); +extern void EndCopyFrom(CopyState cstate); +extern bool NextCopyFrom(CopyState cstate, ExprContext *econtext, + Datum *values, bool *nulls, Oid *tupleOid); +extern bool NextCopyFromRawFields(CopyState cstate, + char ***fields, int *nfields); +extern void CopyFromErrorCallback(void *arg); + extern DestReceiver *CreateCopyDestReceiver(void); #endif /* COPY_H */ diff --git a/src/include/commands/dbcommands.h b/src/include/commands/dbcommands.h index 809754792a..f54c57907a 100644 --- a/src/include/commands/dbcommands.h +++ b/src/include/commands/dbcommands.h @@ -65,4 +65,6 @@ extern char *get_database_name(Oid dbid); extern void dbase_redo(XLogRecPtr lsn, XLogRecord *rptr); extern void dbase_desc(StringInfo buf, uint8 xl_info, char *rec); +extern void check_encoding_locale_matches(int encoding, const char *collate, const char *ctype); + #endif /* DBCOMMANDS_H */ diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h index 01f271bff4..157ee39461 100644 --- a/src/include/commands/defrem.h +++ b/src/include/commands/defrem.h @@ -66,6 +66,7 @@ extern void DropCast(DropCastStmt *stmt); extern void DropCastById(Oid castOid); extern void AlterFunctionNamespace(List *name, List *argtypes, bool isagg, const char *newschema); +extern Oid AlterFunctionNamespace_oid(Oid procOid, Oid nspOid); extern void ExecuteDoStmt(DoStmt *stmt); extern Oid get_cast_oid(Oid sourcetypeid, Oid targettypeid, bool missing_ok); @@ -77,6 +78,7 @@ extern void AlterOperatorOwner(List *name, TypeName *typeName1, TypeName *typename2, Oid newOwnerId); extern void AlterOperatorOwner_oid(Oid operOid, Oid newOwnerId); extern void AlterOperatorNamespace(List *names, List *argtypes, const char *newschema); +extern Oid AlterOperatorNamespace_oid(Oid operOid, Oid newNspOid); /* commands/aggregatecmds.c */ extern void DefineAggregate(List *name, List *args, bool oldstyle, @@ -100,9 +102,11 @@ extern void RenameOpFamily(List *name, const char *access_method, const char *ne extern void AlterOpClassOwner(List *name, const char *access_method, Oid newOwnerId); extern void AlterOpClassOwner_oid(Oid opclassOid, Oid newOwnerId); extern void AlterOpClassNamespace(List *name, char *access_method, const char *newschema); +extern Oid AlterOpClassNamespace_oid(Oid opclassOid, Oid newNspOid); extern void AlterOpFamilyOwner(List *name, const char *access_method, Oid newOwnerId); extern void AlterOpFamilyOwner_oid(Oid opfamilyOid, Oid newOwnerId); extern void AlterOpFamilyNamespace(List *name, char *access_method, const char *newschema); +extern Oid AlterOpFamilyNamespace_oid(Oid opfamilyOid, Oid newNspOid); extern Oid get_am_oid(const char *amname, bool missing_ok); extern Oid get_opclass_oid(Oid amID, List *opclassname, bool missing_ok); extern Oid get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok); @@ -111,6 +115,7 @@ extern Oid get_opfamily_oid(Oid amID, List *opfamilyname, bool missing_ok); extern void DefineTSParser(List *names, List *parameters); extern void RenameTSParser(List *oldname, const char *newname); extern void AlterTSParserNamespace(List *name, const char *newschema); +extern Oid AlterTSParserNamespace_oid(Oid prsId, Oid newNspOid); extern void RemoveTSParsers(DropStmt *drop); extern void RemoveTSParserById(Oid prsId); @@ -121,10 +126,12 @@ extern void RemoveTSDictionaryById(Oid dictId); extern void AlterTSDictionary(AlterTSDictionaryStmt *stmt); extern void AlterTSDictionaryOwner(List *name, Oid newOwnerId); extern void AlterTSDictionaryNamespace(List *name, const char *newschema); +extern Oid AlterTSDictionaryNamespace_oid(Oid dictId, Oid newNspOid); extern void DefineTSTemplate(List *names, List *parameters); extern void RenameTSTemplate(List *oldname, const char *newname); extern void AlterTSTemplateNamespace(List *name, const char *newschema); +extern Oid AlterTSTemplateNamespace_oid(Oid tmplId, Oid newNspOid); extern void RemoveTSTemplates(DropStmt *stmt); extern void RemoveTSTemplateById(Oid tmplId); @@ -135,6 +142,7 @@ extern void RemoveTSConfigurationById(Oid cfgId); extern void AlterTSConfiguration(AlterTSConfigurationStmt *stmt); extern void AlterTSConfigurationOwner(List *name, Oid newOwnerId); extern void AlterTSConfigurationNamespace(List *name, const char *newschema); +extern Oid AlterTSConfigurationNamespace_oid(Oid cfgId, Oid newNspOid); extern text *serialize_deflist(List *deflist); extern List *deserialize_deflist(Datum txt); diff --git a/src/include/commands/extension.h b/src/include/commands/extension.h new file mode 100644 index 0000000000..c6e69d5fd4 --- /dev/null +++ b/src/include/commands/extension.h @@ -0,0 +1,49 @@ +/*------------------------------------------------------------------------- + * + * extension.h + * Extension management commands (create/drop extension). + * + * + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/commands/extension.h + * + *------------------------------------------------------------------------- + */ +#ifndef EXTENSION_H +#define EXTENSION_H + +#include "nodes/parsenodes.h" + + +/* + * creating_extension is only true while running a CREATE EXTENSION command. + * It instructs recordDependencyOnCurrentExtension() to register a dependency + * on the current pg_extension object for each SQL object created by its + * installation script. + */ +extern bool creating_extension; +extern Oid CurrentExtensionObject; + + +extern void CreateExtension(CreateExtensionStmt *stmt); + +extern void RemoveExtensions(DropStmt *stmt); +extern void RemoveExtensionById(Oid extId); + +extern Oid InsertExtensionTuple(const char *extName, Oid extOwner, + Oid schemaOid, bool relocatable, const char *extVersion, + Datum extConfig, Datum extCondition, + List *requiredExtensions); + +extern void ExecAlterExtensionStmt(AlterExtensionStmt *stmt); + +extern void ExecAlterExtensionContentsStmt(AlterExtensionContentsStmt *stmt); + +extern Oid get_extension_oid(const char *extname, bool missing_ok); +extern char *get_extension_name(Oid ext_oid); + +extern void AlterExtensionNamespace(List *names, const char *newschema); + +#endif /* EXTENSION_H */ diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h index b266230778..d4383525db 100644 --- a/src/include/commands/tablecmds.h +++ b/src/include/commands/tablecmds.h @@ -53,7 +53,8 @@ extern void RenameRelationInternal(Oid myrelid, Oid namespaceId); extern void find_composite_type_dependencies(Oid typeOid, - char origRelkind, const char *origRelname); + Relation origRelation, + const char *origTypeName); extern AttrNumber *varattnos_map(TupleDesc olddesc, TupleDesc newdesc); extern AttrNumber *varattnos_map_schema(TupleDesc old, List *schema); diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h index b13363aaf7..1b20296934 100644 --- a/src/include/commands/typecmds.h +++ b/src/include/commands/typecmds.h @@ -41,7 +41,8 @@ extern void AlterTypeOwner(List *names, Oid newOwnerId); extern void AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId, bool hasDependEntry); extern void AlterTypeNamespace(List *names, const char *newschema); -extern void AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, +extern Oid AlterTypeNamespace_oid(Oid typeOid, Oid nspOid); +extern Oid AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, bool isImplicitArray, bool errorOnTableType); diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h index 93f894e639..3ad7b4bd05 100644 --- a/src/include/commands/vacuum.h +++ b/src/include/commands/vacuum.h @@ -66,12 +66,13 @@ typedef struct VacAttrStats * Note: do not assume that the data being analyzed has the same datatype * shown in attr, ie do not trust attr->atttypid, attlen, etc. This is * because some index opclasses store a different type than the underlying - * column/expression. Instead use attrtypid, attrtypmod, and attrtype for + * column/expression. Instead use attrtypid, attrtypmod, attrcollation, and attrtype for * information about the datatype being fed to the typanalyze function. */ Form_pg_attribute attr; /* copy of pg_attribute row for column */ Oid attrtypid; /* type of data being analyzed */ int32 attrtypmod; /* typmod of data being analyzed */ + Oid attrcollation; /* collation of the data being analyzed */ Form_pg_type attrtype; /* copy of pg_type row for attrtypid */ MemoryContext anl_context; /* where to save long-lived data */ diff --git a/src/include/fmgr.h b/src/include/fmgr.h index 33369b02bc..9720117a7f 100644 --- a/src/include/fmgr.h +++ b/src/include/fmgr.h @@ -50,6 +50,7 @@ typedef struct FmgrInfo bool fn_strict; /* function is "strict" (NULL in => NULL out) */ bool fn_retset; /* function returns a set */ unsigned char fn_stats; /* collect stats if track_functions > this */ + Oid fn_collation; /* collation to use */ void *fn_extra; /* extra space for use by handler */ MemoryContext fn_mcxt; /* memory context to store fn_extra in */ fmNodePtr fn_expr; /* expression parse tree for call, or NULL */ @@ -84,6 +85,16 @@ extern void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt); /* + * Initialize the fn_collation field + */ +extern void fmgr_info_collation(Oid collationId, FmgrInfo *finfo); + +/* + * Initialize the fn_expr field and set the collation based on it + */ +extern void fmgr_info_expr(fmNodePtr expr, FmgrInfo *finfo); + +/* * Copy an FmgrInfo struct */ extern void fmgr_info_copy(FmgrInfo *dstinfo, FmgrInfo *srcinfo, @@ -296,6 +307,7 @@ extern struct varlena *pg_detoast_datum_packed(struct varlena * datum); #define PG_RETURN_VARCHAR_P(x) PG_RETURN_POINTER(x) #define PG_RETURN_HEAPTUPLEHEADER(x) PG_RETURN_POINTER(x) +#define PG_GET_COLLATION() (fcinfo->flinfo ? fcinfo->flinfo->fn_collation : InvalidOid) /*------------------------------------------------------------------------- * Support for detecting call convention of dynamically-loaded functions @@ -438,6 +450,12 @@ extern Datum DirectFunctionCall9(PGFunction func, Datum arg1, Datum arg2, Datum arg6, Datum arg7, Datum arg8, Datum arg9); +/* the same but passing a collation */ +extern Datum DirectFunctionCall1WithCollation(PGFunction func, Oid collation, + Datum arg1); +extern Datum DirectFunctionCall2WithCollation(PGFunction func, Oid collation, + Datum arg1, Datum arg2); + /* These are for invocation of a previously-looked-up function with a * directly-computed parameter list. Note that neither arguments nor result * are allowed to be NULL. diff --git a/src/include/mb/pg_wchar.h b/src/include/mb/pg_wchar.h index f110723da1..565b53b3e6 100644 --- a/src/include/mb/pg_wchar.h +++ b/src/include/mb/pg_wchar.h @@ -393,8 +393,8 @@ extern int pg_encoding_max_length(int encoding); extern int pg_database_encoding_max_length(void); #ifdef USE_WIDE_UPPER_LOWER -extern size_t wchar2char(char *to, const wchar_t *from, size_t tolen); -extern size_t char2wchar(wchar_t *to, size_t tolen, const char *from, size_t fromlen); +extern size_t wchar2char(char *to, const wchar_t *from, size_t tolen, Oid collation); +extern size_t char2wchar(wchar_t *to, size_t tolen, const char *from, size_t fromlen, Oid collation); #endif extern int SetClientEncoding(int encoding, bool doit); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 7d1c681e06..a872eaec29 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -419,6 +419,7 @@ typedef struct ExecRowMark Relation relation; /* opened and suitably locked relation */ Index rti; /* its range table index */ Index prti; /* parent range table index, if child */ + Index rowmarkId; /* unique identifier for resjunk columns */ RowMarkType markType; /* see enum in nodes/plannodes.h */ bool noWait; /* NOWAIT option */ ItemPointerData curCtid; /* ctid of currently locked tuple, if any */ diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h index 7c41312587..8b7db798b9 100644 --- a/src/include/nodes/makefuncs.h +++ b/src/include/nodes/makefuncs.h @@ -27,6 +27,7 @@ extern Var *makeVar(Index varno, AttrNumber varattno, Oid vartype, int32 vartypmod, + Oid varcollid, Index varlevelsup); extern Var *makeVarFromTargetEntry(Index varno, @@ -67,10 +68,10 @@ extern RangeVar *makeRangeVar(char *schemaname, char *relname, int location); extern TypeName *makeTypeName(char *typnam); extern TypeName *makeTypeNameFromNameList(List *names); -extern TypeName *makeTypeNameFromOid(Oid typeOid, int32 typmod); +extern TypeName *makeTypeNameFromOid(Oid typeOid, int32 typmod, Oid collOid); extern FuncExpr *makeFuncExpr(Oid funcid, Oid rettype, - List *args, CoercionForm fformat); + List *args, Oid collid, CoercionForm fformat); extern DefElem *makeDefElem(char *name, Node *arg); extern DefElem *makeDefElemExtended(char *nameSpace, char *name, Node *arg, diff --git a/src/include/nodes/nodeFuncs.h b/src/include/nodes/nodeFuncs.h index 433fbfe857..32f09f68ad 100644 --- a/src/include/nodes/nodeFuncs.h +++ b/src/include/nodes/nodeFuncs.h @@ -27,6 +27,8 @@ extern Oid exprType(Node *expr); extern int32 exprTypmod(Node *expr); +extern Oid exprCollation(Node *expr); +extern Oid coercion_expression_result_collation(Oid resulttype, Node *arg); extern bool exprIsLengthCoercion(Node *expr, int32 *coercedTypmod); extern bool expression_returns_set(Node *clause); diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 5b065b517c..b7f130ff77 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -169,6 +169,7 @@ typedef enum NodeTag T_JoinExpr, T_FromExpr, T_IntoClause, + T_CollateClause, /* * TAGS FOR EXPRESSION STATE NODES (execnodes.h) @@ -358,6 +359,9 @@ typedef enum NodeTag T_AlterTableSpaceOptionsStmt, T_SecLabelStmt, T_CreateForeignTableStmt, + T_CreateExtensionStmt, + T_AlterExtensionStmt, + T_AlterExtensionContentsStmt, /* * TAGS FOR PARSE TREE NODES (parsenodes.h) diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 583b54cd2f..6bc427db08 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -167,7 +167,8 @@ typedef struct Query * specify the type by OID than by name. If "names" is NIL then the * actual type OID is given by typeOid, otherwise typeOid is unused. * Similarly, if "typmods" is NIL then the actual typmod is expected to - * be prespecified in typemod, otherwise typemod is unused. + * be prespecified in typemod, otherwise typemod is unused. Similarly + * for collnames/collOid. * * If pct_type is TRUE, then names is actually a field name and we look up * the type of that field. Otherwise (the normal case), names is a type @@ -183,6 +184,8 @@ typedef struct TypeName List *typmods; /* type modifier expression(s) */ int32 typemod; /* prespecified type modifier */ List *arrayBounds; /* array bounds */ + List *collnames; /* collation name */ + Oid collOid; /* collation by OID */ int location; /* token location, or -1 if unknown */ } TypeName; @@ -517,6 +520,7 @@ typedef struct IndexElem char *name; /* name of attribute to index, or NULL */ Node *expr; /* expression to index, or NULL */ char *indexcolname; /* name for index column; NULL = default */ + List *collation; /* name of collation; NIL = default */ List *opclass; /* name of desired opclass; NIL = default */ SortByDir ordering; /* ASC/DESC/default */ SortByNulls nulls_ordering; /* FIRST/LAST/default */ @@ -702,12 +706,14 @@ typedef struct RangeTblEntry * Fields valid for a function RTE (else NULL): * * If the function returns RECORD, funccoltypes lists the column types - * declared in the RTE's column type specification, and funccoltypmods - * lists their declared typmods. Otherwise, both fields are NIL. + * declared in the RTE's column type specification, funccoltypmods + * lists their declared typmods, funccolcollations their collations. + * Otherwise, those fields are NIL. */ Node *funcexpr; /* expression tree for func call */ List *funccoltypes; /* OID list of column type OIDs */ List *funccoltypmods; /* integer list of column typmods */ + List *funccolcollations; /* OID list of column collation OIDs */ /* * Fields valid for a values RTE (else NIL): @@ -722,6 +728,7 @@ typedef struct RangeTblEntry bool self_reference; /* is this a recursive self-reference? */ List *ctecoltypes; /* OID list of column type OIDs */ List *ctecoltypmods; /* integer list of column typmods */ + List *ctecolcollations; /* OID list of column collation OIDs */ /* * Fields valid in all RTEs: @@ -883,6 +890,7 @@ typedef struct CommonTableExpr List *ctecolnames; /* list of output column names */ List *ctecoltypes; /* OID list of output column type OIDs */ List *ctecoltypmods; /* integer list of output column typmods */ + List *ctecolcollations; /* OID list of column collation OIDs */ } CommonTableExpr; /***************************************************************************** @@ -1032,6 +1040,7 @@ typedef struct SetOperationStmt /* Fields derived during parse analysis: */ List *colTypes; /* OID list of output column type OIDs */ List *colTypmods; /* integer list of output column typmods */ + List *colCollations; /* OID list of output column collation OIDs */ List *groupClauses; /* a list of SortGroupClause's */ /* groupClauses is NIL if UNION ALL, but must be set otherwise */ } SetOperationStmt; @@ -1051,19 +1060,21 @@ typedef struct SetOperationStmt /* * When a command can act on several kinds of objects with only one * parse structure required, use these constants to designate the - * object type. + * object type. Note that commands typically don't support all the types. */ typedef enum ObjectType { OBJECT_AGGREGATE, - OBJECT_ATTRIBUTE, /* type's attribute, when distinct from column */ + OBJECT_ATTRIBUTE, /* type's attribute, when distinct from column */ OBJECT_CAST, OBJECT_COLUMN, OBJECT_CONSTRAINT, + OBJECT_COLLATION, OBJECT_CONVERSION, OBJECT_DATABASE, OBJECT_DOMAIN, + OBJECT_EXTENSION, OBJECT_FDW, OBJECT_FOREIGN_SERVER, OBJECT_FOREIGN_TABLE, @@ -1140,6 +1151,7 @@ typedef enum AlterTableType AT_ReAddIndex, /* internal to commands/tablecmds.c */ AT_AddConstraint, /* add constraint */ AT_AddConstraintRecurse, /* internal to commands/tablecmds.c */ + AT_ValidateConstraint, /* validate constraint */ AT_ProcessedConstraint, /* pre-processed add constraint (local in * parser/parse_utilcmd.c) */ AT_AddIndexConstraint, /* add constraint using existing index */ @@ -1182,6 +1194,7 @@ typedef struct AlterTableCmd /* one subcommand of an ALTER TABLE */ Node *transform; /* transformation expr for ALTER TYPE */ DropBehavior behavior; /* RESTRICT or CASCADE for DROP cases */ bool missing_ok; /* skip error if missing? */ + bool validated; } AlterTableCmd; @@ -1523,6 +1536,36 @@ typedef struct AlterTableSpaceOptionsStmt } AlterTableSpaceOptionsStmt; /* ---------------------- + * Create/Alter Extension Statements + * ---------------------- + */ + +typedef struct CreateExtensionStmt +{ + NodeTag type; + char *extname; + List *options; /* List of DefElem nodes */ +} CreateExtensionStmt; + +/* Only used for ALTER EXTENSION UPDATE; later might need an action field */ +typedef struct AlterExtensionStmt +{ + NodeTag type; + char *extname; + List *options; /* List of DefElem nodes */ +} AlterExtensionStmt; + +typedef struct AlterExtensionContentsStmt +{ + NodeTag type; + char *extname; /* Extension's name */ + int action; /* +1 = add object, -1 = drop object */ + ObjectType objtype; /* Object's type */ + List *objname; /* Qualified name of the object */ + List *objargs; /* Arguments if needed (eg, for functions) */ +} AlterExtensionContentsStmt; + +/* ---------------------- * Create/Drop FOREIGN DATA WRAPPER Statements * ---------------------- */ diff --git a/src/include/nodes/pg_list.h b/src/include/nodes/pg_list.h index 2d4ef52b13..e5dd04bd1c 100644 --- a/src/include/nodes/pg_list.h +++ b/src/include/nodes/pg_list.h @@ -182,6 +182,15 @@ extern int list_length(List *l); (cell1) != NULL && (cell2) != NULL; \ (cell1) = lnext(cell1), (cell2) = lnext(cell2)) +/* + * forthree - + * the same for three lists + */ +#define forthree(cell1, list1, cell2, list2, cell3, list3) \ + for ((cell1) = list_head(list1), (cell2) = list_head(list2), (cell3) = list_head(list3); \ + (cell1) != NULL && (cell2) != NULL && (cell3) != NULL; \ + (cell1) = lnext(cell1), (cell2) = lnext(cell2), (cell3) = lnext(cell3)) + extern List *lappend(List *list, void *datum); extern List *lappend_int(List *list, int datum); extern List *lappend_oid(List *list, Oid datum); diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index fcfd20320b..4367e40316 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -199,6 +199,7 @@ typedef struct MergeAppend int numCols; /* number of sort-key columns */ AttrNumber *sortColIdx; /* their indexes in the target list */ Oid *sortOperators; /* OIDs of operators to sort them by */ + Oid *collations; /* OIDs of collations */ bool *nullsFirst; /* NULLS FIRST/LAST directions */ } MergeAppend; @@ -402,6 +403,7 @@ typedef struct FunctionScan List *funccolnames; /* output column names (string Value nodes) */ List *funccoltypes; /* OID list of column type OIDs */ List *funccoltypmods; /* integer list of column typmods */ + List *funccolcollations; /* OID list of column collation OIDs */ } FunctionScan; /* ---------------- @@ -514,6 +516,7 @@ typedef struct MergeJoin List *mergeclauses; /* mergeclauses as expression trees */ /* these are arrays, but have the same length as the mergeclauses list: */ Oid *mergeFamilies; /* per-clause OIDs of btree opfamilies */ + Oid *mergeCollations; /* per-clause OIDs of collations */ int *mergeStrategies; /* per-clause ordering (ASC or DESC) */ bool *mergeNullsFirst; /* per-clause nulls ordering */ } MergeJoin; @@ -547,6 +550,7 @@ typedef struct Sort int numCols; /* number of sort-key columns */ AttrNumber *sortColIdx; /* their indexes in the target list */ Oid *sortOperators; /* OIDs of operators to sort them by */ + Oid *collations; /* OIDs of collations */ bool *nullsFirst; /* NULLS FIRST/LAST directions */ } Sort; @@ -756,7 +760,11 @@ typedef enum RowMarkType * The tableoid column is only present for an inheritance hierarchy. * When markType == ROW_MARK_COPY, there is instead a single column named * wholerow%u whole-row value of relation - * In all three cases, %u represents the parent rangetable index (prti). + * In all three cases, %u represents the rowmark ID number (rowmarkId). + * This number is unique within a plan tree, except that child relation + * entries copy their parent's rowmarkId. (Assigning unique numbers + * means we needn't renumber rowmarkIds when flattening subqueries, which + * would require finding and renaming the resjunk columns as well.) * Note this means that all tables in an inheritance hierarchy share the * same resjunk column names. However, in an inherited UPDATE/DELETE the * columns could have different physical column numbers in each subplan. @@ -766,6 +774,7 @@ typedef struct PlanRowMark NodeTag type; Index rti; /* range table index of markable relation */ Index prti; /* range table index of parent relation */ + Index rowmarkId; /* unique identifier for resjunk columns */ RowMarkType markType; /* see enum above */ bool noWait; /* NOWAIT option */ bool isParent; /* true if this is a "dummy" parent entry */ diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index f8a398dc83..8c366df5f5 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -139,6 +139,7 @@ typedef struct Var * all */ Oid vartype; /* pg_type OID for the type of this var */ int32 vartypmod; /* pg_attribute typmod value */ + Oid varcollid; /* collation */ Index varlevelsup; /* for subquery variables referencing outer * relations; 0 in a normal var, >0 means N * levels up */ @@ -155,6 +156,7 @@ typedef struct Const Expr xpr; Oid consttype; /* pg_type OID of the constant's datatype */ int32 consttypmod; /* typmod value, if any */ + Oid constcollid; /* collation */ int constlen; /* typlen of the constant's datatype */ Datum constvalue; /* the constant's value */ bool constisnull; /* whether the constant is null (if true, @@ -205,6 +207,7 @@ typedef struct Param int paramid; /* numeric ID for parameter */ Oid paramtype; /* pg_type OID of parameter's datatype */ int32 paramtypmod; /* typmod value, if known */ + Oid paramcollation; /* parameter's collation */ int location; /* token location, or -1 if unknown */ } Param; @@ -233,6 +236,7 @@ typedef struct Aggref List *aggdistinct; /* DISTINCT (list of SortGroupClause) */ bool aggstar; /* TRUE if argument list was really '*' */ Index agglevelsup; /* > 0 if agg belongs to outer query */ + Oid collid; /* collation OID to use by function */ int location; /* token location, or -1 if unknown */ } Aggref; @@ -248,6 +252,7 @@ typedef struct WindowFunc Index winref; /* index of associated WindowClause */ bool winstar; /* TRUE if argument list was really '*' */ bool winagg; /* is function a simple aggregate? */ + Oid collid; /* collation OID to use by function */ int location; /* token location, or -1 if unknown */ } WindowFunc; @@ -279,6 +284,7 @@ typedef struct ArrayRef Oid refarraytype; /* type of the array proper */ Oid refelemtype; /* type of the array elements */ int32 reftypmod; /* typmod of the array (and elements too) */ + Oid refcollid; /* collation */ List *refupperindexpr;/* expressions that evaluate to upper array * indexes */ List *reflowerindexpr;/* expressions that evaluate to lower array @@ -324,6 +330,7 @@ typedef struct FuncExpr bool funcretset; /* true if function returns set */ CoercionForm funcformat; /* how to display this function call */ List *args; /* arguments to the function */ + Oid collid; /* collation OID to use by function */ int location; /* token location, or -1 if unknown */ } FuncExpr; @@ -367,6 +374,7 @@ typedef struct OpExpr Oid opresulttype; /* PG_TYPE OID of result value */ bool opretset; /* true if operator returns set */ List *args; /* arguments to the operator (1 or 2) */ + Oid collid; /* collation OID to use by operator */ int location; /* token location, or -1 if unknown */ } OpExpr; @@ -399,6 +407,7 @@ typedef struct ScalarArrayOpExpr Oid opfuncid; /* PG_PROC OID of underlying function */ bool useOr; /* true for ANY, false for ALL */ List *args; /* the scalar and array operands */ + Oid collid; /* collation OID to use by operator */ int location; /* token location, or -1 if unknown */ } ScalarArrayOpExpr; @@ -544,6 +553,7 @@ typedef struct SubPlan /* Extra data useful for determining subplan's output type: */ Oid firstColType; /* Type of first column of subplan result */ int32 firstColTypmod; /* Typmod of first column of subplan result */ + Oid firstColCollation; /* Collation of first column of subplan result */ /* Information about execution strategy: */ bool useHashTable; /* TRUE to store subselect output in a hash * table (implies we are doing "IN") */ @@ -592,6 +602,7 @@ typedef struct FieldSelect Oid resulttype; /* type of the field (result type of this * node) */ int32 resulttypmod; /* output typmod (usually -1) */ + Oid resultcollation;/* collation of the field */ } FieldSelect; /* ---------------- @@ -642,6 +653,18 @@ typedef struct RelabelType int location; /* token location, or -1 if unknown */ } RelabelType; +/* + * CollateClause - COLLATE + */ +typedef struct CollateClause +{ + Expr xpr; + Expr *arg; /* original expression */ + List *collnames; /* assigned collation */ + Oid collOid; /* resolved collation OID */ + int location; /* token location, or -1 if unknown */ +} CollateClause; + /* ---------------- * CoerceViaIO * @@ -733,6 +756,7 @@ typedef struct CaseExpr { Expr xpr; Oid casetype; /* type of expression result */ + Oid casecollation; /* collation of expression result */ Expr *arg; /* implicit equality comparison argument */ List *args; /* the arguments (list of WHEN clauses) */ Expr *defresult; /* the default result (ELSE clause) */ @@ -763,6 +787,7 @@ typedef struct CaseTestExpr Expr xpr; Oid typeId; /* type for substituted value */ int32 typeMod; /* typemod for substituted value */ + Oid collation; /* collation for the substituted value */ } CaseTestExpr; /* @@ -850,6 +875,7 @@ typedef struct RowCompareExpr RowCompareType rctype; /* LT LE GE or GT, never EQ or NE */ List *opnos; /* OID list of pairwise comparison ops */ List *opfamilies; /* OID list of containing operator families */ + List *collids; /* OID list of collations for the comparisons */ List *largs; /* the left-hand input arguments */ List *rargs; /* the right-hand input arguments */ } RowCompareExpr; @@ -861,6 +887,7 @@ typedef struct CoalesceExpr { Expr xpr; Oid coalescetype; /* type of expression result */ + Oid coalescecollation; /* collation of expression result */ List *args; /* the arguments */ int location; /* token location, or -1 if unknown */ } CoalesceExpr; @@ -880,6 +907,7 @@ typedef struct MinMaxExpr Oid minmaxtype; /* common type of arguments and result */ MinMaxOp op; /* function to execute */ List *args; /* the arguments */ + Oid collid; /* collation to use */ int location; /* token location, or -1 if unknown */ } MinMaxExpr; @@ -1023,6 +1051,7 @@ typedef struct SetToDefault Expr xpr; Oid typeId; /* type for substituted value */ int32 typeMod; /* typemod for substituted value */ + Oid collid; /* collation for the substituted value */ int location; /* token location, or -1 if unknown */ } SetToDefault; diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 5abbb07f88..0ea6105852 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -82,6 +82,8 @@ typedef struct PlannerGlobal Index lastPHId; /* highest PlaceHolderVar ID assigned */ + Index lastRowMarkId; /* highest PlanRowMark ID assigned */ + bool transientPlan; /* redo plan when TransactionXmin changes? */ } PlannerGlobal; @@ -454,6 +456,7 @@ typedef struct IndexOptInfo int ncolumns; /* number of columns in index */ Oid *opfamily; /* OIDs of operator families for columns */ int *indexkeys; /* column numbers of index's keys, or 0 */ + Oid *indexcollations; /* OIDs of collations of index columns */ Oid *opcintype; /* OIDs of opclass declared input data types */ Oid *sortopfamily; /* OIDs of btree opfamilies, if orderable */ bool *reverse_sort; /* is sort order descending? */ @@ -467,6 +470,7 @@ typedef struct IndexOptInfo bool predOK; /* true if predicate matches query */ bool unique; /* true if a unique index */ + bool hypothetical; /* true if index doesn't really exist */ bool amcanorderbyop; /* does AM support order by operator result? */ bool amoptionalkey; /* can query omit key for the first column? */ bool amsearchnulls; /* can AM search for NULL/NOT NULL entries? */ @@ -586,6 +590,7 @@ typedef struct PathKey EquivalenceClass *pk_eclass; /* the value that is ordered */ Oid pk_opfamily; /* btree opfamily defining the ordering */ + Oid pk_collation; /* collation */ int pk_strategy; /* sort direction (ASC or DESC) */ bool pk_nulls_first; /* do NULLs come before normal values? */ } PathKey; @@ -1112,6 +1117,7 @@ typedef struct MergeScanSelCache { /* Ordering details (cache lookup key) */ Oid opfamily; /* btree opfamily defining the ordering */ + Oid collation; int strategy; /* sort direction (ASC or DESC) */ bool nulls_first; /* do NULLs come before normal values? */ /* Results */ diff --git a/src/include/optimizer/subselect.h b/src/include/optimizer/subselect.h index 31a5d5f186..c437412149 100644 --- a/src/include/optimizer/subselect.h +++ b/src/include/optimizer/subselect.h @@ -28,7 +28,7 @@ extern Node *SS_process_sublinks(PlannerInfo *root, Node *expr, bool isQual); extern void SS_finalize_plan(PlannerInfo *root, Plan *plan, bool attach_initplans); extern Param *SS_make_initplan_from_plan(PlannerInfo *root, Plan *plan, - Oid resulttype, int32 resulttypmod); + Oid resulttype, int32 resulttypmod, Oid resultcollation); extern Param *assign_nestloop_param(PlannerInfo *root, Var *var); extern int SS_assign_special_param(PlannerInfo *root); diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h index 578d3cd870..f288c76592 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -79,6 +79,7 @@ PG_KEYWORD("close", CLOSE, UNRESERVED_KEYWORD) PG_KEYWORD("cluster", CLUSTER, UNRESERVED_KEYWORD) PG_KEYWORD("coalesce", COALESCE, COL_NAME_KEYWORD) PG_KEYWORD("collate", COLLATE, RESERVED_KEYWORD) +PG_KEYWORD("collation", COLLATION, UNRESERVED_KEYWORD) PG_KEYWORD("column", COLUMN, RESERVED_KEYWORD) PG_KEYWORD("comment", COMMENT, UNRESERVED_KEYWORD) PG_KEYWORD("comments", COMMENTS, UNRESERVED_KEYWORD) @@ -150,6 +151,7 @@ PG_KEYWORD("exclusive", EXCLUSIVE, UNRESERVED_KEYWORD) PG_KEYWORD("execute", EXECUTE, UNRESERVED_KEYWORD) PG_KEYWORD("exists", EXISTS, COL_NAME_KEYWORD) PG_KEYWORD("explain", EXPLAIN, UNRESERVED_KEYWORD) +PG_KEYWORD("extension", EXTENSION, UNRESERVED_KEYWORD) PG_KEYWORD("external", EXTERNAL, UNRESERVED_KEYWORD) PG_KEYWORD("extract", EXTRACT, COL_NAME_KEYWORD) PG_KEYWORD("false", FALSE_P, RESERVED_KEYWORD) @@ -397,6 +399,7 @@ PG_KEYWORD("user", USER, RESERVED_KEYWORD) PG_KEYWORD("using", USING, RESERVED_KEYWORD) PG_KEYWORD("vacuum", VACUUM, UNRESERVED_KEYWORD) PG_KEYWORD("valid", VALID, UNRESERVED_KEYWORD) +PG_KEYWORD("validate", VALIDATE, UNRESERVED_KEYWORD) PG_KEYWORD("validator", VALIDATOR, UNRESERVED_KEYWORD) PG_KEYWORD("value", VALUE_P, UNRESERVED_KEYWORD) PG_KEYWORD("values", VALUES, COL_NAME_KEYWORD) diff --git a/src/include/parser/parse_agg.h b/src/include/parser/parse_agg.h index 716c3d2d13..76d806dd40 100644 --- a/src/include/parser/parse_agg.h +++ b/src/include/parser/parse_agg.h @@ -30,6 +30,7 @@ extern void build_aggregate_fnexprs(Oid *agg_input_types, Oid agg_result_type, Oid transfn_oid, Oid finalfn_oid, + Oid collation, Expr **transfnexpr, Expr **finalfnexpr); diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h index ceaff2f9a9..9f79ad89d4 100644 --- a/src/include/parser/parse_coerce.h +++ b/src/include/parser/parse_coerce.h @@ -87,4 +87,6 @@ extern CoercionPathType find_coercion_pathway(Oid targetTypeId, extern CoercionPathType find_typmod_coercion_function(Oid typeId, Oid *funcid); +extern Oid select_common_collation(ParseState *pstate, List *exprs, bool none_ok); + #endif /* PARSE_COERCE_H */ diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index edae0527e6..a1511cbe64 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -145,6 +145,7 @@ extern ArrayRef *transformArraySubscripts(ParseState *pstate, Oid arrayType, Oid elementType, int32 arrayTypMod, + Oid arrayColl, List *indirection, Node *assignFrom); extern Const *make_const(ParseState *pstate, Value *value, int location); diff --git a/src/include/parser/parse_type.h b/src/include/parser/parse_type.h index dadcde790c..8621ab678d 100644 --- a/src/include/parser/parse_type.h +++ b/src/include/parser/parse_type.h @@ -20,12 +20,17 @@ typedef HeapTuple Type; extern Type LookupTypeName(ParseState *pstate, const TypeName *typeName, - int32 *typmod_p); + int32 *typmod_p, Oid *collid_p); extern Type typenameType(ParseState *pstate, const TypeName *typeName, - int32 *typmod_p); + int32 *typmod_p, Oid *collid_p); + +extern Oid LookupCollation(ParseState *pstate, List *collnames, int location); + extern Oid typenameTypeId(ParseState *pstate, const TypeName *typeName); extern void typenameTypeIdAndMod(ParseState *pstate, const TypeName *typeName, - Oid *typeid_p, int32 *typmod_p); + Oid *typeid_p, int32 *typmod_p); +extern void typenameTypeIdModColl(ParseState *pstate, const TypeName *typeName, + Oid *typeid_p, int32 *typmod_p, Oid *collid_p); extern char *TypeNameToString(const TypeName *typeName); extern char *TypeNameListToString(List *typenames); diff --git a/src/include/parser/parsetree.h b/src/include/parser/parsetree.h index efdbc1ab81..b0aebd5ef8 100644 --- a/src/include/parser/parsetree.h +++ b/src/include/parser/parsetree.h @@ -52,7 +52,7 @@ extern char *get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum); * type and typemod info for that attribute of that RTE. */ extern void get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, - Oid *vartype, int32 *vartypmod); + Oid *vartype, int32 *vartypmod, Oid *varcollid); /* * Check whether an attribute of an RTE has been dropped (note that diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index 5d84350c45..3ee1d077a5 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -330,6 +330,9 @@ */ #undef HAVE_LL_CONSTANTS +/* Define to 1 if the system has the type `locale_t'. */ +#undef HAVE_LOCALE_T + /* Define to 1 if `long int' works and is 64 bits. */ #undef HAVE_LONG_INT_64 @@ -665,6 +668,9 @@ /* Define to build with Kerberos 5 support. (--with-krb5) */ #undef KRB5 +/* Define to 1 if `locale_t' requires <xlocale.h>. */ +#undef LOCALE_T_IN_XLOCALE + /* Define as the maximum alignment requirement of any C data type. */ #undef MAXIMUM_ALIGNOF diff --git a/src/include/pgstat.h b/src/include/pgstat.h index 9f4e0cac9b..0414b19868 100644 --- a/src/include/pgstat.h +++ b/src/include/pgstat.h @@ -484,7 +484,7 @@ typedef union PgStat_Msg * ------------------------------------------------------------ */ -#define PGSTAT_FILE_FORMAT_ID 0x01A5BC98 +#define PGSTAT_FILE_FORMAT_ID 0x01A5BC99 /* ---------- * PgStat_StatDBEntry The collector's data per database @@ -508,6 +508,7 @@ typedef struct PgStat_StatDBEntry PgStat_Counter n_conflict_snapshot; PgStat_Counter n_conflict_bufferpin; PgStat_Counter n_conflict_startup_deadlock; + TimestampTz stat_reset_timestamp; /* @@ -584,6 +585,7 @@ typedef struct PgStat_GlobalStats PgStat_Counter buf_written_backend; PgStat_Counter buf_fsync_backend; PgStat_Counter buf_alloc; + TimestampTz stat_reset_timestamp; } PgStat_GlobalStats; diff --git a/src/include/port.h b/src/include/port.h index 2020a26060..9d08b392ce 100644 --- a/src/include/port.h +++ b/src/include/port.h @@ -42,6 +42,7 @@ extern void join_path_components(char *ret_path, extern void canonicalize_path(char *path); extern void make_native_path(char *path); extern bool path_contains_parent_reference(const char *path); +extern bool path_is_relative_and_below_cwd(const char *path); extern bool path_is_prefix_of_path(const char *path1, const char *path2); extern const char *get_progname(const char *argv0); extern void get_share_path(const char *my_exec_path, char *ret_path); @@ -77,13 +78,7 @@ extern void pgfnames_cleanup(char **filenames); #else #define IS_DIR_SEP(ch) ((ch) == '/' || (ch) == '\\') -/* - * On Win32, a drive letter _not_ followed by a slash, e.g. 'E:abc', is - * relative to the cwd on that drive, or the drive's root directory - * if that drive has no cwd. Because the path itself cannot tell us - * which is the case, we have to assume the worst, i.e. that it is not - * absolute; this check is done by IS_DIR_SEP(filename[2]). - */ +/* See path_is_relative_and_below_cwd() for how we handle 'E:abc'. */ #define is_absolute_path(filename) \ ( \ IS_DIR_SEP((filename)[0]) || \ @@ -461,7 +456,7 @@ extern void qsort_arg(void *base, size_t nel, size_t elsize, qsort_arg_comparator cmp, void *arg); /* port/chklocale.c */ -extern int pg_get_encoding_from_locale(const char *ctype); +extern int pg_get_encoding_from_locale(const char *ctype, bool write_message); /* port/inet_net_ntop.c */ extern char *inet_net_ntop(int af, const void *src, int bits, diff --git a/src/include/replication/walprotocol.h b/src/include/replication/walprotocol.h index 199385120a..da94b6b2f3 100644 --- a/src/include/replication/walprotocol.h +++ b/src/include/replication/walprotocol.h @@ -40,6 +40,36 @@ typedef struct } WalDataMessageHeader; /* + * Reply message from standby (message type 'r'). This is wrapped within + * a CopyData message at the FE/BE protocol level. + * + * Note that the data length is not specified here. + */ +typedef struct +{ + /* + * The xlog locations that have been written, flushed, and applied + * by standby-side. These may be invalid if the standby-side is unable + * to or chooses not to report these. + */ + XLogRecPtr write; + XLogRecPtr flush; + XLogRecPtr apply; + + /* + * The current xmin and epoch from the standby, for Hot Standby feedback. + * This may be invalid if the standby-side does not support feedback, + * or Hot Standby is not yet available. + */ + TransactionId xmin; + uint32 epoch; + + + /* Sender's system clock at the time of transmission */ + TimestampTz sendTime; +} StandbyReplyMessage; + +/* * Maximum data payload in a WAL data message. Must be >= XLOG_BLCKSZ. * * We don't have a good idea of what a good value would be; there's some diff --git a/src/include/replication/walreceiver.h b/src/include/replication/walreceiver.h index 24ad43839f..9137b861c7 100644 --- a/src/include/replication/walreceiver.h +++ b/src/include/replication/walreceiver.h @@ -17,6 +17,8 @@ #include "pgtime.h" extern bool am_walreceiver; +extern int wal_receiver_status_interval; +extern bool hot_standby_feedback; /* * MAXCONNINFO: maximum size of a connection string. diff --git a/src/include/replication/walsender.h b/src/include/replication/walsender.h index 9a196ab1c8..5843307c9d 100644 --- a/src/include/replication/walsender.h +++ b/src/include/replication/walsender.h @@ -35,7 +35,17 @@ typedef struct WalSnd WalSndState state; /* this walsender's state */ XLogRecPtr sentPtr; /* WAL has been sent up to this point */ - slock_t mutex; /* locks shared variables shown above */ + /* + * The xlog locations that have been written, flushed, and applied + * by standby-side. These may be invalid if the standby-side has not + * offered values yet. + */ + XLogRecPtr write; + XLogRecPtr flush; + XLogRecPtr apply; + + /* Protects shared variables shown above. */ + slock_t mutex; /* * Latch used by backends to wake up this walsender when it has work diff --git a/src/include/storage/predicate_internals.h b/src/include/storage/predicate_internals.h index 41aa70fdfa..32e9a1bc21 100644 --- a/src/include/storage/predicate_internals.h +++ b/src/include/storage/predicate_internals.h @@ -33,7 +33,7 @@ typedef uint64 SerCommitSeqNo; * at that point. It's earlier than all normal sequence numbers, * and is only used by recovered prepared transactions */ -#define InvalidSerCommitSeqNo UINT64_MAX +#define InvalidSerCommitSeqNo ((SerCommitSeqNo) UINT64CONST(0xFFFFFFFFFFFFFFFF)) #define RecoverySerCommitSeqNo ((SerCommitSeqNo) 1) #define FirstNormalSerCommitSeqNo ((SerCommitSeqNo) 2) diff --git a/src/include/tsearch/ts_utils.h b/src/include/tsearch/ts_utils.h index 1bd4034488..9c1019f49a 100644 --- a/src/include/tsearch/ts_utils.h +++ b/src/include/tsearch/ts_utils.h @@ -149,6 +149,9 @@ extern Datum gin_cmp_tslexeme(PG_FUNCTION_ARGS); extern Datum gin_cmp_prefix(PG_FUNCTION_ARGS); extern Datum gin_extract_tsquery(PG_FUNCTION_ARGS); extern Datum gin_tsquery_consistent(PG_FUNCTION_ARGS); +extern Datum gin_extract_tsvector_2args(PG_FUNCTION_ARGS); +extern Datum gin_extract_tsquery_5args(PG_FUNCTION_ARGS); +extern Datum gin_tsquery_consistent_6args(PG_FUNCTION_ARGS); /* * Possible strategy numbers for indexes diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h index aac7442710..1e9cf7fbed 100644 --- a/src/include/utils/acl.h +++ b/src/include/utils/acl.h @@ -188,6 +188,7 @@ typedef enum AclObjectKind ACL_KIND_NAMESPACE, /* pg_namespace */ ACL_KIND_OPCLASS, /* pg_opclass */ ACL_KIND_OPFAMILY, /* pg_opfamily */ + ACL_KIND_COLLATION, /* pg_collation */ ACL_KIND_CONVERSION, /* pg_conversion */ ACL_KIND_TABLESPACE, /* pg_tablespace */ ACL_KIND_TSDICTIONARY, /* pg_ts_dict */ @@ -309,6 +310,7 @@ extern bool pg_tablespace_ownercheck(Oid spc_oid, Oid roleid); extern bool pg_opclass_ownercheck(Oid opc_oid, Oid roleid); extern bool pg_opfamily_ownercheck(Oid opf_oid, Oid roleid); extern bool pg_database_ownercheck(Oid db_oid, Oid roleid); +extern bool pg_collation_ownercheck(Oid coll_oid, Oid roleid); extern bool pg_conversion_ownercheck(Oid conv_oid, Oid roleid); extern bool pg_ts_dict_ownercheck(Oid dict_oid, Oid roleid); extern bool pg_ts_config_ownercheck(Oid cfg_oid, Oid roleid); diff --git a/src/include/utils/array.h b/src/include/utils/array.h index 7f7e744cb1..6bc280f142 100644 --- a/src/include/utils/array.h +++ b/src/include/utils/array.h @@ -114,6 +114,9 @@ typedef struct ArrayMapState ArrayMetaState ret_extra; } ArrayMapState; +/* ArrayIteratorData is private in arrayfuncs.c */ +typedef struct ArrayIteratorData *ArrayIterator; + /* * fmgr macros for array objects */ @@ -254,6 +257,10 @@ extern Datum makeArrayResult(ArrayBuildState *astate, extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims, int *dims, int *lbs, MemoryContext rcontext, bool release); +extern ArrayIterator array_create_iterator(ArrayType *arr, int slice_ndim); +extern bool array_iterate(ArrayIterator iterator, Datum *value, bool *isnull); +extern void array_free_iterator(ArrayIterator iterator); + /* * prototypes for functions defined in arrayutils.c */ diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index eaaf7d0651..ac93cc6196 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -440,6 +440,8 @@ extern Datum pg_relation_filenode(PG_FUNCTION_ARGS); extern Datum pg_relation_filepath(PG_FUNCTION_ARGS); /* genfile.c */ +extern bytea *read_binary_file(const char *filename, + int64 seek_offset, int64 bytes_to_read); extern Datum pg_stat_file(PG_FUNCTION_ARGS); extern Datum pg_read_file(PG_FUNCTION_ARGS); extern Datum pg_read_file_all(PG_FUNCTION_ARGS); @@ -625,6 +627,8 @@ extern List *deparse_context_for_planstate(Node *planstate, List *ancestors, extern const char *quote_identifier(const char *ident); extern char *quote_qualified_identifier(const char *qualifier, const char *ident); +extern char *generate_collation_name(Oid collid); + /* tid.c */ extern Datum tidin(PG_FUNCTION_ARGS); @@ -716,7 +720,7 @@ extern Datum textoverlay(PG_FUNCTION_ARGS); extern Datum textoverlay_no_len(PG_FUNCTION_ARGS); extern Datum name_text(PG_FUNCTION_ARGS); extern Datum text_name(PG_FUNCTION_ARGS); -extern int varstr_cmp(char *arg1, int len1, char *arg2, int len2); +extern int varstr_cmp(char *arg1, int len1, char *arg2, int len2, Oid collid); extern List *textToQualifiedNameList(text *textval); extern bool SplitIdentifierString(char *rawstring, char separator, List **namelist); @@ -1051,6 +1055,7 @@ extern Datum window_nth_value(PG_FUNCTION_ARGS); /* access/gin/ginarrayproc.c */ extern Datum ginarrayextract(PG_FUNCTION_ARGS); +extern Datum ginarrayextract_2args(PG_FUNCTION_ARGS); extern Datum ginqueryarrayextract(PG_FUNCTION_ARGS); extern Datum ginarrayconsistent(PG_FUNCTION_ARGS); @@ -1063,6 +1068,12 @@ extern Datum pg_describe_object(PG_FUNCTION_ARGS); /* commands/constraint.c */ extern Datum unique_key_recheck(PG_FUNCTION_ARGS); +/* commands/extension.c */ +extern Datum pg_available_extensions(PG_FUNCTION_ARGS); +extern Datum pg_available_extension_versions(PG_FUNCTION_ARGS); +extern Datum pg_extension_update_paths(PG_FUNCTION_ARGS); +extern Datum pg_extension_config_dump(PG_FUNCTION_ARGS); + /* commands/prepare.c */ extern Datum pg_prepared_statement(PG_FUNCTION_ARGS); diff --git a/src/include/utils/formatting.h b/src/include/utils/formatting.h index 7cd3be1131..175d396297 100644 --- a/src/include/utils/formatting.h +++ b/src/include/utils/formatting.h @@ -20,9 +20,9 @@ #include "fmgr.h" -extern char *str_tolower(const char *buff, size_t nbytes); -extern char *str_toupper(const char *buff, size_t nbytes); -extern char *str_initcap(const char *buff, size_t nbytes); +extern char *str_tolower(const char *buff, size_t nbytes, Oid collid); +extern char *str_toupper(const char *buff, size_t nbytes, Oid collid); +extern char *str_initcap(const char *buff, size_t nbytes, Oid collid); extern Datum timestamp_to_char(PG_FUNCTION_ARGS); extern Datum timestamptz_to_char(PG_FUNCTION_ARGS); diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index 26c241c348..22dfd58f0a 100644 --- a/src/include/utils/lsyscache.h +++ b/src/include/utils/lsyscache.h @@ -60,8 +60,10 @@ extern char *get_relid_attribute_name(Oid relid, AttrNumber attnum); extern AttrNumber get_attnum(Oid relid, const char *attname); extern Oid get_atttype(Oid relid, AttrNumber attnum); extern int32 get_atttypmod(Oid relid, AttrNumber attnum); +extern Oid get_attcollation(Oid relid, AttrNumber attnum); extern void get_atttypetypmod(Oid relid, AttrNumber attnum, Oid *typid, int32 *typmod); +extern char *get_collation_name(Oid colloid); extern char *get_constraint_name(Oid conoid); extern Oid get_opclass_family(Oid opclass); extern Oid get_opclass_input_type(Oid opclass); @@ -124,6 +126,8 @@ extern void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena); extern void getTypeBinaryInputInfo(Oid type, Oid *typReceive, Oid *typIOParam); extern void getTypeBinaryOutputInfo(Oid type, Oid *typSend, bool *typIsVarlena); extern Oid get_typmodin(Oid typid); +extern Oid get_typcollation(Oid typid); +extern bool type_is_collatable(Oid typid); extern Oid getBaseType(Oid typid); extern Oid getBaseTypeAndTypmod(Oid typid, int32 *typmod); extern int32 get_typavgwidth(Oid typid, int32 typmod); diff --git a/src/include/utils/memutils.h b/src/include/utils/memutils.h index 5ee533b30d..7c1202478e 100644 --- a/src/include/utils/memutils.h +++ b/src/include/utils/memutils.h @@ -30,7 +30,9 @@ * be summarily denied. * * XXX This is deliberately chosen to correspond to the limiting size - * of varlena objects under TOAST. See VARATT_MASK_SIZE in postgres.h. + * of varlena objects under TOAST. See VARSIZE_4B() and related macros + * in postgres.h. Many datatypes assume that any allocatable size can + * be represented in a varlena header. * * XXX Also, various places in aset.c assume they can compute twice an * allocation's size without overflow, so beware of raising this. diff --git a/src/include/utils/pg_locale.h b/src/include/utils/pg_locale.h index d56bf38d2d..4c72fd0dc6 100644 --- a/src/include/utils/pg_locale.h +++ b/src/include/utils/pg_locale.h @@ -13,6 +13,9 @@ #define _PG_LOCALE_ #include <locale.h> +#ifdef LOCALE_T_IN_XLOCALE +#include <xlocale.h> +#endif #include "utils/guc.h" @@ -42,8 +45,8 @@ extern const char *locale_time_assign(const char *value, extern bool check_locale(int category, const char *locale); extern char *pg_perm_setlocale(int category, const char *locale); -extern bool lc_collate_is_c(void); -extern bool lc_ctype_is_c(void); +extern bool lc_collate_is_c(Oid collation); +extern bool lc_ctype_is_c(Oid collation); /* * Return the POSIX lconv struct (contains number/money formatting @@ -53,4 +56,20 @@ extern struct lconv *PGLC_localeconv(void); extern void cache_locale_time(void); + +/* + * We define our own wrapper around locale_t so we can keep the same + * function signatures for all builds, while not having to create a + * fake version of the standard type locale_t in the global namespace. + * The fake version of pg_locale_t can be checked for truth; that's + * about all it will be needed for. + */ +#ifdef HAVE_LOCALE_T +typedef locale_t pg_locale_t; +#else +typedef int pg_locale_t; +#endif + +extern pg_locale_t pg_newlocale_from_collation(Oid collid); + #endif /* _PG_LOCALE_ */ diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h index 5498df93ca..d47c4beaa5 100644 --- a/src/include/utils/rel.h +++ b/src/include/utils/rel.h @@ -203,6 +203,7 @@ typedef struct RelationData Oid *rd_exclprocs; /* OIDs of exclusion ops' procs, if any */ uint16 *rd_exclstrats; /* exclusion ops' strategy numbers, if any */ void *rd_amcache; /* available for use by index AM */ + Oid *rd_indcollation; /* OIDs of index collations */ /* * Hack for CLUSTER, rewriting ALTER TABLE, etc: when writing a new diff --git a/src/include/utils/selfuncs.h b/src/include/utils/selfuncs.h index e9913aa049..baf6d8caf8 100644 --- a/src/include/utils/selfuncs.h +++ b/src/include/utils/selfuncs.h @@ -74,6 +74,7 @@ typedef struct VariableStatData Oid vartype; /* exposed type of expression */ Oid atttype; /* type to pass to get_attstatsslot */ int32 atttypmod; /* typmod to pass to get_attstatsslot */ + Oid attcollation; /* collation of the variable */ bool isunique; /* true if matched to a unique index */ } VariableStatData; @@ -178,7 +179,7 @@ extern Selectivity rowcomparesel(PlannerInfo *root, int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo); extern void mergejoinscansel(PlannerInfo *root, Node *clause, - Oid opfamily, int strategy, bool nulls_first, + Oid opfamily, Oid collation, int strategy, bool nulls_first, Selectivity *leftstart, Selectivity *leftend, Selectivity *rightstart, Selectivity *rightend); diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h index 66515a8f82..55d22303a7 100644 --- a/src/include/utils/syscache.h +++ b/src/include/utils/syscache.h @@ -44,6 +44,8 @@ enum SysCacheIdentifier CASTSOURCETARGET, CLAAMNAMENSP, CLAOID, + COLLNAMEENCNSP, + COLLOID, CONDEFAULT, CONNAMENSP, CONSTROID, diff --git a/src/include/utils/tuplesort.h b/src/include/utils/tuplesort.h index b38c260d50..59505d4b57 100644 --- a/src/include/utils/tuplesort.h +++ b/src/include/utils/tuplesort.h @@ -60,7 +60,7 @@ typedef struct Tuplesortstate Tuplesortstate; extern Tuplesortstate *tuplesort_begin_heap(TupleDesc tupDesc, int nkeys, AttrNumber *attNums, - Oid *sortOperators, bool *nullsFirstFlags, + Oid *sortOperators, Oid *collations, bool *nullsFirstFlags, int workMem, bool randomAccess); extern Tuplesortstate *tuplesort_begin_cluster(TupleDesc tupDesc, Relation indexRel, @@ -72,7 +72,7 @@ extern Tuplesortstate *tuplesort_begin_index_hash(Relation indexRel, uint32 hash_mask, int workMem, bool randomAccess); extern Tuplesortstate *tuplesort_begin_datum(Oid datumType, - Oid sortOperator, bool nullsFirstFlag, + Oid sortOperator, Oid sortCollation, bool nullsFirstFlag, int workMem, bool randomAccess); extern void tuplesort_set_bound(Tuplesortstate *state, int64 bound); diff --git a/src/interfaces/ecpg/test/expected/sql-fetch.stderr b/src/interfaces/ecpg/test/expected/sql-fetch.stderr index 038b61e71c..228421835d 100644 --- a/src/interfaces/ecpg/test/expected/sql-fetch.stderr +++ b/src/interfaces/ecpg/test/expected/sql-fetch.stderr @@ -138,10 +138,10 @@ [NO_PID]: sqlca: code: 0, state: 00000 [NO_PID]: ecpg_execute on line 53: using PQexec [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: ecpg_check_PQresult on line 53: bad response - ERROR: cannot drop "my_table" because it is being used by active queries in this session +[NO_PID]: ecpg_check_PQresult on line 53: bad response - ERROR: cannot DROP TABLE "my_table" because it is being used by active queries in this session [NO_PID]: sqlca: code: 0, state: 00000 -[NO_PID]: raising sqlstate 55006 (sqlcode -400): cannot drop "my_table" because it is being used by active queries in this session on line 53 +[NO_PID]: raising sqlstate 55006 (sqlcode -400): cannot DROP TABLE "my_table" because it is being used by active queries in this session on line 53 [NO_PID]: sqlca: code: -400, state: 55006 -SQL error: cannot drop "my_table" because it is being used by active queries in this session on line 53 +SQL error: cannot DROP TABLE "my_table" because it is being used by active queries in this session on line 53 [NO_PID]: ecpg_finish: connection regress1 closed [NO_PID]: sqlca: code: 0, state: 00000 diff --git a/src/makefiles/pgxs.mk b/src/makefiles/pgxs.mk index 912578a84d..87ade0a420 100644 --- a/src/makefiles/pgxs.mk +++ b/src/makefiles/pgxs.mk @@ -17,16 +17,18 @@ # # Set one of these three variables to specify what is built: # -# MODULES -- list of shared objects to be built from source files with -# same stem (do not include suffix in this list) -# MODULE_big -- a shared object to build from multiple source files +# MODULES -- list of shared-library objects to be built from source files +# with same stem (do not include library suffixes in this list) +# MODULE_big -- a shared library to build from multiple source files # (list object files in OBJS) -# PROGRAM -- a binary program to build (list object files in OBJS) +# PROGRAM -- an executable program to build (list object files in OBJS) # # The following variables can also be set: # -# MODULEDIR -- subdirectory into which DATA and DOCS files should be -# installed (if not set, default is "contrib") +# EXTENSION -- name of extension (there must be a $EXTENSION.control file) +# MODULEDIR -- subdirectory of $PREFIX/share into which DATA and DOCS files +# should be installed (if not set, default is "extension" if EXTENSION +# is set, or "contrib" if not) # DATA -- random files to install into $PREFIX/share/$MODULEDIR # DATA_built -- random files to install into $PREFIX/share/$MODULEDIR, # which need to be built first @@ -71,18 +73,23 @@ override CFLAGS += $(CFLAGS_SL) endif ifdef MODULEDIR -datamoduledir = $(MODULEDIR) -docmoduledir = $(MODULEDIR) +datamoduledir := $(MODULEDIR) +docmoduledir := $(MODULEDIR) else -datamoduledir = contrib -docmoduledir = contrib +ifdef EXTENSION +datamoduledir := extension +docmoduledir := extension +else +datamoduledir := contrib +docmoduledir := contrib +endif endif ifdef PG_CPPFLAGS override CPPFLAGS := $(PG_CPPFLAGS) $(CPPFLAGS) endif -all: $(PROGRAM) $(DATA_built) $(SCRIPTS_built) $(addsuffix $(DLSUFFIX), $(MODULES)) +all: $(PROGRAM) $(DATA_built) $(SCRIPTS_built) $(addsuffix $(DLSUFFIX), $(MODULES)) $(addsuffix .control, $(EXTENSION)) ifdef MODULE_big # shared library parameters @@ -95,6 +102,12 @@ endif # MODULE_big install: all installdirs +ifneq (,$(EXTENSION)) + @for file in $(addprefix $(srcdir)/, $(addsuffix .control, $(EXTENSION))); do \ + echo "$(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/extension'"; \ + $(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/extension'; \ + done +endif # EXTENSION ifneq (,$(DATA)$(DATA_built)) @for file in $(addprefix $(srcdir)/, $(DATA)) $(DATA_built); do \ echo "$(INSTALL_DATA) $$file '$(DESTDIR)$(datadir)/$(datamoduledir)'"; \ @@ -167,6 +180,9 @@ endif # MODULE_big uninstall: +ifneq (,$(EXTENSION)) + rm -f $(addprefix '$(DESTDIR)$(datadir)/extension'/, $(notdir $(addsuffix .control, $(EXTENSION)))) +endif ifneq (,$(DATA)$(DATA_built)) rm -f $(addprefix '$(DESTDIR)$(datadir)/$(datamoduledir)'/, $(notdir $(DATA) $(DATA_built))) endif @@ -215,8 +231,7 @@ ifdef EXTRA_CLEAN endif ifdef REGRESS # things created by various check targets - rm -rf results tmp_check log - rm -f regression.diffs regression.out regress.out run_check.out + rm -rf $(pg_regress_clean_files) ifeq ($(PORTNAME), win) rm -f regress.def endif @@ -264,12 +279,11 @@ endif # against installed postmaster installcheck: submake - $(top_builddir)/src/test/regress/pg_regress --inputdir=$(srcdir) --psqldir=$(PSQLDIR) $(REGRESS_OPTS) $(REGRESS) + $(pg_regress_installcheck) $(REGRESS_OPTS) $(REGRESS) # in-tree test doesn't work yet (no way to install my shared library) #check: all submake -# $(top_builddir)/src/test/regress/pg_regress --temp-install \ -# --top-builddir=$(top_builddir) $(REGRESS_OPTS) $(REGRESS) +# $(pg_regress_check) $(REGRESS_OPTS) $(REGRESS) check: @echo "'make check' is not supported." @echo "Do 'make install', then 'make installcheck' instead." diff --git a/src/pl/plperl/.gitignore b/src/pl/plperl/.gitignore index c04f42ba07..1a798733ac 100644 --- a/src/pl/plperl/.gitignore +++ b/src/pl/plperl/.gitignore @@ -4,4 +4,6 @@ /plperl_opmask.h # Generated subdirectories +/log/ /results/ +/tmp_check/ diff --git a/src/pl/plperl/GNUmakefile b/src/pl/plperl/GNUmakefile index 85cf9a8f93..01e585e428 100644 --- a/src/pl/plperl/GNUmakefile +++ b/src/pl/plperl/GNUmakefile @@ -76,8 +76,11 @@ installdirs: installdirs-lib uninstall: uninstall-lib +check: submake + $(pg_regress_check) $(REGRESS_OPTS) $(REGRESS) + installcheck: submake - $(top_builddir)/src/test/regress/pg_regress --inputdir=$(srcdir) --psqldir=$(PSQLDIR) $(REGRESS_OPTS) $(REGRESS) + $(pg_regress_installcheck) $(REGRESS_OPTS) $(REGRESS) .PHONY: submake submake: @@ -85,8 +88,7 @@ submake: clean distclean maintainer-clean: clean-lib rm -f SPI.c Util.c $(OBJS) perlchunks.h plperl_opmask.h - rm -rf results - rm -f regression.diffs regression.out + rm -rf $(pg_regress_clean_files) else # can't build diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y index eae9bbad6c..0ef6b5d48c 100644 --- a/src/pl/plpgsql/src/gram.y +++ b/src/pl/plpgsql/src/gram.y @@ -175,7 +175,7 @@ static List *read_raise_options(void); %type <expr> expr_until_then expr_until_loop opt_expr_until_when %type <expr> opt_exitcond -%type <ival> assign_var +%type <ival> assign_var foreach_slice %type <var> cursor_variable %type <datum> decl_cursor_arg %type <forvariable> for_variable @@ -190,7 +190,7 @@ static List *read_raise_options(void); %type <stmt> stmt_return stmt_raise stmt_execsql %type <stmt> stmt_dynexecute stmt_for stmt_perform stmt_getdiag %type <stmt> stmt_open stmt_fetch stmt_move stmt_close stmt_null -%type <stmt> stmt_case +%type <stmt> stmt_case stmt_foreach_a %type <list> proc_exceptions %type <exception_block> exception_sect @@ -239,6 +239,7 @@ static List *read_raise_options(void); %token <keyword> K_ABSOLUTE %token <keyword> K_ALIAS %token <keyword> K_ALL +%token <keyword> K_ARRAY %token <keyword> K_BACKWARD %token <keyword> K_BEGIN %token <keyword> K_BY @@ -264,6 +265,7 @@ static List *read_raise_options(void); %token <keyword> K_FETCH %token <keyword> K_FIRST %token <keyword> K_FOR +%token <keyword> K_FOREACH %token <keyword> K_FORWARD %token <keyword> K_FROM %token <keyword> K_GET @@ -298,6 +300,7 @@ static List *read_raise_options(void); %token <keyword> K_ROWTYPE %token <keyword> K_ROW_COUNT %token <keyword> K_SCROLL +%token <keyword> K_SLICE %token <keyword> K_SQLSTATE %token <keyword> K_STRICT %token <keyword> K_THEN @@ -739,6 +742,8 @@ proc_stmt : pl_block ';' { $$ = $1; } | stmt_for { $$ = $1; } + | stmt_foreach_a + { $$ = $1; } | stmt_exit { $$ = $1; } | stmt_return @@ -1386,6 +1391,58 @@ for_variable : T_DATUM } ; +stmt_foreach_a : opt_block_label K_FOREACH for_variable foreach_slice K_IN K_ARRAY expr_until_loop loop_body + { + PLpgSQL_stmt_foreach_a *new; + + new = palloc0(sizeof(PLpgSQL_stmt_foreach_a)); + new->cmd_type = PLPGSQL_STMT_FOREACH_A; + new->lineno = plpgsql_location_to_lineno(@2); + new->label = $1; + new->slice = $4; + new->expr = $7; + new->body = $8.stmts; + + if ($3.rec) + { + new->varno = $3.rec->dno; + check_assignable((PLpgSQL_datum *) $3.rec, @3); + } + else if ($3.row) + { + new->varno = $3.row->dno; + check_assignable((PLpgSQL_datum *) $3.row, @3); + } + else if ($3.scalar) + { + new->varno = $3.scalar->dno; + check_assignable($3.scalar, @3); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("loop variable of FOREACH must be a known variable or list of variables"), + parser_errposition(@3))); + } + + check_labels($1, $8.end_label, $8.end_label_location); + plpgsql_ns_pop(); + + $$ = (PLpgSQL_stmt *) new; + } + ; + +foreach_slice : + { + $$ = 0; + } + | K_SLICE ICONST + { + $$ = $2; + } + ; + stmt_exit : exit_type opt_label opt_exitcond { PLpgSQL_stmt_exit *new; @@ -2035,6 +2092,7 @@ any_identifier : T_WORD unreserved_keyword : K_ABSOLUTE | K_ALIAS + | K_ARRAY | K_BACKWARD | K_CONSTANT | K_CURSOR @@ -2063,6 +2121,7 @@ unreserved_keyword : | K_ROW_COUNT | K_ROWTYPE | K_SCROLL + | K_SLICE | K_SQLSTATE | K_TYPE | K_USE_COLUMN diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c index 41188a2369..d2b3862ec7 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c @@ -1565,7 +1565,7 @@ plpgsql_parse_wordtype(char *ident) * Word wasn't found in the namespace stack. Try to find a data type with * that name, but ignore shell types and complex types. */ - typeTup = LookupTypeName(NULL, makeTypeName(ident), NULL); + typeTup = LookupTypeName(NULL, makeTypeName(ident), NULL, NULL); if (typeTup) { Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup); diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index b685841d97..7af6eee088 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -107,6 +107,8 @@ static int exec_stmt_fors(PLpgSQL_execstate *estate, PLpgSQL_stmt_fors *stmt); static int exec_stmt_forc(PLpgSQL_execstate *estate, PLpgSQL_stmt_forc *stmt); +static int exec_stmt_foreach_a(PLpgSQL_execstate *estate, + PLpgSQL_stmt_foreach_a *stmt); static int exec_stmt_open(PLpgSQL_execstate *estate, PLpgSQL_stmt_open *stmt); static int exec_stmt_fetch(PLpgSQL_execstate *estate, @@ -1312,6 +1314,10 @@ exec_stmt(PLpgSQL_execstate *estate, PLpgSQL_stmt *stmt) rc = exec_stmt_forc(estate, (PLpgSQL_stmt_forc *) stmt); break; + case PLPGSQL_STMT_FOREACH_A: + rc = exec_stmt_foreach_a(estate, (PLpgSQL_stmt_foreach_a *) stmt); + break; + case PLPGSQL_STMT_EXIT: rc = exec_stmt_exit(estate, (PLpgSQL_stmt_exit *) stmt); break; @@ -2028,6 +2034,185 @@ exec_stmt_forc(PLpgSQL_execstate *estate, PLpgSQL_stmt_forc *stmt) /* ---------- + * exec_stmt_foreach_a Loop over elements or slices of an array + * + * When looping over elements, the loop variable is the same type that the + * array stores (eg: integer), when looping through slices, the loop variable + * is an array of size and dimensions to match the size of the slice. + * ---------- + */ +static int +exec_stmt_foreach_a(PLpgSQL_execstate *estate, PLpgSQL_stmt_foreach_a *stmt) +{ + ArrayType *arr; + Oid arrtype; + PLpgSQL_datum *loop_var; + Oid loop_var_elem_type; + bool found = false; + int rc = PLPGSQL_RC_OK; + ArrayIterator array_iterator; + Oid iterator_result_type; + Datum value; + bool isnull; + + /* get the value of the array expression */ + value = exec_eval_expr(estate, stmt->expr, &isnull, &arrtype); + if (isnull) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("FOREACH expression must not be NULL"))); + + /* check the type of the expression - must be an array */ + if (!OidIsValid(get_element_type(arrtype))) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("FOREACH expression must yield an array, not type %s", + format_type_be(arrtype)))); + + /* + * We must copy the array, else it will disappear in exec_eval_cleanup. + * This is annoying, but cleanup will certainly happen while running the + * loop body, so we have little choice. + */ + arr = DatumGetArrayTypePCopy(value); + + /* Clean up any leftover temporary memory */ + exec_eval_cleanup(estate); + + /* Slice dimension must be less than or equal to array dimension */ + if (stmt->slice < 0 || stmt->slice > ARR_NDIM(arr)) + ereport(ERROR, + (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), + errmsg("slice dimension (%d) is out of the valid range 0..%d", + stmt->slice, ARR_NDIM(arr)))); + + /* Set up the loop variable and see if it is of an array type */ + loop_var = estate->datums[stmt->varno]; + if (loop_var->dtype == PLPGSQL_DTYPE_REC || + loop_var->dtype == PLPGSQL_DTYPE_ROW) + { + /* + * Record/row variable is certainly not of array type, and might not + * be initialized at all yet, so don't try to get its type + */ + loop_var_elem_type = InvalidOid; + } + else + loop_var_elem_type = get_element_type(exec_get_datum_type(estate, + loop_var)); + + /* + * Sanity-check the loop variable type. We don't try very hard here, + * and should not be too picky since it's possible that exec_assign_value + * can coerce values of different types. But it seems worthwhile to + * complain if the array-ness of the loop variable is not right. + */ + if (stmt->slice > 0 && loop_var_elem_type == InvalidOid) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("FOREACH ... SLICE loop variable must be of an array type"))); + if (stmt->slice == 0 && loop_var_elem_type != InvalidOid) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("FOREACH loop variable must not be of an array type"))); + + /* Create an iterator to step through the array */ + array_iterator = array_create_iterator(arr, stmt->slice); + + /* Identify iterator result type */ + if (stmt->slice > 0) + { + /* When slicing, nominal type of result is same as array type */ + iterator_result_type = arrtype; + } + else + { + /* Without slicing, results are individual array elements */ + iterator_result_type = ARR_ELEMTYPE(arr); + } + + /* Iterate over the array elements or slices */ + while (array_iterate(array_iterator, &value, &isnull)) + { + found = true; /* looped at least once */ + + /* Assign current element/slice to the loop variable */ + exec_assign_value(estate, loop_var, value, iterator_result_type, + &isnull); + + /* In slice case, value is temporary; must free it to avoid leakage */ + if (stmt->slice > 0) + pfree(DatumGetPointer(value)); + + /* + * Execute the statements + */ + rc = exec_stmts(estate, stmt->body); + + /* Handle the return code */ + if (rc == PLPGSQL_RC_RETURN) + break; /* break out of the loop */ + else if (rc == PLPGSQL_RC_EXIT) + { + if (estate->exitlabel == NULL) + /* unlabelled exit, finish the current loop */ + rc = PLPGSQL_RC_OK; + else if (stmt->label != NULL && + strcmp(stmt->label, estate->exitlabel) == 0) + { + /* labelled exit, matches the current stmt's label */ + estate->exitlabel = NULL; + rc = PLPGSQL_RC_OK; + } + + /* + * otherwise, this is a labelled exit that does not match the + * current statement's label, if any: return RC_EXIT so that the + * EXIT continues to propagate up the stack. + */ + break; + } + else if (rc == PLPGSQL_RC_CONTINUE) + { + if (estate->exitlabel == NULL) + /* unlabelled continue, so re-run the current loop */ + rc = PLPGSQL_RC_OK; + else if (stmt->label != NULL && + strcmp(stmt->label, estate->exitlabel) == 0) + { + /* label matches named continue, so re-run loop */ + estate->exitlabel = NULL; + rc = PLPGSQL_RC_OK; + } + else + { + /* + * otherwise, this is a named continue that does not match the + * current statement's label, if any: return RC_CONTINUE so + * that the CONTINUE will propagate up the stack. + */ + break; + } + } + } + + /* Release temporary memory, including the array value */ + array_free_iterator(array_iterator); + pfree(arr); + + /* + * Set the FOUND variable to indicate the result of executing the loop + * (namely, whether we looped one or more times). This must be set here so + * that it does not interfere with the value of the FOUND variable inside + * the loop processing itself. + */ + exec_set_found(estate, found); + + return rc; +} + + +/* ---------- * exec_stmt_exit Implements EXIT and CONTINUE * * This begins the process of exiting / restarting a loop. diff --git a/src/pl/plpgsql/src/pl_funcs.c b/src/pl/plpgsql/src/pl_funcs.c index e24f71ac6c..f13e4c3db6 100644 --- a/src/pl/plpgsql/src/pl_funcs.c +++ b/src/pl/plpgsql/src/pl_funcs.c @@ -230,6 +230,8 @@ plpgsql_stmt_typename(PLpgSQL_stmt *stmt) return _("FOR over SELECT rows"); case PLPGSQL_STMT_FORC: return _("FOR over cursor"); + case PLPGSQL_STMT_FOREACH_A: + return _("FOREACH over array"); case PLPGSQL_STMT_EXIT: return "EXIT"; case PLPGSQL_STMT_RETURN: @@ -278,6 +280,7 @@ static void dump_while(PLpgSQL_stmt_while *stmt); static void dump_fori(PLpgSQL_stmt_fori *stmt); static void dump_fors(PLpgSQL_stmt_fors *stmt); static void dump_forc(PLpgSQL_stmt_forc *stmt); +static void dump_foreach_a(PLpgSQL_stmt_foreach_a *stmt); static void dump_exit(PLpgSQL_stmt_exit *stmt); static void dump_return(PLpgSQL_stmt_return *stmt); static void dump_return_next(PLpgSQL_stmt_return_next *stmt); @@ -337,6 +340,9 @@ dump_stmt(PLpgSQL_stmt *stmt) case PLPGSQL_STMT_FORC: dump_forc((PLpgSQL_stmt_forc *) stmt); break; + case PLPGSQL_STMT_FOREACH_A: + dump_foreach_a((PLpgSQL_stmt_foreach_a *) stmt); + break; case PLPGSQL_STMT_EXIT: dump_exit((PLpgSQL_stmt_exit *) stmt); break; @@ -596,6 +602,23 @@ dump_forc(PLpgSQL_stmt_forc *stmt) } static void +dump_foreach_a(PLpgSQL_stmt_foreach_a *stmt) +{ + dump_ind(); + printf("FOREACHA var %d ", stmt->varno); + if (stmt->slice != 0) + printf("SLICE %d ", stmt->slice); + printf("IN "); + dump_expr(stmt->expr); + printf("\n"); + + dump_stmts(stmt->body); + + dump_ind(); + printf(" ENDFOREACHA"); +} + +static void dump_open(PLpgSQL_stmt_open *stmt) { dump_ind(); diff --git a/src/pl/plpgsql/src/pl_scanner.c b/src/pl/plpgsql/src/pl_scanner.c index 6675184d61..e8a2628f2f 100644 --- a/src/pl/plpgsql/src/pl_scanner.c +++ b/src/pl/plpgsql/src/pl_scanner.c @@ -77,6 +77,7 @@ static const ScanKeyword reserved_keywords[] = { PG_KEYWORD("exit", K_EXIT, RESERVED_KEYWORD) PG_KEYWORD("fetch", K_FETCH, RESERVED_KEYWORD) PG_KEYWORD("for", K_FOR, RESERVED_KEYWORD) + PG_KEYWORD("foreach", K_FOREACH, RESERVED_KEYWORD) PG_KEYWORD("from", K_FROM, RESERVED_KEYWORD) PG_KEYWORD("get", K_GET, RESERVED_KEYWORD) PG_KEYWORD("if", K_IF, RESERVED_KEYWORD) @@ -105,6 +106,7 @@ static const int num_reserved_keywords = lengthof(reserved_keywords); static const ScanKeyword unreserved_keywords[] = { PG_KEYWORD("absolute", K_ABSOLUTE, UNRESERVED_KEYWORD) PG_KEYWORD("alias", K_ALIAS, UNRESERVED_KEYWORD) + PG_KEYWORD("array", K_ARRAY, UNRESERVED_KEYWORD) PG_KEYWORD("backward", K_BACKWARD, UNRESERVED_KEYWORD) PG_KEYWORD("constant", K_CONSTANT, UNRESERVED_KEYWORD) PG_KEYWORD("cursor", K_CURSOR, UNRESERVED_KEYWORD) @@ -133,6 +135,7 @@ static const ScanKeyword unreserved_keywords[] = { PG_KEYWORD("row_count", K_ROW_COUNT, UNRESERVED_KEYWORD) PG_KEYWORD("rowtype", K_ROWTYPE, UNRESERVED_KEYWORD) PG_KEYWORD("scroll", K_SCROLL, UNRESERVED_KEYWORD) + PG_KEYWORD("slice", K_SLICE, UNRESERVED_KEYWORD) PG_KEYWORD("sqlstate", K_SQLSTATE, UNRESERVED_KEYWORD) PG_KEYWORD("type", K_TYPE, UNRESERVED_KEYWORD) PG_KEYWORD("use_column", K_USE_COLUMN, UNRESERVED_KEYWORD) diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h index 0ad7e28136..7015379842 100644 --- a/src/pl/plpgsql/src/plpgsql.h +++ b/src/pl/plpgsql/src/plpgsql.h @@ -90,6 +90,7 @@ enum PLpgSQL_stmt_types PLPGSQL_STMT_FORI, PLPGSQL_STMT_FORS, PLPGSQL_STMT_FORC, + PLPGSQL_STMT_FOREACH_A, PLPGSQL_STMT_EXIT, PLPGSQL_STMT_RETURN, PLPGSQL_STMT_RETURN_NEXT, @@ -495,6 +496,18 @@ typedef struct typedef struct +{ /* FOREACH item in array loop */ + int cmd_type; + int lineno; + char *label; + int varno; /* loop target variable */ + int slice; /* slice dimension, or 0 */ + PLpgSQL_expr *expr; /* array expression */ + List *body; /* List of statements */ +} PLpgSQL_stmt_foreach_a; + + +typedef struct { /* OPEN a curvar */ int cmd_type; int lineno; diff --git a/src/pl/plpython/.gitignore b/src/pl/plpython/.gitignore index 19b6c5ba42..5dcb3ff972 100644 --- a/src/pl/plpython/.gitignore +++ b/src/pl/plpython/.gitignore @@ -1,2 +1,4 @@ # Generated subdirectories +/log/ /results/ +/tmp_check/ diff --git a/src/pl/plpython/Makefile b/src/pl/plpython/Makefile index 16d78ae0e2..3a2411bea1 100644 --- a/src/pl/plpython/Makefile +++ b/src/pl/plpython/Makefile @@ -120,13 +120,19 @@ prep3: clean3: rm -rf python3/ +check: submake prep3 + $(pg_regress_check) --inputdir=./python3 --outputdir=./python3 $(REGRESS_OPTS) $(REGRESS) + installcheck: submake prep3 - $(top_builddir)/src/test/regress/pg_regress --inputdir=./python3 --outputdir=./python3 --psqldir=$(PSQLDIR) $(REGRESS_OPTS) $(REGRESS) + $(pg_regress_installcheck) --inputdir=./python3 --outputdir=./python3 $(REGRESS_OPTS) $(REGRESS) clean: clean3 else +check: submake + $(pg_regress_check) $(REGRESS_OPTS) $(REGRESS) + installcheck: submake - $(top_builddir)/src/test/regress/pg_regress --inputdir=$(srcdir) --psqldir=$(PSQLDIR) $(REGRESS_OPTS) $(REGRESS) + $(pg_regress_installcheck) $(REGRESS_OPTS) $(REGRESS) endif .PHONY: submake @@ -135,8 +141,7 @@ submake: clean distclean maintainer-clean: clean-lib rm -f $(OBJS) - rm -rf results - rm -f regression.diffs regression.out + rm -rf $(pg_regress_clean_files) ifeq ($(PORTNAME), win32) rm -f python${pytverstr}.def endif diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c index fff7de7674..82baf940e3 100644 --- a/src/pl/plpython/plpython.c +++ b/src/pl/plpython/plpython.c @@ -1220,8 +1220,13 @@ PLy_procedure_call(PLyProcedure *proc, char *kargs, PyObject *vargs) PyObject *rv; PyDict_SetItemString(proc->globals, kargs, vargs); +#if PY_VERSION_HEX >= 0x03020000 + rv = PyEval_EvalCode(proc->code, + proc->globals, proc->globals); +#else rv = PyEval_EvalCode((PyCodeObject *) proc->code, proc->globals, proc->globals); +#endif /* If the Python code returned an error, propagate it */ if (rv == NULL) diff --git a/src/pl/tcl/.gitignore b/src/pl/tcl/.gitignore index 19b6c5ba42..5dcb3ff972 100644 --- a/src/pl/tcl/.gitignore +++ b/src/pl/tcl/.gitignore @@ -1,2 +1,4 @@ # Generated subdirectories +/log/ /results/ +/tmp_check/ diff --git a/src/pl/tcl/Makefile b/src/pl/tcl/Makefile index 24be38ff69..b29478dd6f 100644 --- a/src/pl/tcl/Makefile +++ b/src/pl/tcl/Makefile @@ -58,8 +58,11 @@ installdirs: installdirs-lib uninstall: uninstall-lib $(MAKE) -C modules $@ +check: submake + $(pg_regress_check) $(REGRESS_OPTS) $(REGRESS) + installcheck: submake - $(top_builddir)/src/test/regress/pg_regress --inputdir=$(srcdir) --psqldir=$(PSQLDIR) $(REGRESS_OPTS) $(REGRESS) + $(pg_regress_installcheck) $(REGRESS_OPTS) $(REGRESS) .PHONY: submake submake: @@ -77,6 +80,5 @@ endif # TCL_SHARED_BUILD = 0 clean distclean maintainer-clean: clean-lib rm -f $(OBJS) - rm -rf results - rm -f regression.diffs regression.out + rm -rf $(pg_regress_clean_files) $(MAKE) -C modules $@ diff --git a/src/port/chklocale.c b/src/port/chklocale.c index 8fd092e2d6..62932c2005 100644 --- a/src/port/chklocale.c +++ b/src/port/chklocale.c @@ -227,7 +227,7 @@ win32_langinfo(const char *ctype) * with any desired encoding. */ int -pg_get_encoding_from_locale(const char *ctype) +pg_get_encoding_from_locale(const char *ctype, bool write_message) { char *sys; int i; @@ -322,17 +322,20 @@ pg_get_encoding_from_locale(const char *ctype) * We print a warning if we got a CODESET string but couldn't recognize * it. This means we need another entry in the table. */ + if (write_message) + { #ifdef FRONTEND - fprintf(stderr, _("could not determine encoding for locale \"%s\": codeset is \"%s\""), - ctype, sys); - /* keep newline separate so there's only one translatable string */ - fputc('\n', stderr); + fprintf(stderr, _("could not determine encoding for locale \"%s\": codeset is \"%s\""), + ctype, sys); + /* keep newline separate so there's only one translatable string */ + fputc('\n', stderr); #else - ereport(WARNING, - (errmsg("could not determine encoding for locale \"%s\": codeset is \"%s\"", - ctype, sys), - errdetail("Please report this to <[email protected]>."))); + ereport(WARNING, + (errmsg("could not determine encoding for locale \"%s\": codeset is \"%s\"", + ctype, sys), + errdetail("Please report this to <[email protected]>."))); #endif + } free(sys); return -1; @@ -340,14 +343,14 @@ pg_get_encoding_from_locale(const char *ctype) #else /* (HAVE_LANGINFO_H && CODESET) || WIN32 */ /* - * stub if no platform support + * stub if no multi-language platform support * * Note: we could return -1 here, but that would have the effect of * forcing users to specify an encoding to initdb on such platforms. * It seems better to silently default to SQL_ASCII. */ int -pg_get_encoding_from_locale(const char *ctype) +pg_get_encoding_from_locale(const char *ctype, bool write_message) { return PG_SQL_ASCII; } diff --git a/src/port/path.c b/src/port/path.c index 5b0056dfe5..df2c4e7be6 100644 --- a/src/port/path.c +++ b/src/port/path.c @@ -359,6 +359,39 @@ path_contains_parent_reference(const char *path) } /* + * Detect whether a path is only in or below the current working directory. + * An absolute path that matches the current working directory should + * return false (we only want relative to the cwd). We don't allow + * "/../" even if that would keep us under the cwd (it is too hard to + * track that). + */ +bool +path_is_relative_and_below_cwd(const char *path) +{ + if (is_absolute_path(path)) + return false; + /* don't allow anything above the cwd */ + else if (path_contains_parent_reference(path)) + return false; +#ifdef WIN32 + /* + * On Win32, a drive letter _not_ followed by a slash, e.g. 'E:abc', is + * relative to the cwd on that drive, or the drive's root directory + * if that drive has no cwd. Because the path itself cannot tell us + * which is the case, we have to assume the worst, i.e. that it is not + * below the cwd. We could use GetFullPathName() to find the full path + * but that could change if the current directory for the drive changes + * underneath us, so we just disallow it. + */ + else if (isalpha((unsigned char) path[0]) && path[1] == ':' && + !IS_DIR_SEP(path[2])) + return false; +#endif + else + return true; +} + +/* * Detect whether path1 is a prefix of path2 (including equality). * * This is pretty trivial, but it seems better to export a function than diff --git a/src/test/isolation/Makefile b/src/test/isolation/Makefile index 1d4c7db8bc..82ce248a07 100644 --- a/src/test/isolation/Makefile +++ b/src/test/isolation/Makefile @@ -10,7 +10,7 @@ ifeq ($(PORTNAME), win32) LDLIBS += -lws2_32 endif -override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS) +override CPPFLAGS := -I$(srcdir) -I$(libpq_srcdir) $(CPPFLAGS) override LDLIBS := $(libpq_pgport) $(LDLIBS) OBJS = specparse.o isolationtester.o @@ -68,7 +68,7 @@ maintainer-clean: distclean rm -f specparse.c specscanner.c installcheck: all - ./pg_isolation_regress --schedule=$(srcdir)/isolation_schedule + ./pg_isolation_regress --inputdir=$(srcdir) --schedule=$(srcdir)/isolation_schedule check: all - ./pg_isolation_regress --temp-install=./tmp_check --top-builddir=$(top_builddir) --schedule=$(srcdir)/isolation_schedule + ./pg_isolation_regress --temp-install=./tmp_check --inputdir=$(srcdir) --top-builddir=$(top_builddir) --schedule=$(srcdir)/isolation_schedule diff --git a/src/test/isolation/README b/src/test/isolation/README index f6984b0bee..ee94081f09 100644 --- a/src/test/isolation/README +++ b/src/test/isolation/README @@ -26,7 +26,7 @@ Test specification ================== Each isolation test is defined by a specification file, stored in the specs -subdirectory. A test specification consists of five parts, in this order: +subdirectory. A test specification consists of four parts, in this order: setup { <SQL> } diff --git a/src/test/regress/GNUmakefile b/src/test/regress/GNUmakefile index 15b9ec49f7..120d07087c 100644 --- a/src/test/regress/GNUmakefile +++ b/src/test/regress/GNUmakefile @@ -138,19 +138,19 @@ tablespace-setup: ## Run tests ## -pg_regress_call = ./pg_regress --inputdir=$(srcdir) --dlpath=. $(if $(MULTIBYTE),--multibyte=$(MULTIBYTE)) $(NOLOCALE) +REGRESS_OPTS = --dlpath=. $(if $(MULTIBYTE),--multibyte=$(MULTIBYTE)) $(NOLOCALE) check: all tablespace-setup - $(pg_regress_call) --temp-install=./tmp_check --top-builddir=$(top_builddir) --schedule=$(srcdir)/parallel_schedule $(MAXCONNOPT) $(TEMP_CONF) + $(pg_regress_check) $(REGRESS_OPTS) --schedule=$(srcdir)/parallel_schedule $(MAXCONNOPT) $(TEMP_CONF) $(EXTRA_TESTS) installcheck: all tablespace-setup - $(pg_regress_call) --psqldir=$(PSQLDIR) --schedule=$(srcdir)/serial_schedule + $(pg_regress_installcheck) $(REGRESS_OPTS) --schedule=$(srcdir)/serial_schedule $(EXTRA_TESTS) installcheck-parallel: all tablespace-setup - $(pg_regress_call) --psqldir=$(PSQLDIR) --schedule=$(srcdir)/parallel_schedule $(MAXCONNOPT) + $(pg_regress_installcheck) $(REGRESS_OPTS) --schedule=$(srcdir)/parallel_schedule $(MAXCONNOPT) $(EXTRA_TESTS) standbycheck: all - $(pg_regress_call) --psqldir=$(PSQLDIR) --schedule=$(srcdir)/standby_schedule --use-existing + $(pg_regress_installcheck) $(REGRESS_OPTS) --schedule=$(srcdir)/standby_schedule --use-existing # old interfaces follow... @@ -159,10 +159,10 @@ runtest: installcheck runtest-parallel: installcheck-parallel bigtest: all tablespace-setup - $(pg_regress_call) --psqldir=$(PSQLDIR) --schedule=$(srcdir)/serial_schedule numeric_big + $(pg_regress_installcheck) $(REGRESS_OPTS) --schedule=$(srcdir)/serial_schedule numeric_big bigcheck: all tablespace-setup - $(pg_regress_call) --temp-install=./tmp_check --top-builddir=$(top_builddir) --schedule=$(srcdir)/parallel_schedule $(MAXCONNOPT) numeric_big + $(pg_regress_check) $(REGRESS_OPTS) --schedule=$(srcdir)/parallel_schedule $(MAXCONNOPT) numeric_big ## @@ -176,5 +176,4 @@ clean distclean maintainer-clean: clean-lib # things created by various check targets rm -f $(output_files) $(input_files) rm -rf testtablespace - rm -rf results tmp_check log - rm -f regression.diffs regression.out regress.out run_check.out + rm -rf $(pg_regress_clean_files) diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index eea0b0953a..eba8a7240a 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -184,6 +184,18 @@ DETAIL: Key (a)=(5) is not present in table "tmp2". DELETE FROM tmp3 where a=5; -- Try (and succeed) ALTER TABLE tmp3 add constraint tmpconstr foreign key (a) references tmp2 match full; +ALTER TABLE tmp3 drop constraint tmpconstr; +INSERT INTO tmp3 values (5,50); +-- Try NOT VALID and then VALIDATE CONSTRAINT, but fails. Delete failure then re-validate +ALTER TABLE tmp3 add constraint tmpconstr foreign key (a) references tmp2 match full NOT VALID; +ALTER TABLE tmp3 validate constraint tmpconstr; +ERROR: insert or update on table "tmp3" violates foreign key constraint "tmpconstr" +DETAIL: Key (a)=(5) is not present in table "tmp2". +-- Delete failing row +DELETE FROM tmp3 where a=5; +-- Try (and succeed) and repeat to show it works on already valid constraint +ALTER TABLE tmp3 validate constraint tmpconstr; +ALTER TABLE tmp3 validate constraint tmpconstr; -- Try (and fail) to create constraint from tmp5(a) to tmp4(a) - unique constraint on -- tmp4 is a,b ALTER TABLE tmp5 add constraint tmpconstr foreign key(a) references tmp4(a) match full; diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out new file mode 100644 index 0000000000..ff2678975e --- /dev/null +++ b/src/test/regress/expected/collate.linux.utf8.out @@ -0,0 +1,827 @@ +/* + * This test is for Linux/glibc systems and assumes that a full set of + * locales is installed. It must be run in a UTF-8 locale. + */ +SET client_encoding TO UTF8; +CREATE TABLE collate_test1 ( + a int, + b text COLLATE "en_US.utf8" NOT NULL +); +\d collate_test1 + Table "public.collate_test1" + Column | Type | Modifiers +--------+---------+----------------------------- + a | integer | + b | text | collate en_US.utf8 not null + +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "ja_JP.eucjp" +); +ERROR: collation "ja_JP.eucjp" for current database encoding "UTF8" does not exist +LINE 3: b text COLLATE "ja_JP.eucjp" + ^ +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "foo" +); +ERROR: collation "foo" for current database encoding "UTF8" does not exist +LINE 3: b text COLLATE "foo" + ^ +CREATE TABLE collate_test_fail ( + a int COLLATE "en_US.utf8", + b text +); +ERROR: collations are not supported by type integer +LINE 2: a int COLLATE "en_US.utf8", + ^ +CREATE TABLE collate_test_like ( + LIKE collate_test1 +); +\d collate_test_like + Table "public.collate_test_like" + Column | Type | Modifiers +--------+---------+----------------------------- + a | integer | + b | text | collate en_US.utf8 not null + +CREATE TABLE collate_test2 ( + a int, + b text COLLATE "sv_SE.utf8" +); +CREATE TABLE collate_test3 ( + a int, + b text COLLATE "C" +); +INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'äbc'), (3, 'bbc'), (4, 'ABC'); +INSERT INTO collate_test2 SELECT * FROM collate_test1; +INSERT INTO collate_test3 SELECT * FROM collate_test1; +SELECT * FROM collate_test1 WHERE b >= 'bbc'; + a | b +---+----- + 3 | bbc +(1 row) + +SELECT * FROM collate_test2 WHERE b >= 'bbc'; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test3 WHERE b >= 'bbc'; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test3 WHERE b >= 'BBC'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc +(3 rows) + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test1 WHERE b >= 'bbc' COLLATE "C"; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "C"; + a | b +---+----- + 2 | äbc + 3 | bbc +(2 rows) + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US.utf8"; +ERROR: collation mismatch between explicit collations "C" and "en_US.utf8" +LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e... + ^ +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US"; +ERROR: collation mismatch between explicit collations "C" and "en_US" +LINE 1: ...* FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "e... + ^ +CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE.utf8"; +CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE.utf8"; -- fails +ERROR: collations are not supported by type integer +CREATE TABLE collate_test4 ( + a int, + b testdomain_sv +); +INSERT INTO collate_test4 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test4 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +CREATE TABLE collate_test5 ( + a int, + b testdomain_sv COLLATE "en_US.utf8" +); +INSERT INTO collate_test5 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test5 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, b FROM collate_test1 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, b FROM collate_test2 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test3 ORDER BY b; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- star expansion +SELECT * FROM collate_test1 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT * FROM collate_test2 ORDER BY b; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT * FROM collate_test3 ORDER BY b; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- constant expression folding +SELECT 'bbc' COLLATE "en_US.utf8" > 'äbc' COLLATE "en_US.utf8" AS "true"; + true +------ + t +(1 row) + +SELECT 'bbc' COLLATE "sv_SE.utf8" > 'äbc' COLLATE "sv_SE.utf8" AS "false"; + false +------- + f +(1 row) + +-- upper/lower +CREATE TABLE collate_test10 ( + a int, + x text COLLATE "en_US.utf8", + y text COLLATE "tr_TR.utf8" +); +INSERT INTO collate_test10 VALUES (1, 'hij', 'hij'), (2, 'HIJ', 'HIJ'); +SELECT a, lower(x), lower(y), upper(x), upper(y), initcap(x), initcap(y) FROM collate_test10; + a | lower | lower | upper | upper | initcap | initcap +---+-------+-------+-------+-------+---------+--------- + 1 | hij | hij | HIJ | HİJ | Hij | Hij + 2 | hij | hıj | HIJ | HIJ | Hij | Hıj +(2 rows) + +SELECT a, lower(x COLLATE "C"), lower(y COLLATE "C") FROM collate_test10; + a | lower | lower +---+-------+------- + 1 | hij | hij + 2 | hij | hij +(2 rows) + +SELECT a, x, y FROM collate_test10 ORDER BY lower(y), a; + a | x | y +---+-----+----- + 2 | HIJ | HIJ + 1 | hij | hij +(2 rows) + +-- LIKE/ILIKE +SELECT * FROM collate_test1 WHERE b LIKE 'abc'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b LIKE 'abc%'; + a | b +---+----- + 1 | abc +(1 row) + +SELECT * FROM collate_test1 WHERE b LIKE '%bc%'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc +(3 rows) + +SELECT * FROM collate_test1 WHERE b ILIKE 'abc'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ILIKE 'abc%'; + a | b +---+----- + 1 | abc + 4 | ABC +(2 rows) + +SELECT * FROM collate_test1 WHERE b ILIKE '%bc%'; + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(4 rows) + +SELECT 'Türkiye' COLLATE "en_US.utf8" ILIKE '%KI%' AS "true"; + true +------ + t +(1 row) + +SELECT 'Türkiye' COLLATE "tr_TR.utf8" ILIKE '%KI%' AS "false"; + false +------- + f +(1 row) + +-- The following actually exercises the selectivity estimation for ILIKE. +SELECT relname FROM pg_class WHERE relname ILIKE 'abc%'; + relname +--------- +(0 rows) + +-- to_char +SET lc_time TO 'tr_TR.utf8'; +SELECT to_char(date '2010-04-01', 'DD TMMON YYYY'); + to_char +------------- + 01 NIS 2010 +(1 row) + +SELECT to_char(date '2010-04-01', 'DD TMMON YYYY' COLLATE "tr_TR.utf8"); + to_char +------------- + 01 NİS 2010 +(1 row) + +-- backwards parsing +CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; +CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; +CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; +SELECT table_name, view_definition FROM information_schema.views + WHERE table_name LIKE 'collview%' ORDER BY 1; + table_name | view_definition +------------+-------------------------------------------------------------------------------------------------------------------- + collview1 | SELECT collate_test1.a, collate_test1.b FROM collate_test1 WHERE ((collate_test1.b COLLATE "C") >= 'bbc'::text); + collview2 | SELECT collate_test1.a, collate_test1.b FROM collate_test1 ORDER BY (collate_test1.b COLLATE "C"); + collview3 | SELECT collate_test10.a, lower(((collate_test10.x || collate_test10.x) COLLATE "C")) AS lower FROM collate_test10; +(3 rows) + +-- collation propagation in various expression type +SELECT a, coalesce(b, 'foo') FROM collate_test1 ORDER BY 2; + a | coalesce +---+---------- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, coalesce(b, 'foo') FROM collate_test2 ORDER BY 2; + a | coalesce +---+---------- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, coalesce(b, 'foo') FROM collate_test3 ORDER BY 2; + a | coalesce +---+---------- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, lower(coalesce(x, 'foo')), lower(coalesce(y, 'foo')) FROM collate_test10; + a | lower | lower +---+-------+------- + 1 | hij | hij + 2 | hij | hıj +(2 rows) + +SELECT a, b, greatest(b, 'CCC') FROM collate_test1 ORDER BY 3; + a | b | greatest +---+-----+---------- + 1 | abc | CCC + 2 | äbc | CCC + 3 | bbc | CCC + 4 | ABC | CCC +(4 rows) + +SELECT a, b, greatest(b, 'CCC') FROM collate_test2 ORDER BY 3; + a | b | greatest +---+-----+---------- + 1 | abc | CCC + 3 | bbc | CCC + 4 | ABC | CCC + 2 | äbc | äbc +(4 rows) + +SELECT a, b, greatest(b, 'CCC') FROM collate_test3 ORDER BY 3; + a | b | greatest +---+-----+---------- + 4 | ABC | CCC + 1 | abc | abc + 3 | bbc | bbc + 2 | äbc | äbc +(4 rows) + +SELECT a, x, y, lower(greatest(x, 'foo')), lower(greatest(y, 'foo')) FROM collate_test10; + a | x | y | lower | lower +---+-----+-----+-------+------- + 1 | hij | hij | hij | hij + 2 | HIJ | HIJ | hij | hıj +(2 rows) + +SELECT a, nullif(b, 'abc') FROM collate_test1 ORDER BY 2; + a | nullif +---+-------- + 4 | ABC + 2 | äbc + 3 | bbc + 1 | +(4 rows) + +SELECT a, nullif(b, 'abc') FROM collate_test2 ORDER BY 2; + a | nullif +---+-------- + 4 | ABC + 3 | bbc + 2 | äbc + 1 | +(4 rows) + +SELECT a, nullif(b, 'abc') FROM collate_test3 ORDER BY 2; + a | nullif +---+-------- + 4 | ABC + 3 | bbc + 2 | äbc + 1 | +(4 rows) + +SELECT a, lower(nullif(x, 'foo')), lower(nullif(y, 'foo')) FROM collate_test10; + a | lower | lower +---+-------+------- + 1 | hij | hij + 2 | hij | hıj +(2 rows) + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test1 ORDER BY 2; + a | b +---+------ + 4 | ABC + 2 | äbc + 1 | abcd + 3 | bbc +(4 rows) + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test2 ORDER BY 2; + a | b +---+------ + 4 | ABC + 1 | abcd + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test3 ORDER BY 2; + a | b +---+------ + 4 | ABC + 1 | abcd + 3 | bbc + 2 | äbc +(4 rows) + +CREATE DOMAIN testdomain AS text; +SELECT a, b::testdomain FROM collate_test1 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, b::testdomain FROM collate_test2 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b::testdomain FROM collate_test3 ORDER BY 2; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b::testdomain_sv FROM collate_test3 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, lower(x::testdomain), lower(y::testdomain) FROM collate_test10; + a | lower | lower +---+-------+------- + 1 | hij | hij + 2 | hij | hıj +(2 rows) + +SELECT min(b), max(b) FROM collate_test1; + min | max +-----+----- + abc | bbc +(1 row) + +SELECT min(b), max(b) FROM collate_test2; + min | max +-----+----- + abc | äbc +(1 row) + +SELECT min(b), max(b) FROM collate_test3; + min | max +-----+----- + ABC | äbc +(1 row) + +SELECT array_agg(b ORDER BY b) FROM collate_test1; + array_agg +------------------- + {abc,ABC,äbc,bbc} +(1 row) + +SELECT array_agg(b ORDER BY b) FROM collate_test2; + array_agg +------------------- + {abc,ABC,bbc,äbc} +(1 row) + +SELECT array_agg(b ORDER BY b) FROM collate_test3; + array_agg +------------------- + {ABC,abc,bbc,äbc} +(1 row) + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test1 ORDER BY 2; + a | b +---+----- + 1 | abc + 1 | abc + 4 | ABC + 4 | ABC + 2 | äbc + 2 | äbc + 3 | bbc + 3 | bbc +(8 rows) + +SELECT a, b FROM collate_test2 UNION SELECT a, b FROM collate_test2 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test3 WHERE a < 4 INTERSECT SELECT a, b FROM collate_test3 WHERE a > 1 ORDER BY 2; + a | b +---+----- + 3 | bbc + 2 | äbc +(2 rows) + +SELECT a, b FROM collate_test3 EXCEPT SELECT a, b FROM collate_test3 WHERE a < 2 ORDER BY 2; + a | b +---+----- + 4 | ABC + 3 | bbc + 2 | äbc +(3 rows) + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: no collation was derived for the sort expression +LINE 1: ...e_test1 UNION ALL SELECT a, b FROM collate_test3 ORDER BY 2; + ^ +HINT: Use the COLLATE clause to set the collation explicitly. +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- ok + a | b +---+----- + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC + 1 | abc + 2 | äbc + 3 | bbc + 4 | ABC +(8 rows) + +SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: collation mismatch between implicit collations "en_US.utf8" and "C" +LINE 1: SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collat... + ^ +HINT: You can override the collation by applying the COLLATE clause to one or both expressions. +SELECT a, b COLLATE "C" FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- ok + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: collation mismatch between implicit collations "en_US.utf8" and "C" +LINE 1: ...ELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM col... + ^ +HINT: You can override the collation by applying the COLLATE clause to one or both expressions. +SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +ERROR: collation mismatch between implicit collations "en_US.utf8" and "C" +LINE 1: SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM colla... + ^ +HINT: You can override the collation by applying the COLLATE clause to one or both expressions. +-- casting +SELECT CAST('42' AS text COLLATE "C"); +ERROR: COLLATE clause not allowed in cast target +LINE 1: SELECT CAST('42' AS text COLLATE "C"); + ^ +SELECT a, CAST(b AS varchar) FROM collate_test1 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 2 | äbc + 3 | bbc +(4 rows) + +SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2; + a | b +---+----- + 1 | abc + 4 | ABC + 3 | bbc + 2 | äbc +(4 rows) + +SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2; + a | b +---+----- + 4 | ABC + 1 | abc + 3 | bbc + 2 | äbc +(4 rows) + +-- polymorphism +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1; + unnest +-------- + abc + ABC + äbc + bbc +(4 rows) + +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test2)) ORDER BY 1; + unnest +-------- + abc + ABC + bbc + äbc +(4 rows) + +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test3)) ORDER BY 1; + unnest +-------- + ABC + abc + bbc + äbc +(4 rows) + +CREATE FUNCTION dup (f1 anyelement, f2 out anyelement, f3 out anyarray) + AS 'select $1, array[$1,$1]' LANGUAGE sql; +SELECT a, (dup(b)).* FROM collate_test1 ORDER BY 2; + a | f2 | f3 +---+-----+----------- + 1 | abc | {abc,abc} + 4 | ABC | {ABC,ABC} + 2 | äbc | {äbc,äbc} + 3 | bbc | {bbc,bbc} +(4 rows) + +SELECT a, (dup(b)).* FROM collate_test2 ORDER BY 2; + a | f2 | f3 +---+-----+----------- + 1 | abc | {abc,abc} + 4 | ABC | {ABC,ABC} + 3 | bbc | {bbc,bbc} + 2 | äbc | {äbc,äbc} +(4 rows) + +SELECT a, (dup(b)).* FROM collate_test3 ORDER BY 2; + a | f2 | f3 +---+-----+----------- + 4 | ABC | {ABC,ABC} + 1 | abc | {abc,abc} + 3 | bbc | {bbc,bbc} + 2 | äbc | {äbc,äbc} +(4 rows) + +-- indexes +CREATE INDEX collate_test1_idx1 ON collate_test1 (b); +CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "C"); +CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "C")); -- this is different grammatically +CREATE INDEX collate_test1_idx4 ON collate_test1 (a COLLATE "C"); -- fail +ERROR: collations are not supported by type integer +CREATE INDEX collate_test1_idx5 ON collate_test1 ((a COLLATE "C")); -- fail +ERROR: collations are not supported by type integer +SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%'; + relname | pg_get_indexdef +--------------------+---------------------------------------------------------------------------------------------- + collate_test1_idx1 | CREATE INDEX collate_test1_idx1 ON collate_test1 USING btree (b) + collate_test1_idx2 | CREATE INDEX collate_test1_idx2 ON collate_test1 USING btree (b COLLATE "C") + collate_test1_idx3 | CREATE INDEX collate_test1_idx3 ON collate_test1 USING btree (((b COLLATE "C")) COLLATE "C") +(3 rows) + +-- schema manipulation commands +CREATE ROLE regress_test_role; +CREATE SCHEMA test_schema; +CREATE COLLATION test0 (locale = 'en_US.utf8'); +CREATE COLLATION test0 (locale = 'en_US.utf8'); -- fail +ERROR: collation "test0" for encoding "UTF8" already exists +CREATE COLLATION test1 (lc_collate = 'en_US.utf8', lc_ctype = 'de_DE.utf8'); +CREATE COLLATION test2 (locale = 'en_US'); -- fail +ERROR: encoding UTF8 does not match locale en_US +DETAIL: The chosen LC_CTYPE setting requires encoding LATIN1. +CREATE COLLATION test3 (lc_collate = 'en_US.utf8'); -- fail +ERROR: parameter "lc_ctype" must be specified +CREATE COLLATION test4 FROM nonsense; +ERROR: collation "nonsense" for current database encoding "UTF8" does not exist +CREATE COLLATION test5 FROM test0; +SELECT collname, collencoding, collcollate, collctype FROM pg_collation WHERE collname LIKE 'test%' ORDER BY 1; + collname | collencoding | collcollate | collctype +----------+--------------+-------------+------------ + test0 | 6 | en_US.utf8 | en_US.utf8 + test1 | 6 | en_US.utf8 | de_DE.utf8 + test5 | 6 | en_US.utf8 | en_US.utf8 +(3 rows) + +ALTER COLLATION test1 RENAME TO test11; +ALTER COLLATION test0 RENAME TO test11; -- fail +ERROR: collation "test11" for current database encoding "UTF8" already exists in schema "public" +ALTER COLLATION test1 RENAME TO test22; -- fail +ERROR: collation "test1" for current database encoding "UTF8" does not exist +ALTER COLLATION test11 OWNER TO regress_test_role; +ALTER COLLATION test11 OWNER TO nonsense; +ERROR: role "nonsense" does not exist +ALTER COLLATION test11 SET SCHEMA test_schema; +COMMENT ON COLLATION test0 IS 'US English'; +SELECT collname, nspname, obj_description(pg_collation.oid, 'pg_collation') + FROM pg_collation JOIN pg_namespace ON (collnamespace = pg_namespace.oid) + WHERE collname LIKE 'test%' + ORDER BY 1; + collname | nspname | obj_description +----------+-------------+----------------- + test0 | public | US English + test11 | test_schema | + test5 | public | +(3 rows) + +DROP COLLATION test0, test_schema.test11, test5; +DROP COLLATION test0; -- fail +ERROR: collation "test0" for current database encoding "UTF8" does not exist +DROP COLLATION IF EXISTS test0; +NOTICE: collation "test0" does not exist, skipping +SELECT collname FROM pg_collation WHERE collname LIKE 'test%'; + collname +---------- +(0 rows) + +DROP SCHEMA test_schema; +DROP ROLE regress_test_role; +-- dependencies +CREATE COLLATION test0 (locale = 'en_US.utf8'); +CREATE TABLE collate_dep_test1 (a int, b text COLLATE test0); +CREATE DOMAIN collate_dep_dom1 AS text COLLATE test0; +CREATE TYPE collate_dep_test2 AS (x int, y text COLLATE test0); +CREATE VIEW collate_dep_test3 AS SELECT text 'foo' COLLATE test0 AS foo; +CREATE TABLE collate_dep_test4t (a int, b text); +CREATE INDEX collate_dep_test4i ON collate_dep_test4t (b COLLATE test0); +DROP COLLATION test0 RESTRICT; -- fail +ERROR: cannot drop collation test0 because other objects depend on it +DETAIL: table collate_dep_test1 column b depends on collation test0 +type collate_dep_dom1 depends on collation test0 +composite type collate_dep_test2 column y depends on collation test0 +view collate_dep_test3 depends on collation test0 +index collate_dep_test4i depends on collation test0 +HINT: Use DROP ... CASCADE to drop the dependent objects too. +DROP COLLATION test0 CASCADE; +NOTICE: drop cascades to 5 other objects +DETAIL: drop cascades to table collate_dep_test1 column b +drop cascades to type collate_dep_dom1 +drop cascades to composite type collate_dep_test2 column y +drop cascades to view collate_dep_test3 +drop cascades to index collate_dep_test4i +\d collate_dep_test1 +Table "public.collate_dep_test1" + Column | Type | Modifiers +--------+---------+----------- + a | integer | + +\d collate_dep_test2 +Composite type "public.collate_dep_test2" + Column | Type +--------+--------- + x | integer + +DROP TABLE collate_dep_test1, collate_dep_test4t; +DROP TYPE collate_dep_test2; diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out index 22ccce212c..bfabcbc8b4 100644 --- a/src/test/regress/expected/plpgsql.out +++ b/src/test/regress/expected/plpgsql.out @@ -4240,3 +4240,197 @@ select unreserved_test(); (1 row) drop function unreserved_test(); +-- +-- Test FOREACH over arrays +-- +create function foreach_test(anyarray) +returns void as $$ +declare x int; +begin + foreach x in array $1 + loop + raise notice '%', x; + end loop; + end; +$$ language plpgsql; +select foreach_test(ARRAY[1,2,3,4]); +NOTICE: 1 +NOTICE: 2 +NOTICE: 3 +NOTICE: 4 + foreach_test +-------------- + +(1 row) + +select foreach_test(ARRAY[[1,2],[3,4]]); +NOTICE: 1 +NOTICE: 2 +NOTICE: 3 +NOTICE: 4 + foreach_test +-------------- + +(1 row) + +create or replace function foreach_test(anyarray) +returns void as $$ +declare x int; +begin + foreach x slice 1 in array $1 + loop + raise notice '%', x; + end loop; + end; +$$ language plpgsql; +-- should fail +select foreach_test(ARRAY[1,2,3,4]); +ERROR: FOREACH ... SLICE loop variable must be of an array type +CONTEXT: PL/pgSQL function "foreach_test" line 4 at FOREACH over array +select foreach_test(ARRAY[[1,2],[3,4]]); +ERROR: FOREACH ... SLICE loop variable must be of an array type +CONTEXT: PL/pgSQL function "foreach_test" line 4 at FOREACH over array +create or replace function foreach_test(anyarray) +returns void as $$ +declare x int[]; +begin + foreach x slice 1 in array $1 + loop + raise notice '%', x; + end loop; + end; +$$ language plpgsql; +select foreach_test(ARRAY[1,2,3,4]); +NOTICE: {1,2,3,4} + foreach_test +-------------- + +(1 row) + +select foreach_test(ARRAY[[1,2],[3,4]]); +NOTICE: {1,2} +NOTICE: {3,4} + foreach_test +-------------- + +(1 row) + +-- higher level of slicing +create or replace function foreach_test(anyarray) +returns void as $$ +declare x int[]; +begin + foreach x slice 2 in array $1 + loop + raise notice '%', x; + end loop; + end; +$$ language plpgsql; +-- should fail +select foreach_test(ARRAY[1,2,3,4]); +ERROR: slice dimension (2) is out of the valid range 0..1 +CONTEXT: PL/pgSQL function "foreach_test" line 4 at FOREACH over array +-- ok +select foreach_test(ARRAY[[1,2],[3,4]]); +NOTICE: {{1,2},{3,4}} + foreach_test +-------------- + +(1 row) + +select foreach_test(ARRAY[[[1,2]],[[3,4]]]); +NOTICE: {{1,2}} +NOTICE: {{3,4}} + foreach_test +-------------- + +(1 row) + +create type xy_tuple AS (x int, y int); +-- iteration over array of records +create or replace function foreach_test(anyarray) +returns void as $$ +declare r record; +begin + foreach r in array $1 + loop + raise notice '%', r; + end loop; + end; +$$ language plpgsql; +select foreach_test(ARRAY[(10,20),(40,69),(35,78)]::xy_tuple[]); +NOTICE: (10,20) +NOTICE: (40,69) +NOTICE: (35,78) + foreach_test +-------------- + +(1 row) + +select foreach_test(ARRAY[[(10,20),(40,69)],[(35,78),(88,76)]]::xy_tuple[]); +NOTICE: (10,20) +NOTICE: (40,69) +NOTICE: (35,78) +NOTICE: (88,76) + foreach_test +-------------- + +(1 row) + +create or replace function foreach_test(anyarray) +returns void as $$ +declare x int; y int; +begin + foreach x, y in array $1 + loop + raise notice 'x = %, y = %', x, y; + end loop; + end; +$$ language plpgsql; +select foreach_test(ARRAY[(10,20),(40,69),(35,78)]::xy_tuple[]); +NOTICE: x = 10, y = 20 +NOTICE: x = 40, y = 69 +NOTICE: x = 35, y = 78 + foreach_test +-------------- + +(1 row) + +select foreach_test(ARRAY[[(10,20),(40,69)],[(35,78),(88,76)]]::xy_tuple[]); +NOTICE: x = 10, y = 20 +NOTICE: x = 40, y = 69 +NOTICE: x = 35, y = 78 +NOTICE: x = 88, y = 76 + foreach_test +-------------- + +(1 row) + +-- slicing over array of composite types +create or replace function foreach_test(anyarray) +returns void as $$ +declare x xy_tuple[]; +begin + foreach x slice 1 in array $1 + loop + raise notice '%', x; + end loop; + end; +$$ language plpgsql; +select foreach_test(ARRAY[(10,20),(40,69),(35,78)]::xy_tuple[]); +NOTICE: {"(10,20)","(40,69)","(35,78)"} + foreach_test +-------------- + +(1 row) + +select foreach_test(ARRAY[[(10,20),(40,69)],[(35,78),(88,76)]]::xy_tuple[]); +NOTICE: {"(10,20)","(40,69)"} +NOTICE: {"(35,78)","(88,76)"} + foreach_test +-------------- + +(1 row) + +drop function foreach_test(anyarray); +drop type xy_tuple; diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 72e5630b1f..d8e9ea1ab1 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1276,67 +1276,69 @@ drop table cchild; -- Check that ruleutils are working -- SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schema' ORDER BY viewname; - viewname | definition ------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - iexit | SELECT ih.name, ih.thepath, interpt_pp(ih.thepath, r.thepath) AS exit FROM ihighway ih, ramp r WHERE (ih.thepath ## r.thepath); - pg_cursors | SELECT c.name, c.statement, c.is_holdable, c.is_binary, c.is_scrollable, c.creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time); - pg_group | SELECT pg_authid.rolname AS groname, pg_authid.oid AS grosysid, ARRAY(SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid WHERE (NOT pg_authid.rolcanlogin); - pg_indexes | SELECT n.nspname AS schemaname, c.relname AS tablename, i.relname AS indexname, t.spcname AS tablespace, pg_get_indexdef(i.oid) AS indexdef FROM ((((pg_index x JOIN pg_class c ON ((c.oid = x.indrelid))) JOIN pg_class i ON ((i.oid = x.indexrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = 'r'::"char") AND (i.relkind = 'i'::"char")); - pg_locks | SELECT l.locktype, l.database, l.relation, l.page, l.tuple, l.virtualxid, l.transactionid, l.classid, l.objid, l.objsubid, l.virtualtransaction, l.pid, l.mode, l.granted FROM pg_lock_status() l(locktype, database, relation, page, tuple, virtualxid, transactionid, classid, objid, objsubid, virtualtransaction, pid, mode, granted); - pg_prepared_statements | SELECT p.name, p.statement, p.prepare_time, p.parameter_types, p.from_sql FROM pg_prepared_statement() p(name, statement, prepare_time, parameter_types, from_sql); - pg_prepared_xacts | SELECT p.transaction, p.gid, p.prepared, u.rolname AS owner, d.datname AS database FROM ((pg_prepared_xact() p(transaction, gid, prepared, ownerid, dbid) LEFT JOIN pg_authid u ON ((p.ownerid = u.oid))) LEFT JOIN pg_database d ON ((p.dbid = d.oid))); - pg_roles | SELECT pg_authid.rolname, pg_authid.rolsuper, pg_authid.rolinherit, pg_authid.rolcreaterole, pg_authid.rolcreatedb, pg_authid.rolcatupdate, pg_authid.rolcanlogin, pg_authid.rolreplication, pg_authid.rolconnlimit, '********'::text AS rolpassword, pg_authid.rolvaliduntil, s.setconfig AS rolconfig, pg_authid.oid FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))); - pg_rules | SELECT n.nspname AS schemaname, c.relname AS tablename, r.rulename, pg_get_ruledef(r.oid) AS definition FROM ((pg_rewrite r JOIN pg_class c ON ((c.oid = r.ev_class))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (r.rulename <> '_RETURN'::name); - pg_seclabels | (((((SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (rel.relkind = 'r'::"char") THEN 'table'::text WHEN (rel.relkind = 'v'::"char") THEN 'view'::text WHEN (rel.relkind = 'S'::"char") THEN 'sequence'::text WHEN (rel.relkind = 'f'::"char") THEN 'foreign table'::text ELSE NULL::text END AS objtype, rel.relnamespace AS objnamespace, CASE WHEN pg_table_is_visible(rel.oid) THEN quote_ident((rel.relname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((rel.relname)::text)) END AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_class rel ON (((l.classoid = rel.tableoid) AND (l.objoid = rel.oid)))) JOIN pg_namespace nsp ON ((rel.relnamespace = nsp.oid))) WHERE (l.objsubid = 0) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'column'::text AS objtype, rel.relnamespace AS objnamespace, ((CASE WHEN pg_table_is_visible(rel.oid) THEN quote_ident((rel.relname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((rel.relname)::text)) END || '.'::text) || (att.attname)::text) AS objname, l.provider, l.label FROM (((pg_seclabel l JOIN pg_class rel ON (((l.classoid = rel.tableoid) AND (l.objoid = rel.oid)))) JOIN pg_attribute att ON (((rel.oid = att.attrelid) AND (l.objsubid = att.attnum)))) JOIN pg_namespace nsp ON ((rel.relnamespace = nsp.oid))) WHERE (l.objsubid <> 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (pro.proisagg = true) THEN 'aggregate'::text WHEN (pro.proisagg = false) THEN 'function'::text ELSE NULL::text END AS objtype, pro.pronamespace AS objnamespace, (((CASE WHEN pg_function_is_visible(pro.oid) THEN quote_ident((pro.proname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((pro.proname)::text)) END || '('::text) || pg_get_function_arguments(pro.oid)) || ')'::text) AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_proc pro ON (((l.classoid = pro.tableoid) AND (l.objoid = pro.oid)))) JOIN pg_namespace nsp ON ((pro.pronamespace = nsp.oid))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (typ.typtype = 'd'::"char") THEN 'domain'::text ELSE 'type'::text END AS objtype, typ.typnamespace AS objnamespace, CASE WHEN pg_type_is_visible(typ.oid) THEN quote_ident((typ.typname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((typ.typname)::text)) END AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_type typ ON (((l.classoid = typ.tableoid) AND (l.objoid = typ.oid)))) JOIN pg_namespace nsp ON ((typ.typnamespace = nsp.oid))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'large object'::text AS objtype, NULL::oid AS objnamespace, (l.objoid)::text AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_largeobject_metadata lom ON ((l.objoid = lom.oid))) WHERE ((l.classoid = ('pg_largeobject'::regclass)::oid) AND (l.objsubid = 0))) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'language'::text AS objtype, NULL::oid AS objnamespace, quote_ident((lan.lanname)::text) AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_language lan ON (((l.classoid = lan.tableoid) AND (l.objoid = lan.oid)))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'schema'::text AS objtype, nsp.oid AS objnamespace, quote_ident((nsp.nspname)::text) AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_namespace nsp ON (((l.classoid = nsp.tableoid) AND (l.objoid = nsp.oid)))) WHERE (l.objsubid = 0); - pg_settings | SELECT a.name, a.setting, a.unit, a.category, a.short_desc, a.extra_desc, a.context, a.vartype, a.source, a.min_val, a.max_val, a.enumvals, a.boot_val, a.reset_val, a.sourcefile, a.sourceline FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline); - pg_shadow | SELECT pg_authid.rolname AS usename, pg_authid.oid AS usesysid, pg_authid.rolcreatedb AS usecreatedb, pg_authid.rolsuper AS usesuper, pg_authid.rolcatupdate AS usecatupd, pg_authid.rolreplication AS userepl, pg_authid.rolpassword AS passwd, (pg_authid.rolvaliduntil)::abstime AS valuntil, s.setconfig AS useconfig FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))) WHERE pg_authid.rolcanlogin; - pg_stat_activity | SELECT s.datid, d.datname, s.procpid, s.usesysid, u.rolname AS usename, s.application_name, s.client_addr, s.client_port, s.backend_start, s.xact_start, s.query_start, s.waiting, s.current_query FROM pg_database d, pg_stat_get_activity(NULL::integer) s(datid, procpid, usesysid, application_name, current_query, waiting, xact_start, query_start, backend_start, client_addr, client_port), pg_authid u WHERE ((s.datid = d.oid) AND (s.usesysid = u.oid)); - pg_stat_all_indexes | SELECT c.oid AS relid, i.oid AS indexrelid, n.nspname AS schemaname, c.relname, i.relname AS indexrelname, pg_stat_get_numscans(i.oid) AS idx_scan, pg_stat_get_tuples_returned(i.oid) AS idx_tup_read, pg_stat_get_tuples_fetched(i.oid) AS idx_tup_fetch FROM (((pg_class c JOIN pg_index x ON ((c.oid = x.indrelid))) JOIN pg_class i ON ((i.oid = x.indexrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char"])); - pg_stat_all_tables | SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, pg_stat_get_numscans(c.oid) AS seq_scan, pg_stat_get_tuples_returned(c.oid) AS seq_tup_read, (sum(pg_stat_get_numscans(i.indexrelid)))::bigint AS idx_scan, ((sum(pg_stat_get_tuples_fetched(i.indexrelid)))::bigint + pg_stat_get_tuples_fetched(c.oid)) AS idx_tup_fetch, pg_stat_get_tuples_inserted(c.oid) AS n_tup_ins, pg_stat_get_tuples_updated(c.oid) AS n_tup_upd, pg_stat_get_tuples_deleted(c.oid) AS n_tup_del, pg_stat_get_tuples_hot_updated(c.oid) AS n_tup_hot_upd, pg_stat_get_live_tuples(c.oid) AS n_live_tup, pg_stat_get_dead_tuples(c.oid) AS n_dead_tup, pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum, pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum, pg_stat_get_last_analyze_time(c.oid) AS last_analyze, pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze, pg_stat_get_vacuum_count(c.oid) AS vacuum_count, pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count, pg_stat_get_analyze_count(c.oid) AS analyze_count, pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count FROM ((pg_class c LEFT JOIN pg_index i ON ((c.oid = i.indrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char"])) GROUP BY c.oid, n.nspname, c.relname; - pg_stat_bgwriter | SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints_timed, pg_stat_get_bgwriter_requested_checkpoints() AS checkpoints_req, pg_stat_get_bgwriter_buf_written_checkpoints() AS buffers_checkpoint, pg_stat_get_bgwriter_buf_written_clean() AS buffers_clean, pg_stat_get_bgwriter_maxwritten_clean() AS maxwritten_clean, pg_stat_get_buf_written_backend() AS buffers_backend, pg_stat_get_buf_fsync_backend() AS buffers_backend_fsync, pg_stat_get_buf_alloc() AS buffers_alloc; - pg_stat_database | SELECT d.oid AS datid, d.datname, pg_stat_get_db_numbackends(d.oid) AS numbackends, pg_stat_get_db_xact_commit(d.oid) AS xact_commit, pg_stat_get_db_xact_rollback(d.oid) AS xact_rollback, (pg_stat_get_db_blocks_fetched(d.oid) - pg_stat_get_db_blocks_hit(d.oid)) AS blks_read, pg_stat_get_db_blocks_hit(d.oid) AS blks_hit, pg_stat_get_db_tuples_returned(d.oid) AS tup_returned, pg_stat_get_db_tuples_fetched(d.oid) AS tup_fetched, pg_stat_get_db_tuples_inserted(d.oid) AS tup_inserted, pg_stat_get_db_tuples_updated(d.oid) AS tup_updated, pg_stat_get_db_tuples_deleted(d.oid) AS tup_deleted, pg_stat_get_db_conflict_all(d.oid) AS conflicts FROM pg_database d; - pg_stat_database_conflicts | SELECT d.oid AS datid, d.datname, pg_stat_get_db_conflict_tablespace(d.oid) AS confl_tablespace, pg_stat_get_db_conflict_lock(d.oid) AS confl_lock, pg_stat_get_db_conflict_snapshot(d.oid) AS confl_snapshot, pg_stat_get_db_conflict_bufferpin(d.oid) AS confl_bufferpin, pg_stat_get_db_conflict_startup_deadlock(d.oid) AS confl_deadlock FROM pg_database d; - pg_stat_replication | SELECT s.procpid, s.usesysid, u.rolname AS usename, s.application_name, s.client_addr, s.client_port, s.backend_start, w.state, w.sent_location FROM pg_stat_get_activity(NULL::integer) s(datid, procpid, usesysid, application_name, current_query, waiting, xact_start, query_start, backend_start, client_addr, client_port), pg_authid u, pg_stat_get_wal_senders() w(procpid, state, sent_location) WHERE ((s.usesysid = u.oid) AND (s.procpid = w.procpid)); - pg_stat_sys_indexes | SELECT pg_stat_all_indexes.relid, pg_stat_all_indexes.indexrelid, pg_stat_all_indexes.schemaname, pg_stat_all_indexes.relname, pg_stat_all_indexes.indexrelname, pg_stat_all_indexes.idx_scan, pg_stat_all_indexes.idx_tup_read, pg_stat_all_indexes.idx_tup_fetch FROM pg_stat_all_indexes WHERE ((pg_stat_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_indexes.schemaname ~ '^pg_toast'::text)); - pg_stat_sys_tables | SELECT pg_stat_all_tables.relid, pg_stat_all_tables.schemaname, pg_stat_all_tables.relname, pg_stat_all_tables.seq_scan, pg_stat_all_tables.seq_tup_read, pg_stat_all_tables.idx_scan, pg_stat_all_tables.idx_tup_fetch, pg_stat_all_tables.n_tup_ins, pg_stat_all_tables.n_tup_upd, pg_stat_all_tables.n_tup_del, pg_stat_all_tables.n_tup_hot_upd, pg_stat_all_tables.n_live_tup, pg_stat_all_tables.n_dead_tup, pg_stat_all_tables.last_vacuum, pg_stat_all_tables.last_autovacuum, pg_stat_all_tables.last_analyze, pg_stat_all_tables.last_autoanalyze, pg_stat_all_tables.vacuum_count, pg_stat_all_tables.autovacuum_count, pg_stat_all_tables.analyze_count, pg_stat_all_tables.autoanalyze_count FROM pg_stat_all_tables WHERE ((pg_stat_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_tables.schemaname ~ '^pg_toast'::text)); - pg_stat_user_functions | SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, pg_stat_get_function_calls(p.oid) AS calls, (pg_stat_get_function_time(p.oid) / 1000) AS total_time, (pg_stat_get_function_self_time(p.oid) / 1000) AS self_time FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_function_calls(p.oid) IS NOT NULL)); - pg_stat_user_indexes | SELECT pg_stat_all_indexes.relid, pg_stat_all_indexes.indexrelid, pg_stat_all_indexes.schemaname, pg_stat_all_indexes.relname, pg_stat_all_indexes.indexrelname, pg_stat_all_indexes.idx_scan, pg_stat_all_indexes.idx_tup_read, pg_stat_all_indexes.idx_tup_fetch FROM pg_stat_all_indexes WHERE ((pg_stat_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_indexes.schemaname !~ '^pg_toast'::text)); - pg_stat_user_tables | SELECT pg_stat_all_tables.relid, pg_stat_all_tables.schemaname, pg_stat_all_tables.relname, pg_stat_all_tables.seq_scan, pg_stat_all_tables.seq_tup_read, pg_stat_all_tables.idx_scan, pg_stat_all_tables.idx_tup_fetch, pg_stat_all_tables.n_tup_ins, pg_stat_all_tables.n_tup_upd, pg_stat_all_tables.n_tup_del, pg_stat_all_tables.n_tup_hot_upd, pg_stat_all_tables.n_live_tup, pg_stat_all_tables.n_dead_tup, pg_stat_all_tables.last_vacuum, pg_stat_all_tables.last_autovacuum, pg_stat_all_tables.last_analyze, pg_stat_all_tables.last_autoanalyze, pg_stat_all_tables.vacuum_count, pg_stat_all_tables.autovacuum_count, pg_stat_all_tables.analyze_count, pg_stat_all_tables.autoanalyze_count FROM pg_stat_all_tables WHERE ((pg_stat_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_tables.schemaname !~ '^pg_toast'::text)); - pg_stat_xact_all_tables | SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, pg_stat_get_xact_numscans(c.oid) AS seq_scan, pg_stat_get_xact_tuples_returned(c.oid) AS seq_tup_read, (sum(pg_stat_get_xact_numscans(i.indexrelid)))::bigint AS idx_scan, ((sum(pg_stat_get_xact_tuples_fetched(i.indexrelid)))::bigint + pg_stat_get_xact_tuples_fetched(c.oid)) AS idx_tup_fetch, pg_stat_get_xact_tuples_inserted(c.oid) AS n_tup_ins, pg_stat_get_xact_tuples_updated(c.oid) AS n_tup_upd, pg_stat_get_xact_tuples_deleted(c.oid) AS n_tup_del, pg_stat_get_xact_tuples_hot_updated(c.oid) AS n_tup_hot_upd FROM ((pg_class c LEFT JOIN pg_index i ON ((c.oid = i.indrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char"])) GROUP BY c.oid, n.nspname, c.relname; - pg_stat_xact_sys_tables | SELECT pg_stat_xact_all_tables.relid, pg_stat_xact_all_tables.schemaname, pg_stat_xact_all_tables.relname, pg_stat_xact_all_tables.seq_scan, pg_stat_xact_all_tables.seq_tup_read, pg_stat_xact_all_tables.idx_scan, pg_stat_xact_all_tables.idx_tup_fetch, pg_stat_xact_all_tables.n_tup_ins, pg_stat_xact_all_tables.n_tup_upd, pg_stat_xact_all_tables.n_tup_del, pg_stat_xact_all_tables.n_tup_hot_upd FROM pg_stat_xact_all_tables WHERE ((pg_stat_xact_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_xact_all_tables.schemaname ~ '^pg_toast'::text)); - pg_stat_xact_user_functions | SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, pg_stat_get_xact_function_calls(p.oid) AS calls, (pg_stat_get_xact_function_time(p.oid) / 1000) AS total_time, (pg_stat_get_xact_function_self_time(p.oid) / 1000) AS self_time FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_xact_function_calls(p.oid) IS NOT NULL)); - pg_stat_xact_user_tables | SELECT pg_stat_xact_all_tables.relid, pg_stat_xact_all_tables.schemaname, pg_stat_xact_all_tables.relname, pg_stat_xact_all_tables.seq_scan, pg_stat_xact_all_tables.seq_tup_read, pg_stat_xact_all_tables.idx_scan, pg_stat_xact_all_tables.idx_tup_fetch, pg_stat_xact_all_tables.n_tup_ins, pg_stat_xact_all_tables.n_tup_upd, pg_stat_xact_all_tables.n_tup_del, pg_stat_xact_all_tables.n_tup_hot_upd FROM pg_stat_xact_all_tables WHERE ((pg_stat_xact_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_xact_all_tables.schemaname !~ '^pg_toast'::text)); - pg_statio_all_indexes | SELECT c.oid AS relid, i.oid AS indexrelid, n.nspname AS schemaname, c.relname, i.relname AS indexrelname, (pg_stat_get_blocks_fetched(i.oid) - pg_stat_get_blocks_hit(i.oid)) AS idx_blks_read, pg_stat_get_blocks_hit(i.oid) AS idx_blks_hit FROM (((pg_class c JOIN pg_index x ON ((c.oid = x.indrelid))) JOIN pg_class i ON ((i.oid = x.indexrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char"])); - pg_statio_all_sequences | SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, (pg_stat_get_blocks_fetched(c.oid) - pg_stat_get_blocks_hit(c.oid)) AS blks_read, pg_stat_get_blocks_hit(c.oid) AS blks_hit FROM (pg_class c LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = 'S'::"char"); - pg_statio_all_tables | SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, (pg_stat_get_blocks_fetched(c.oid) - pg_stat_get_blocks_hit(c.oid)) AS heap_blks_read, pg_stat_get_blocks_hit(c.oid) AS heap_blks_hit, (sum((pg_stat_get_blocks_fetched(i.indexrelid) - pg_stat_get_blocks_hit(i.indexrelid))))::bigint AS idx_blks_read, (sum(pg_stat_get_blocks_hit(i.indexrelid)))::bigint AS idx_blks_hit, (pg_stat_get_blocks_fetched(t.oid) - pg_stat_get_blocks_hit(t.oid)) AS toast_blks_read, pg_stat_get_blocks_hit(t.oid) AS toast_blks_hit, (pg_stat_get_blocks_fetched(x.oid) - pg_stat_get_blocks_hit(x.oid)) AS tidx_blks_read, pg_stat_get_blocks_hit(x.oid) AS tidx_blks_hit FROM ((((pg_class c LEFT JOIN pg_index i ON ((c.oid = i.indrelid))) LEFT JOIN pg_class t ON ((c.reltoastrelid = t.oid))) LEFT JOIN pg_class x ON ((t.reltoastidxid = x.oid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char"])) GROUP BY c.oid, n.nspname, c.relname, t.oid, x.oid; - pg_statio_sys_indexes | SELECT pg_statio_all_indexes.relid, pg_statio_all_indexes.indexrelid, pg_statio_all_indexes.schemaname, pg_statio_all_indexes.relname, pg_statio_all_indexes.indexrelname, pg_statio_all_indexes.idx_blks_read, pg_statio_all_indexes.idx_blks_hit FROM pg_statio_all_indexes WHERE ((pg_statio_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_indexes.schemaname ~ '^pg_toast'::text)); - pg_statio_sys_sequences | SELECT pg_statio_all_sequences.relid, pg_statio_all_sequences.schemaname, pg_statio_all_sequences.relname, pg_statio_all_sequences.blks_read, pg_statio_all_sequences.blks_hit FROM pg_statio_all_sequences WHERE ((pg_statio_all_sequences.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_sequences.schemaname ~ '^pg_toast'::text)); - pg_statio_sys_tables | SELECT pg_statio_all_tables.relid, pg_statio_all_tables.schemaname, pg_statio_all_tables.relname, pg_statio_all_tables.heap_blks_read, pg_statio_all_tables.heap_blks_hit, pg_statio_all_tables.idx_blks_read, pg_statio_all_tables.idx_blks_hit, pg_statio_all_tables.toast_blks_read, pg_statio_all_tables.toast_blks_hit, pg_statio_all_tables.tidx_blks_read, pg_statio_all_tables.tidx_blks_hit FROM pg_statio_all_tables WHERE ((pg_statio_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_tables.schemaname ~ '^pg_toast'::text)); - pg_statio_user_indexes | SELECT pg_statio_all_indexes.relid, pg_statio_all_indexes.indexrelid, pg_statio_all_indexes.schemaname, pg_statio_all_indexes.relname, pg_statio_all_indexes.indexrelname, pg_statio_all_indexes.idx_blks_read, pg_statio_all_indexes.idx_blks_hit FROM pg_statio_all_indexes WHERE ((pg_statio_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_indexes.schemaname !~ '^pg_toast'::text)); - pg_statio_user_sequences | SELECT pg_statio_all_sequences.relid, pg_statio_all_sequences.schemaname, pg_statio_all_sequences.relname, pg_statio_all_sequences.blks_read, pg_statio_all_sequences.blks_hit FROM pg_statio_all_sequences WHERE ((pg_statio_all_sequences.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_sequences.schemaname !~ '^pg_toast'::text)); - pg_statio_user_tables | SELECT pg_statio_all_tables.relid, pg_statio_all_tables.schemaname, pg_statio_all_tables.relname, pg_statio_all_tables.heap_blks_read, pg_statio_all_tables.heap_blks_hit, pg_statio_all_tables.idx_blks_read, pg_statio_all_tables.idx_blks_hit, pg_statio_all_tables.toast_blks_read, pg_statio_all_tables.toast_blks_hit, pg_statio_all_tables.tidx_blks_read, pg_statio_all_tables.tidx_blks_hit FROM pg_statio_all_tables WHERE ((pg_statio_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_tables.schemaname !~ '^pg_toast'::text)); - pg_stats | SELECT n.nspname AS schemaname, c.relname AS tablename, a.attname, s.stainherit AS inherited, s.stanullfrac AS null_frac, s.stawidth AS avg_width, s.stadistinct AS n_distinct, CASE WHEN (s.stakind1 = ANY (ARRAY[1, 4])) THEN s.stavalues1 WHEN (s.stakind2 = ANY (ARRAY[1, 4])) THEN s.stavalues2 WHEN (s.stakind3 = ANY (ARRAY[1, 4])) THEN s.stavalues3 WHEN (s.stakind4 = ANY (ARRAY[1, 4])) THEN s.stavalues4 ELSE NULL::anyarray END AS most_common_vals, CASE WHEN (s.stakind1 = ANY (ARRAY[1, 4])) THEN s.stanumbers1 WHEN (s.stakind2 = ANY (ARRAY[1, 4])) THEN s.stanumbers2 WHEN (s.stakind3 = ANY (ARRAY[1, 4])) THEN s.stanumbers3 WHEN (s.stakind4 = ANY (ARRAY[1, 4])) THEN s.stanumbers4 ELSE NULL::real[] END AS most_common_freqs, CASE WHEN (s.stakind1 = 2) THEN s.stavalues1 WHEN (s.stakind2 = 2) THEN s.stavalues2 WHEN (s.stakind3 = 2) THEN s.stavalues3 WHEN (s.stakind4 = 2) THEN s.stavalues4 ELSE NULL::anyarray END AS histogram_bounds, CASE WHEN (s.stakind1 = 3) THEN s.stanumbers1[1] WHEN (s.stakind2 = 3) THEN s.stanumbers2[1] WHEN (s.stakind3 = 3) THEN s.stanumbers3[1] WHEN (s.stakind4 = 3) THEN s.stanumbers4[1] ELSE NULL::real END AS correlation FROM (((pg_statistic s JOIN pg_class c ON ((c.oid = s.starelid))) JOIN pg_attribute a ON (((c.oid = a.attrelid) AND (a.attnum = s.staattnum)))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE ((NOT a.attisdropped) AND has_column_privilege(c.oid, a.attnum, 'select'::text)); - pg_tables | SELECT n.nspname AS schemaname, c.relname AS tablename, pg_get_userbyid(c.relowner) AS tableowner, t.spcname AS tablespace, c.relhasindex AS hasindexes, c.relhasrules AS hasrules, c.relhastriggers AS hastriggers FROM ((pg_class c LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = c.reltablespace))) WHERE (c.relkind = 'r'::"char"); - pg_timezone_abbrevs | SELECT pg_timezone_abbrevs.abbrev, pg_timezone_abbrevs.utc_offset, pg_timezone_abbrevs.is_dst FROM pg_timezone_abbrevs() pg_timezone_abbrevs(abbrev, utc_offset, is_dst); - pg_timezone_names | SELECT pg_timezone_names.name, pg_timezone_names.abbrev, pg_timezone_names.utc_offset, pg_timezone_names.is_dst FROM pg_timezone_names() pg_timezone_names(name, abbrev, utc_offset, is_dst); - pg_user | SELECT pg_shadow.usename, pg_shadow.usesysid, pg_shadow.usecreatedb, pg_shadow.usesuper, pg_shadow.usecatupd, pg_shadow.userepl, '********'::text AS passwd, pg_shadow.valuntil, pg_shadow.useconfig FROM pg_shadow; - pg_user_mappings | SELECT u.oid AS umid, s.oid AS srvid, s.srvname, u.umuser, CASE WHEN (u.umuser = (0)::oid) THEN 'public'::name ELSE a.rolname END AS usename, CASE WHEN (pg_has_role(s.srvowner, 'USAGE'::text) OR has_server_privilege(s.oid, 'USAGE'::text)) THEN u.umoptions ELSE NULL::text[] END AS umoptions FROM ((pg_user_mapping u LEFT JOIN pg_authid a ON ((a.oid = u.umuser))) JOIN pg_foreign_server s ON ((u.umserver = s.oid))); - pg_views | SELECT n.nspname AS schemaname, c.relname AS viewname, pg_get_userbyid(c.relowner) AS viewowner, pg_get_viewdef(c.oid) AS definition FROM (pg_class c LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = 'v'::"char"); - rtest_v1 | SELECT rtest_t1.a, rtest_t1.b FROM rtest_t1; - rtest_vcomp | SELECT x.part, (x.size * y.factor) AS size_in_cm FROM rtest_comp x, rtest_unitfact y WHERE (x.unit = y.unit); - rtest_vview1 | SELECT x.a, x.b FROM rtest_view1 x WHERE (0 < (SELECT count(*) AS count FROM rtest_view2 y WHERE (y.a = x.a))); - rtest_vview2 | SELECT rtest_view1.a, rtest_view1.b FROM rtest_view1 WHERE rtest_view1.v; - rtest_vview3 | SELECT x.a, x.b FROM rtest_vview2 x WHERE (0 < (SELECT count(*) AS count FROM rtest_view2 y WHERE (y.a = x.a))); - rtest_vview4 | SELECT x.a, x.b, count(y.a) AS refcount FROM rtest_view1 x, rtest_view2 y WHERE (x.a = y.a) GROUP BY x.a, x.b; - rtest_vview5 | SELECT rtest_view1.a, rtest_view1.b, rtest_viewfunc1(rtest_view1.a) AS refcount FROM rtest_view1; - shoe | SELECT sh.shoename, sh.sh_avail, sh.slcolor, sh.slminlen, (sh.slminlen * un.un_fact) AS slminlen_cm, sh.slmaxlen, (sh.slmaxlen * un.un_fact) AS slmaxlen_cm, sh.slunit FROM shoe_data sh, unit un WHERE (sh.slunit = un.un_name); - shoe_ready | SELECT rsh.shoename, rsh.sh_avail, rsl.sl_name, rsl.sl_avail, int4smaller(rsh.sh_avail, rsl.sl_avail) AS total_avail FROM shoe rsh, shoelace rsl WHERE (((rsl.sl_color = rsh.slcolor) AND (rsl.sl_len_cm >= rsh.slminlen_cm)) AND (rsl.sl_len_cm <= rsh.slmaxlen_cm)); - shoelace | SELECT s.sl_name, s.sl_avail, s.sl_color, s.sl_len, s.sl_unit, (s.sl_len * u.un_fact) AS sl_len_cm FROM shoelace_data s, unit u WHERE (s.sl_unit = u.un_name); - shoelace_candelete | SELECT shoelace_obsolete.sl_name, shoelace_obsolete.sl_avail, shoelace_obsolete.sl_color, shoelace_obsolete.sl_len, shoelace_obsolete.sl_unit, shoelace_obsolete.sl_len_cm FROM shoelace_obsolete WHERE (shoelace_obsolete.sl_avail = 0); - shoelace_obsolete | SELECT shoelace.sl_name, shoelace.sl_avail, shoelace.sl_color, shoelace.sl_len, shoelace.sl_unit, shoelace.sl_len_cm FROM shoelace WHERE (NOT (EXISTS (SELECT shoe.shoename FROM shoe WHERE (shoe.slcolor = shoelace.sl_color)))); - street | SELECT r.name, r.thepath, c.cname FROM ONLY road r, real_city c WHERE (c.outline ## r.thepath); - toyemp | SELECT emp.name, emp.age, emp.location, (12 * emp.salary) AS annualsal FROM emp; -(58 rows) + viewname | definition +---------------------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + iexit | SELECT ih.name, ih.thepath, interpt_pp(ih.thepath, r.thepath) AS exit FROM ihighway ih, ramp r WHERE (ih.thepath ## r.thepath); + pg_available_extension_versions | SELECT e.name, e.version, (x.extname IS NOT NULL) AS installed, e.relocatable, e.schema, e.requires, e.comment FROM (pg_available_extension_versions() e(name, version, relocatable, schema, requires, comment) LEFT JOIN pg_extension x ON (((e.name = x.extname) AND (e.version = x.extversion)))); + pg_available_extensions | SELECT e.name, e.default_version, x.extversion AS installed_version, e.comment FROM (pg_available_extensions() e(name, default_version, comment) LEFT JOIN pg_extension x ON ((e.name = x.extname))); + pg_cursors | SELECT c.name, c.statement, c.is_holdable, c.is_binary, c.is_scrollable, c.creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time); + pg_group | SELECT pg_authid.rolname AS groname, pg_authid.oid AS grosysid, ARRAY(SELECT pg_auth_members.member FROM pg_auth_members WHERE (pg_auth_members.roleid = pg_authid.oid)) AS grolist FROM pg_authid WHERE (NOT pg_authid.rolcanlogin); + pg_indexes | SELECT n.nspname AS schemaname, c.relname AS tablename, i.relname AS indexname, t.spcname AS tablespace, pg_get_indexdef(i.oid) AS indexdef FROM ((((pg_index x JOIN pg_class c ON ((c.oid = x.indrelid))) JOIN pg_class i ON ((i.oid = x.indexrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = 'r'::"char") AND (i.relkind = 'i'::"char")); + pg_locks | SELECT l.locktype, l.database, l.relation, l.page, l.tuple, l.virtualxid, l.transactionid, l.classid, l.objid, l.objsubid, l.virtualtransaction, l.pid, l.mode, l.granted FROM pg_lock_status() l(locktype, database, relation, page, tuple, virtualxid, transactionid, classid, objid, objsubid, virtualtransaction, pid, mode, granted); + pg_prepared_statements | SELECT p.name, p.statement, p.prepare_time, p.parameter_types, p.from_sql FROM pg_prepared_statement() p(name, statement, prepare_time, parameter_types, from_sql); + pg_prepared_xacts | SELECT p.transaction, p.gid, p.prepared, u.rolname AS owner, d.datname AS database FROM ((pg_prepared_xact() p(transaction, gid, prepared, ownerid, dbid) LEFT JOIN pg_authid u ON ((p.ownerid = u.oid))) LEFT JOIN pg_database d ON ((p.dbid = d.oid))); + pg_roles | SELECT pg_authid.rolname, pg_authid.rolsuper, pg_authid.rolinherit, pg_authid.rolcreaterole, pg_authid.rolcreatedb, pg_authid.rolcatupdate, pg_authid.rolcanlogin, pg_authid.rolreplication, pg_authid.rolconnlimit, '********'::text AS rolpassword, pg_authid.rolvaliduntil, s.setconfig AS rolconfig, pg_authid.oid FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))); + pg_rules | SELECT n.nspname AS schemaname, c.relname AS tablename, r.rulename, pg_get_ruledef(r.oid) AS definition FROM ((pg_rewrite r JOIN pg_class c ON ((c.oid = r.ev_class))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (r.rulename <> '_RETURN'::name); + pg_seclabels | (((((SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (rel.relkind = 'r'::"char") THEN 'table'::text WHEN (rel.relkind = 'v'::"char") THEN 'view'::text WHEN (rel.relkind = 'S'::"char") THEN 'sequence'::text WHEN (rel.relkind = 'f'::"char") THEN 'foreign table'::text ELSE NULL::text END AS objtype, rel.relnamespace AS objnamespace, CASE WHEN pg_table_is_visible(rel.oid) THEN quote_ident((rel.relname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((rel.relname)::text)) END AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_class rel ON (((l.classoid = rel.tableoid) AND (l.objoid = rel.oid)))) JOIN pg_namespace nsp ON ((rel.relnamespace = nsp.oid))) WHERE (l.objsubid = 0) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'column'::text AS objtype, rel.relnamespace AS objnamespace, ((CASE WHEN pg_table_is_visible(rel.oid) THEN quote_ident((rel.relname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((rel.relname)::text)) END || '.'::text) || (att.attname)::text) AS objname, l.provider, l.label FROM (((pg_seclabel l JOIN pg_class rel ON (((l.classoid = rel.tableoid) AND (l.objoid = rel.oid)))) JOIN pg_attribute att ON (((rel.oid = att.attrelid) AND (l.objsubid = att.attnum)))) JOIN pg_namespace nsp ON ((rel.relnamespace = nsp.oid))) WHERE (l.objsubid <> 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (pro.proisagg = true) THEN 'aggregate'::text WHEN (pro.proisagg = false) THEN 'function'::text ELSE NULL::text END AS objtype, pro.pronamespace AS objnamespace, (((CASE WHEN pg_function_is_visible(pro.oid) THEN quote_ident((pro.proname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((pro.proname)::text)) END || '('::text) || pg_get_function_arguments(pro.oid)) || ')'::text) AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_proc pro ON (((l.classoid = pro.tableoid) AND (l.objoid = pro.oid)))) JOIN pg_namespace nsp ON ((pro.pronamespace = nsp.oid))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, CASE WHEN (typ.typtype = 'd'::"char") THEN 'domain'::text ELSE 'type'::text END AS objtype, typ.typnamespace AS objnamespace, CASE WHEN pg_type_is_visible(typ.oid) THEN quote_ident((typ.typname)::text) ELSE ((quote_ident((nsp.nspname)::text) || '.'::text) || quote_ident((typ.typname)::text)) END AS objname, l.provider, l.label FROM ((pg_seclabel l JOIN pg_type typ ON (((l.classoid = typ.tableoid) AND (l.objoid = typ.oid)))) JOIN pg_namespace nsp ON ((typ.typnamespace = nsp.oid))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'large object'::text AS objtype, NULL::oid AS objnamespace, (l.objoid)::text AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_largeobject_metadata lom ON ((l.objoid = lom.oid))) WHERE ((l.classoid = ('pg_largeobject'::regclass)::oid) AND (l.objsubid = 0))) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'language'::text AS objtype, NULL::oid AS objnamespace, quote_ident((lan.lanname)::text) AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_language lan ON (((l.classoid = lan.tableoid) AND (l.objoid = lan.oid)))) WHERE (l.objsubid = 0)) UNION ALL SELECT l.objoid, l.classoid, l.objsubid, 'schema'::text AS objtype, nsp.oid AS objnamespace, quote_ident((nsp.nspname)::text) AS objname, l.provider, l.label FROM (pg_seclabel l JOIN pg_namespace nsp ON (((l.classoid = nsp.tableoid) AND (l.objoid = nsp.oid)))) WHERE (l.objsubid = 0); + pg_settings | SELECT a.name, a.setting, a.unit, a.category, a.short_desc, a.extra_desc, a.context, a.vartype, a.source, a.min_val, a.max_val, a.enumvals, a.boot_val, a.reset_val, a.sourcefile, a.sourceline FROM pg_show_all_settings() a(name, setting, unit, category, short_desc, extra_desc, context, vartype, source, min_val, max_val, enumvals, boot_val, reset_val, sourcefile, sourceline); + pg_shadow | SELECT pg_authid.rolname AS usename, pg_authid.oid AS usesysid, pg_authid.rolcreatedb AS usecreatedb, pg_authid.rolsuper AS usesuper, pg_authid.rolcatupdate AS usecatupd, pg_authid.rolreplication AS userepl, pg_authid.rolpassword AS passwd, (pg_authid.rolvaliduntil)::abstime AS valuntil, s.setconfig AS useconfig FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))) WHERE pg_authid.rolcanlogin; + pg_stat_activity | SELECT s.datid, d.datname, s.procpid, s.usesysid, u.rolname AS usename, s.application_name, s.client_addr, s.client_port, s.backend_start, s.xact_start, s.query_start, s.waiting, s.current_query FROM pg_database d, pg_stat_get_activity(NULL::integer) s(datid, procpid, usesysid, application_name, current_query, waiting, xact_start, query_start, backend_start, client_addr, client_port), pg_authid u WHERE ((s.datid = d.oid) AND (s.usesysid = u.oid)); + pg_stat_all_indexes | SELECT c.oid AS relid, i.oid AS indexrelid, n.nspname AS schemaname, c.relname, i.relname AS indexrelname, pg_stat_get_numscans(i.oid) AS idx_scan, pg_stat_get_tuples_returned(i.oid) AS idx_tup_read, pg_stat_get_tuples_fetched(i.oid) AS idx_tup_fetch FROM (((pg_class c JOIN pg_index x ON ((c.oid = x.indrelid))) JOIN pg_class i ON ((i.oid = x.indexrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char"])); + pg_stat_all_tables | SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, pg_stat_get_numscans(c.oid) AS seq_scan, pg_stat_get_tuples_returned(c.oid) AS seq_tup_read, (sum(pg_stat_get_numscans(i.indexrelid)))::bigint AS idx_scan, ((sum(pg_stat_get_tuples_fetched(i.indexrelid)))::bigint + pg_stat_get_tuples_fetched(c.oid)) AS idx_tup_fetch, pg_stat_get_tuples_inserted(c.oid) AS n_tup_ins, pg_stat_get_tuples_updated(c.oid) AS n_tup_upd, pg_stat_get_tuples_deleted(c.oid) AS n_tup_del, pg_stat_get_tuples_hot_updated(c.oid) AS n_tup_hot_upd, pg_stat_get_live_tuples(c.oid) AS n_live_tup, pg_stat_get_dead_tuples(c.oid) AS n_dead_tup, pg_stat_get_last_vacuum_time(c.oid) AS last_vacuum, pg_stat_get_last_autovacuum_time(c.oid) AS last_autovacuum, pg_stat_get_last_analyze_time(c.oid) AS last_analyze, pg_stat_get_last_autoanalyze_time(c.oid) AS last_autoanalyze, pg_stat_get_vacuum_count(c.oid) AS vacuum_count, pg_stat_get_autovacuum_count(c.oid) AS autovacuum_count, pg_stat_get_analyze_count(c.oid) AS analyze_count, pg_stat_get_autoanalyze_count(c.oid) AS autoanalyze_count FROM ((pg_class c LEFT JOIN pg_index i ON ((c.oid = i.indrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char"])) GROUP BY c.oid, n.nspname, c.relname; + pg_stat_bgwriter | SELECT pg_stat_get_bgwriter_timed_checkpoints() AS checkpoints_timed, pg_stat_get_bgwriter_requested_checkpoints() AS checkpoints_req, pg_stat_get_bgwriter_buf_written_checkpoints() AS buffers_checkpoint, pg_stat_get_bgwriter_buf_written_clean() AS buffers_clean, pg_stat_get_bgwriter_maxwritten_clean() AS maxwritten_clean, pg_stat_get_buf_written_backend() AS buffers_backend, pg_stat_get_buf_fsync_backend() AS buffers_backend_fsync, pg_stat_get_buf_alloc() AS buffers_alloc, pg_stat_get_bgwriter_stat_reset_time() AS stats_reset; + pg_stat_database | SELECT d.oid AS datid, d.datname, pg_stat_get_db_numbackends(d.oid) AS numbackends, pg_stat_get_db_xact_commit(d.oid) AS xact_commit, pg_stat_get_db_xact_rollback(d.oid) AS xact_rollback, (pg_stat_get_db_blocks_fetched(d.oid) - pg_stat_get_db_blocks_hit(d.oid)) AS blks_read, pg_stat_get_db_blocks_hit(d.oid) AS blks_hit, pg_stat_get_db_tuples_returned(d.oid) AS tup_returned, pg_stat_get_db_tuples_fetched(d.oid) AS tup_fetched, pg_stat_get_db_tuples_inserted(d.oid) AS tup_inserted, pg_stat_get_db_tuples_updated(d.oid) AS tup_updated, pg_stat_get_db_tuples_deleted(d.oid) AS tup_deleted, pg_stat_get_db_conflict_all(d.oid) AS conflicts, pg_stat_get_db_stat_reset_time(d.oid) AS stats_reset FROM pg_database d; + pg_stat_database_conflicts | SELECT d.oid AS datid, d.datname, pg_stat_get_db_conflict_tablespace(d.oid) AS confl_tablespace, pg_stat_get_db_conflict_lock(d.oid) AS confl_lock, pg_stat_get_db_conflict_snapshot(d.oid) AS confl_snapshot, pg_stat_get_db_conflict_bufferpin(d.oid) AS confl_bufferpin, pg_stat_get_db_conflict_startup_deadlock(d.oid) AS confl_deadlock FROM pg_database d; + pg_stat_replication | SELECT s.procpid, s.usesysid, u.rolname AS usename, s.application_name, s.client_addr, s.client_port, s.backend_start, w.state, w.sent_location, w.write_location, w.flush_location, w.apply_location FROM pg_stat_get_activity(NULL::integer) s(datid, procpid, usesysid, application_name, current_query, waiting, xact_start, query_start, backend_start, client_addr, client_port), pg_authid u, pg_stat_get_wal_senders() w(procpid, state, sent_location, write_location, flush_location, apply_location) WHERE ((s.usesysid = u.oid) AND (s.procpid = w.procpid)); + pg_stat_sys_indexes | SELECT pg_stat_all_indexes.relid, pg_stat_all_indexes.indexrelid, pg_stat_all_indexes.schemaname, pg_stat_all_indexes.relname, pg_stat_all_indexes.indexrelname, pg_stat_all_indexes.idx_scan, pg_stat_all_indexes.idx_tup_read, pg_stat_all_indexes.idx_tup_fetch FROM pg_stat_all_indexes WHERE ((pg_stat_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_indexes.schemaname ~ '^pg_toast'::text)); + pg_stat_sys_tables | SELECT pg_stat_all_tables.relid, pg_stat_all_tables.schemaname, pg_stat_all_tables.relname, pg_stat_all_tables.seq_scan, pg_stat_all_tables.seq_tup_read, pg_stat_all_tables.idx_scan, pg_stat_all_tables.idx_tup_fetch, pg_stat_all_tables.n_tup_ins, pg_stat_all_tables.n_tup_upd, pg_stat_all_tables.n_tup_del, pg_stat_all_tables.n_tup_hot_upd, pg_stat_all_tables.n_live_tup, pg_stat_all_tables.n_dead_tup, pg_stat_all_tables.last_vacuum, pg_stat_all_tables.last_autovacuum, pg_stat_all_tables.last_analyze, pg_stat_all_tables.last_autoanalyze, pg_stat_all_tables.vacuum_count, pg_stat_all_tables.autovacuum_count, pg_stat_all_tables.analyze_count, pg_stat_all_tables.autoanalyze_count FROM pg_stat_all_tables WHERE ((pg_stat_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_all_tables.schemaname ~ '^pg_toast'::text)); + pg_stat_user_functions | SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, pg_stat_get_function_calls(p.oid) AS calls, (pg_stat_get_function_time(p.oid) / 1000) AS total_time, (pg_stat_get_function_self_time(p.oid) / 1000) AS self_time FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_function_calls(p.oid) IS NOT NULL)); + pg_stat_user_indexes | SELECT pg_stat_all_indexes.relid, pg_stat_all_indexes.indexrelid, pg_stat_all_indexes.schemaname, pg_stat_all_indexes.relname, pg_stat_all_indexes.indexrelname, pg_stat_all_indexes.idx_scan, pg_stat_all_indexes.idx_tup_read, pg_stat_all_indexes.idx_tup_fetch FROM pg_stat_all_indexes WHERE ((pg_stat_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_indexes.schemaname !~ '^pg_toast'::text)); + pg_stat_user_tables | SELECT pg_stat_all_tables.relid, pg_stat_all_tables.schemaname, pg_stat_all_tables.relname, pg_stat_all_tables.seq_scan, pg_stat_all_tables.seq_tup_read, pg_stat_all_tables.idx_scan, pg_stat_all_tables.idx_tup_fetch, pg_stat_all_tables.n_tup_ins, pg_stat_all_tables.n_tup_upd, pg_stat_all_tables.n_tup_del, pg_stat_all_tables.n_tup_hot_upd, pg_stat_all_tables.n_live_tup, pg_stat_all_tables.n_dead_tup, pg_stat_all_tables.last_vacuum, pg_stat_all_tables.last_autovacuum, pg_stat_all_tables.last_analyze, pg_stat_all_tables.last_autoanalyze, pg_stat_all_tables.vacuum_count, pg_stat_all_tables.autovacuum_count, pg_stat_all_tables.analyze_count, pg_stat_all_tables.autoanalyze_count FROM pg_stat_all_tables WHERE ((pg_stat_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_all_tables.schemaname !~ '^pg_toast'::text)); + pg_stat_xact_all_tables | SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, pg_stat_get_xact_numscans(c.oid) AS seq_scan, pg_stat_get_xact_tuples_returned(c.oid) AS seq_tup_read, (sum(pg_stat_get_xact_numscans(i.indexrelid)))::bigint AS idx_scan, ((sum(pg_stat_get_xact_tuples_fetched(i.indexrelid)))::bigint + pg_stat_get_xact_tuples_fetched(c.oid)) AS idx_tup_fetch, pg_stat_get_xact_tuples_inserted(c.oid) AS n_tup_ins, pg_stat_get_xact_tuples_updated(c.oid) AS n_tup_upd, pg_stat_get_xact_tuples_deleted(c.oid) AS n_tup_del, pg_stat_get_xact_tuples_hot_updated(c.oid) AS n_tup_hot_upd FROM ((pg_class c LEFT JOIN pg_index i ON ((c.oid = i.indrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char"])) GROUP BY c.oid, n.nspname, c.relname; + pg_stat_xact_sys_tables | SELECT pg_stat_xact_all_tables.relid, pg_stat_xact_all_tables.schemaname, pg_stat_xact_all_tables.relname, pg_stat_xact_all_tables.seq_scan, pg_stat_xact_all_tables.seq_tup_read, pg_stat_xact_all_tables.idx_scan, pg_stat_xact_all_tables.idx_tup_fetch, pg_stat_xact_all_tables.n_tup_ins, pg_stat_xact_all_tables.n_tup_upd, pg_stat_xact_all_tables.n_tup_del, pg_stat_xact_all_tables.n_tup_hot_upd FROM pg_stat_xact_all_tables WHERE ((pg_stat_xact_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_stat_xact_all_tables.schemaname ~ '^pg_toast'::text)); + pg_stat_xact_user_functions | SELECT p.oid AS funcid, n.nspname AS schemaname, p.proname AS funcname, pg_stat_get_xact_function_calls(p.oid) AS calls, (pg_stat_get_xact_function_time(p.oid) / 1000) AS total_time, (pg_stat_get_xact_function_self_time(p.oid) / 1000) AS self_time FROM (pg_proc p LEFT JOIN pg_namespace n ON ((n.oid = p.pronamespace))) WHERE ((p.prolang <> (12)::oid) AND (pg_stat_get_xact_function_calls(p.oid) IS NOT NULL)); + pg_stat_xact_user_tables | SELECT pg_stat_xact_all_tables.relid, pg_stat_xact_all_tables.schemaname, pg_stat_xact_all_tables.relname, pg_stat_xact_all_tables.seq_scan, pg_stat_xact_all_tables.seq_tup_read, pg_stat_xact_all_tables.idx_scan, pg_stat_xact_all_tables.idx_tup_fetch, pg_stat_xact_all_tables.n_tup_ins, pg_stat_xact_all_tables.n_tup_upd, pg_stat_xact_all_tables.n_tup_del, pg_stat_xact_all_tables.n_tup_hot_upd FROM pg_stat_xact_all_tables WHERE ((pg_stat_xact_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_stat_xact_all_tables.schemaname !~ '^pg_toast'::text)); + pg_statio_all_indexes | SELECT c.oid AS relid, i.oid AS indexrelid, n.nspname AS schemaname, c.relname, i.relname AS indexrelname, (pg_stat_get_blocks_fetched(i.oid) - pg_stat_get_blocks_hit(i.oid)) AS idx_blks_read, pg_stat_get_blocks_hit(i.oid) AS idx_blks_hit FROM (((pg_class c JOIN pg_index x ON ((c.oid = x.indrelid))) JOIN pg_class i ON ((i.oid = x.indexrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char"])); + pg_statio_all_sequences | SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, (pg_stat_get_blocks_fetched(c.oid) - pg_stat_get_blocks_hit(c.oid)) AS blks_read, pg_stat_get_blocks_hit(c.oid) AS blks_hit FROM (pg_class c LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = 'S'::"char"); + pg_statio_all_tables | SELECT c.oid AS relid, n.nspname AS schemaname, c.relname, (pg_stat_get_blocks_fetched(c.oid) - pg_stat_get_blocks_hit(c.oid)) AS heap_blks_read, pg_stat_get_blocks_hit(c.oid) AS heap_blks_hit, (sum((pg_stat_get_blocks_fetched(i.indexrelid) - pg_stat_get_blocks_hit(i.indexrelid))))::bigint AS idx_blks_read, (sum(pg_stat_get_blocks_hit(i.indexrelid)))::bigint AS idx_blks_hit, (pg_stat_get_blocks_fetched(t.oid) - pg_stat_get_blocks_hit(t.oid)) AS toast_blks_read, pg_stat_get_blocks_hit(t.oid) AS toast_blks_hit, (pg_stat_get_blocks_fetched(x.oid) - pg_stat_get_blocks_hit(x.oid)) AS tidx_blks_read, pg_stat_get_blocks_hit(x.oid) AS tidx_blks_hit FROM ((((pg_class c LEFT JOIN pg_index i ON ((c.oid = i.indrelid))) LEFT JOIN pg_class t ON ((c.reltoastrelid = t.oid))) LEFT JOIN pg_class x ON ((t.reltoastidxid = x.oid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = ANY (ARRAY['r'::"char", 't'::"char"])) GROUP BY c.oid, n.nspname, c.relname, t.oid, x.oid; + pg_statio_sys_indexes | SELECT pg_statio_all_indexes.relid, pg_statio_all_indexes.indexrelid, pg_statio_all_indexes.schemaname, pg_statio_all_indexes.relname, pg_statio_all_indexes.indexrelname, pg_statio_all_indexes.idx_blks_read, pg_statio_all_indexes.idx_blks_hit FROM pg_statio_all_indexes WHERE ((pg_statio_all_indexes.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_indexes.schemaname ~ '^pg_toast'::text)); + pg_statio_sys_sequences | SELECT pg_statio_all_sequences.relid, pg_statio_all_sequences.schemaname, pg_statio_all_sequences.relname, pg_statio_all_sequences.blks_read, pg_statio_all_sequences.blks_hit FROM pg_statio_all_sequences WHERE ((pg_statio_all_sequences.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_sequences.schemaname ~ '^pg_toast'::text)); + pg_statio_sys_tables | SELECT pg_statio_all_tables.relid, pg_statio_all_tables.schemaname, pg_statio_all_tables.relname, pg_statio_all_tables.heap_blks_read, pg_statio_all_tables.heap_blks_hit, pg_statio_all_tables.idx_blks_read, pg_statio_all_tables.idx_blks_hit, pg_statio_all_tables.toast_blks_read, pg_statio_all_tables.toast_blks_hit, pg_statio_all_tables.tidx_blks_read, pg_statio_all_tables.tidx_blks_hit FROM pg_statio_all_tables WHERE ((pg_statio_all_tables.schemaname = ANY (ARRAY['pg_catalog'::name, 'information_schema'::name])) OR (pg_statio_all_tables.schemaname ~ '^pg_toast'::text)); + pg_statio_user_indexes | SELECT pg_statio_all_indexes.relid, pg_statio_all_indexes.indexrelid, pg_statio_all_indexes.schemaname, pg_statio_all_indexes.relname, pg_statio_all_indexes.indexrelname, pg_statio_all_indexes.idx_blks_read, pg_statio_all_indexes.idx_blks_hit FROM pg_statio_all_indexes WHERE ((pg_statio_all_indexes.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_indexes.schemaname !~ '^pg_toast'::text)); + pg_statio_user_sequences | SELECT pg_statio_all_sequences.relid, pg_statio_all_sequences.schemaname, pg_statio_all_sequences.relname, pg_statio_all_sequences.blks_read, pg_statio_all_sequences.blks_hit FROM pg_statio_all_sequences WHERE ((pg_statio_all_sequences.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_sequences.schemaname !~ '^pg_toast'::text)); + pg_statio_user_tables | SELECT pg_statio_all_tables.relid, pg_statio_all_tables.schemaname, pg_statio_all_tables.relname, pg_statio_all_tables.heap_blks_read, pg_statio_all_tables.heap_blks_hit, pg_statio_all_tables.idx_blks_read, pg_statio_all_tables.idx_blks_hit, pg_statio_all_tables.toast_blks_read, pg_statio_all_tables.toast_blks_hit, pg_statio_all_tables.tidx_blks_read, pg_statio_all_tables.tidx_blks_hit FROM pg_statio_all_tables WHERE ((pg_statio_all_tables.schemaname <> ALL (ARRAY['pg_catalog'::name, 'information_schema'::name])) AND (pg_statio_all_tables.schemaname !~ '^pg_toast'::text)); + pg_stats | SELECT n.nspname AS schemaname, c.relname AS tablename, a.attname, s.stainherit AS inherited, s.stanullfrac AS null_frac, s.stawidth AS avg_width, s.stadistinct AS n_distinct, CASE WHEN (s.stakind1 = ANY (ARRAY[1, 4])) THEN s.stavalues1 WHEN (s.stakind2 = ANY (ARRAY[1, 4])) THEN s.stavalues2 WHEN (s.stakind3 = ANY (ARRAY[1, 4])) THEN s.stavalues3 WHEN (s.stakind4 = ANY (ARRAY[1, 4])) THEN s.stavalues4 ELSE NULL::anyarray END AS most_common_vals, CASE WHEN (s.stakind1 = ANY (ARRAY[1, 4])) THEN s.stanumbers1 WHEN (s.stakind2 = ANY (ARRAY[1, 4])) THEN s.stanumbers2 WHEN (s.stakind3 = ANY (ARRAY[1, 4])) THEN s.stanumbers3 WHEN (s.stakind4 = ANY (ARRAY[1, 4])) THEN s.stanumbers4 ELSE NULL::real[] END AS most_common_freqs, CASE WHEN (s.stakind1 = 2) THEN s.stavalues1 WHEN (s.stakind2 = 2) THEN s.stavalues2 WHEN (s.stakind3 = 2) THEN s.stavalues3 WHEN (s.stakind4 = 2) THEN s.stavalues4 ELSE NULL::anyarray END AS histogram_bounds, CASE WHEN (s.stakind1 = 3) THEN s.stanumbers1[1] WHEN (s.stakind2 = 3) THEN s.stanumbers2[1] WHEN (s.stakind3 = 3) THEN s.stanumbers3[1] WHEN (s.stakind4 = 3) THEN s.stanumbers4[1] ELSE NULL::real END AS correlation FROM (((pg_statistic s JOIN pg_class c ON ((c.oid = s.starelid))) JOIN pg_attribute a ON (((c.oid = a.attrelid) AND (a.attnum = s.staattnum)))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE ((NOT a.attisdropped) AND has_column_privilege(c.oid, a.attnum, 'select'::text)); + pg_tables | SELECT n.nspname AS schemaname, c.relname AS tablename, pg_get_userbyid(c.relowner) AS tableowner, t.spcname AS tablespace, c.relhasindex AS hasindexes, c.relhasrules AS hasrules, c.relhastriggers AS hastriggers FROM ((pg_class c LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = c.reltablespace))) WHERE (c.relkind = 'r'::"char"); + pg_timezone_abbrevs | SELECT pg_timezone_abbrevs.abbrev, pg_timezone_abbrevs.utc_offset, pg_timezone_abbrevs.is_dst FROM pg_timezone_abbrevs() pg_timezone_abbrevs(abbrev, utc_offset, is_dst); + pg_timezone_names | SELECT pg_timezone_names.name, pg_timezone_names.abbrev, pg_timezone_names.utc_offset, pg_timezone_names.is_dst FROM pg_timezone_names() pg_timezone_names(name, abbrev, utc_offset, is_dst); + pg_user | SELECT pg_shadow.usename, pg_shadow.usesysid, pg_shadow.usecreatedb, pg_shadow.usesuper, pg_shadow.usecatupd, pg_shadow.userepl, '********'::text AS passwd, pg_shadow.valuntil, pg_shadow.useconfig FROM pg_shadow; + pg_user_mappings | SELECT u.oid AS umid, s.oid AS srvid, s.srvname, u.umuser, CASE WHEN (u.umuser = (0)::oid) THEN 'public'::name ELSE a.rolname END AS usename, CASE WHEN (pg_has_role(s.srvowner, 'USAGE'::text) OR has_server_privilege(s.oid, 'USAGE'::text)) THEN u.umoptions ELSE NULL::text[] END AS umoptions FROM ((pg_user_mapping u LEFT JOIN pg_authid a ON ((a.oid = u.umuser))) JOIN pg_foreign_server s ON ((u.umserver = s.oid))); + pg_views | SELECT n.nspname AS schemaname, c.relname AS viewname, pg_get_userbyid(c.relowner) AS viewowner, pg_get_viewdef(c.oid) AS definition FROM (pg_class c LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (c.relkind = 'v'::"char"); + rtest_v1 | SELECT rtest_t1.a, rtest_t1.b FROM rtest_t1; + rtest_vcomp | SELECT x.part, (x.size * y.factor) AS size_in_cm FROM rtest_comp x, rtest_unitfact y WHERE (x.unit = y.unit); + rtest_vview1 | SELECT x.a, x.b FROM rtest_view1 x WHERE (0 < (SELECT count(*) AS count FROM rtest_view2 y WHERE (y.a = x.a))); + rtest_vview2 | SELECT rtest_view1.a, rtest_view1.b FROM rtest_view1 WHERE rtest_view1.v; + rtest_vview3 | SELECT x.a, x.b FROM rtest_vview2 x WHERE (0 < (SELECT count(*) AS count FROM rtest_view2 y WHERE (y.a = x.a))); + rtest_vview4 | SELECT x.a, x.b, count(y.a) AS refcount FROM rtest_view1 x, rtest_view2 y WHERE (x.a = y.a) GROUP BY x.a, x.b; + rtest_vview5 | SELECT rtest_view1.a, rtest_view1.b, rtest_viewfunc1(rtest_view1.a) AS refcount FROM rtest_view1; + shoe | SELECT sh.shoename, sh.sh_avail, sh.slcolor, sh.slminlen, (sh.slminlen * un.un_fact) AS slminlen_cm, sh.slmaxlen, (sh.slmaxlen * un.un_fact) AS slmaxlen_cm, sh.slunit FROM shoe_data sh, unit un WHERE (sh.slunit = un.un_name); + shoe_ready | SELECT rsh.shoename, rsh.sh_avail, rsl.sl_name, rsl.sl_avail, int4smaller(rsh.sh_avail, rsl.sl_avail) AS total_avail FROM shoe rsh, shoelace rsl WHERE (((rsl.sl_color = rsh.slcolor) AND (rsl.sl_len_cm >= rsh.slminlen_cm)) AND (rsl.sl_len_cm <= rsh.slmaxlen_cm)); + shoelace | SELECT s.sl_name, s.sl_avail, s.sl_color, s.sl_len, s.sl_unit, (s.sl_len * u.un_fact) AS sl_len_cm FROM shoelace_data s, unit u WHERE (s.sl_unit = u.un_name); + shoelace_candelete | SELECT shoelace_obsolete.sl_name, shoelace_obsolete.sl_avail, shoelace_obsolete.sl_color, shoelace_obsolete.sl_len, shoelace_obsolete.sl_unit, shoelace_obsolete.sl_len_cm FROM shoelace_obsolete WHERE (shoelace_obsolete.sl_avail = 0); + shoelace_obsolete | SELECT shoelace.sl_name, shoelace.sl_avail, shoelace.sl_color, shoelace.sl_len, shoelace.sl_unit, shoelace.sl_len_cm FROM shoelace WHERE (NOT (EXISTS (SELECT shoe.shoename FROM shoe WHERE (shoe.slcolor = shoelace.sl_color)))); + street | SELECT r.name, r.thepath, c.cname FROM ONLY road r, real_city c WHERE (c.outline ## r.thepath); + toyemp | SELECT emp.name, emp.age, emp.location, (12 * emp.salary) AS annualsal FROM emp; +(60 rows) SELECT tablename, rulename, definition FROM pg_rules ORDER BY tablename, rulename; diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out index 1ee820fd7c..ab9e891788 100644 --- a/src/test/regress/expected/sanity_check.out +++ b/src/test/regress/expected/sanity_check.out @@ -92,6 +92,7 @@ SELECT relname, relhasindex pg_authid | t pg_cast | t pg_class | t + pg_collation | t pg_constraint | t pg_conversion | t pg_database | t @@ -100,6 +101,7 @@ SELECT relname, relhasindex pg_depend | t pg_description | t pg_enum | t + pg_extension | t pg_foreign_data_wrapper | t pg_foreign_server | t pg_foreign_table | t @@ -155,7 +157,7 @@ SELECT relname, relhasindex timetz_tbl | f tinterval_tbl | f varchar_tbl | f -(144 rows) +(146 rows) -- -- another sanity check: every system catalog that has OIDs should have diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c index a3211622b9..a6160298ef 100644 --- a/src/test/regress/pg_regress.c +++ b/src/test/regress/pg_regress.c @@ -2094,7 +2094,7 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc /* "make install" */ #ifndef WIN32_ONLY_COMPILER snprintf(buf, sizeof(buf), - SYSTEMQUOTE "\"%s\" -C \"%s\" DESTDIR=\"%s/install\" install with_perl=no with_python=no > \"%s/log/install.log\" 2>&1" SYSTEMQUOTE, + SYSTEMQUOTE "\"%s\" -C \"%s\" DESTDIR=\"%s/install\" install > \"%s/log/install.log\" 2>&1" SYSTEMQUOTE, makeprog, top_builddir, temp_install, outputdir); #else snprintf(buf, sizeof(buf), diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql index cfbfbb6b43..6531a9f162 100644 --- a/src/test/regress/sql/alter_table.sql +++ b/src/test/regress/sql/alter_table.sql @@ -221,6 +221,21 @@ DELETE FROM tmp3 where a=5; -- Try (and succeed) ALTER TABLE tmp3 add constraint tmpconstr foreign key (a) references tmp2 match full; +ALTER TABLE tmp3 drop constraint tmpconstr; + +INSERT INTO tmp3 values (5,50); + +-- Try NOT VALID and then VALIDATE CONSTRAINT, but fails. Delete failure then re-validate +ALTER TABLE tmp3 add constraint tmpconstr foreign key (a) references tmp2 match full NOT VALID; +ALTER TABLE tmp3 validate constraint tmpconstr; + +-- Delete failing row +DELETE FROM tmp3 where a=5; + +-- Try (and succeed) and repeat to show it works on already valid constraint +ALTER TABLE tmp3 validate constraint tmpconstr; +ALTER TABLE tmp3 validate constraint tmpconstr; + -- Try (and fail) to create constraint from tmp5(a) to tmp4(a) - unique constraint on -- tmp4 is a,b diff --git a/src/test/regress/sql/collate.linux.utf8.sql b/src/test/regress/sql/collate.linux.utf8.sql new file mode 100644 index 0000000000..856a497914 --- /dev/null +++ b/src/test/regress/sql/collate.linux.utf8.sql @@ -0,0 +1,286 @@ +/* + * This test is for Linux/glibc systems and assumes that a full set of + * locales is installed. It must be run in a UTF-8 locale. + */ + +SET client_encoding TO UTF8; + + +CREATE TABLE collate_test1 ( + a int, + b text COLLATE "en_US.utf8" NOT NULL +); + +\d collate_test1 + +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "ja_JP.eucjp" +); + +CREATE TABLE collate_test_fail ( + a int, + b text COLLATE "foo" +); + +CREATE TABLE collate_test_fail ( + a int COLLATE "en_US.utf8", + b text +); + +CREATE TABLE collate_test_like ( + LIKE collate_test1 +); + +\d collate_test_like + +CREATE TABLE collate_test2 ( + a int, + b text COLLATE "sv_SE.utf8" +); + +CREATE TABLE collate_test3 ( + a int, + b text COLLATE "C" +); + +INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'äbc'), (3, 'bbc'), (4, 'ABC'); +INSERT INTO collate_test2 SELECT * FROM collate_test1; +INSERT INTO collate_test3 SELECT * FROM collate_test1; + +SELECT * FROM collate_test1 WHERE b >= 'bbc'; +SELECT * FROM collate_test2 WHERE b >= 'bbc'; +SELECT * FROM collate_test3 WHERE b >= 'bbc'; +SELECT * FROM collate_test3 WHERE b >= 'BBC'; + +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; +SELECT * FROM collate_test1 WHERE b >= 'bbc' COLLATE "C"; +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "C"; +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US.utf8"; +SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc' COLLATE "en_US"; + + +CREATE DOMAIN testdomain_sv AS text COLLATE "sv_SE.utf8"; +CREATE DOMAIN testdomain_i AS int COLLATE "sv_SE.utf8"; -- fails +CREATE TABLE collate_test4 ( + a int, + b testdomain_sv +); +INSERT INTO collate_test4 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test4 ORDER BY b; + +CREATE TABLE collate_test5 ( + a int, + b testdomain_sv COLLATE "en_US.utf8" +); +INSERT INTO collate_test5 SELECT * FROM collate_test1; +SELECT a, b FROM collate_test5 ORDER BY b; + + +SELECT a, b FROM collate_test1 ORDER BY b; +SELECT a, b FROM collate_test2 ORDER BY b; +SELECT a, b FROM collate_test3 ORDER BY b; + +SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; + +-- star expansion +SELECT * FROM collate_test1 ORDER BY b; +SELECT * FROM collate_test2 ORDER BY b; +SELECT * FROM collate_test3 ORDER BY b; + +-- constant expression folding +SELECT 'bbc' COLLATE "en_US.utf8" > 'äbc' COLLATE "en_US.utf8" AS "true"; +SELECT 'bbc' COLLATE "sv_SE.utf8" > 'äbc' COLLATE "sv_SE.utf8" AS "false"; + +-- upper/lower + +CREATE TABLE collate_test10 ( + a int, + x text COLLATE "en_US.utf8", + y text COLLATE "tr_TR.utf8" +); + +INSERT INTO collate_test10 VALUES (1, 'hij', 'hij'), (2, 'HIJ', 'HIJ'); + +SELECT a, lower(x), lower(y), upper(x), upper(y), initcap(x), initcap(y) FROM collate_test10; +SELECT a, lower(x COLLATE "C"), lower(y COLLATE "C") FROM collate_test10; + +SELECT a, x, y FROM collate_test10 ORDER BY lower(y), a; + +-- LIKE/ILIKE + +SELECT * FROM collate_test1 WHERE b LIKE 'abc'; +SELECT * FROM collate_test1 WHERE b LIKE 'abc%'; +SELECT * FROM collate_test1 WHERE b LIKE '%bc%'; +SELECT * FROM collate_test1 WHERE b ILIKE 'abc'; +SELECT * FROM collate_test1 WHERE b ILIKE 'abc%'; +SELECT * FROM collate_test1 WHERE b ILIKE '%bc%'; + +SELECT 'Türkiye' COLLATE "en_US.utf8" ILIKE '%KI%' AS "true"; +SELECT 'Türkiye' COLLATE "tr_TR.utf8" ILIKE '%KI%' AS "false"; + +-- The following actually exercises the selectivity estimation for ILIKE. +SELECT relname FROM pg_class WHERE relname ILIKE 'abc%'; + + +-- to_char + +SET lc_time TO 'tr_TR.utf8'; +SELECT to_char(date '2010-04-01', 'DD TMMON YYYY'); +SELECT to_char(date '2010-04-01', 'DD TMMON YYYY' COLLATE "tr_TR.utf8"); + + +-- backwards parsing + +CREATE VIEW collview1 AS SELECT * FROM collate_test1 WHERE b COLLATE "C" >= 'bbc'; +CREATE VIEW collview2 AS SELECT a, b FROM collate_test1 ORDER BY b COLLATE "C"; +CREATE VIEW collview3 AS SELECT a, lower((x || x) COLLATE "C") FROM collate_test10; + +SELECT table_name, view_definition FROM information_schema.views + WHERE table_name LIKE 'collview%' ORDER BY 1; + + +-- collation propagation in various expression type + +SELECT a, coalesce(b, 'foo') FROM collate_test1 ORDER BY 2; +SELECT a, coalesce(b, 'foo') FROM collate_test2 ORDER BY 2; +SELECT a, coalesce(b, 'foo') FROM collate_test3 ORDER BY 2; +SELECT a, lower(coalesce(x, 'foo')), lower(coalesce(y, 'foo')) FROM collate_test10; + +SELECT a, b, greatest(b, 'CCC') FROM collate_test1 ORDER BY 3; +SELECT a, b, greatest(b, 'CCC') FROM collate_test2 ORDER BY 3; +SELECT a, b, greatest(b, 'CCC') FROM collate_test3 ORDER BY 3; +SELECT a, x, y, lower(greatest(x, 'foo')), lower(greatest(y, 'foo')) FROM collate_test10; + +SELECT a, nullif(b, 'abc') FROM collate_test1 ORDER BY 2; +SELECT a, nullif(b, 'abc') FROM collate_test2 ORDER BY 2; +SELECT a, nullif(b, 'abc') FROM collate_test3 ORDER BY 2; +SELECT a, lower(nullif(x, 'foo')), lower(nullif(y, 'foo')) FROM collate_test10; + +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test1 ORDER BY 2; +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test2 ORDER BY 2; +SELECT a, CASE b WHEN 'abc' THEN 'abcd' ELSE b END FROM collate_test3 ORDER BY 2; + +CREATE DOMAIN testdomain AS text; +SELECT a, b::testdomain FROM collate_test1 ORDER BY 2; +SELECT a, b::testdomain FROM collate_test2 ORDER BY 2; +SELECT a, b::testdomain FROM collate_test3 ORDER BY 2; +SELECT a, b::testdomain_sv FROM collate_test3 ORDER BY 2; +SELECT a, lower(x::testdomain), lower(y::testdomain) FROM collate_test10; + +SELECT min(b), max(b) FROM collate_test1; +SELECT min(b), max(b) FROM collate_test2; +SELECT min(b), max(b) FROM collate_test3; + +SELECT array_agg(b ORDER BY b) FROM collate_test1; +SELECT array_agg(b ORDER BY b) FROM collate_test2; +SELECT array_agg(b ORDER BY b) FROM collate_test3; + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test1 ORDER BY 2; +SELECT a, b FROM collate_test2 UNION SELECT a, b FROM collate_test2 ORDER BY 2; +SELECT a, b FROM collate_test3 WHERE a < 4 INTERSECT SELECT a, b FROM collate_test3 WHERE a > 1 ORDER BY 2; +SELECT a, b FROM collate_test3 EXCEPT SELECT a, b FROM collate_test3 WHERE a < 2 ORDER BY 2; + +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +SELECT a, b FROM collate_test1 UNION ALL SELECT a, b FROM collate_test3; -- ok +SELECT a, b FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +SELECT a, b COLLATE "C" FROM collate_test1 UNION SELECT a, b FROM collate_test3 ORDER BY 2; -- ok +SELECT a, b FROM collate_test1 INTERSECT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail +SELECT a, b FROM collate_test1 EXCEPT SELECT a, b FROM collate_test3 ORDER BY 2; -- fail + + +-- casting + +SELECT CAST('42' AS text COLLATE "C"); + +SELECT a, CAST(b AS varchar) FROM collate_test1 ORDER BY 2; +SELECT a, CAST(b AS varchar) FROM collate_test2 ORDER BY 2; +SELECT a, CAST(b AS varchar) FROM collate_test3 ORDER BY 2; + + +-- polymorphism + +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test1)) ORDER BY 1; +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test2)) ORDER BY 1; +SELECT * FROM unnest((SELECT array_agg(b ORDER BY b) FROM collate_test3)) ORDER BY 1; + +CREATE FUNCTION dup (f1 anyelement, f2 out anyelement, f3 out anyarray) + AS 'select $1, array[$1,$1]' LANGUAGE sql; + +SELECT a, (dup(b)).* FROM collate_test1 ORDER BY 2; +SELECT a, (dup(b)).* FROM collate_test2 ORDER BY 2; +SELECT a, (dup(b)).* FROM collate_test3 ORDER BY 2; + + +-- indexes + +CREATE INDEX collate_test1_idx1 ON collate_test1 (b); +CREATE INDEX collate_test1_idx2 ON collate_test1 (b COLLATE "C"); +CREATE INDEX collate_test1_idx3 ON collate_test1 ((b COLLATE "C")); -- this is different grammatically + +CREATE INDEX collate_test1_idx4 ON collate_test1 (a COLLATE "C"); -- fail +CREATE INDEX collate_test1_idx5 ON collate_test1 ((a COLLATE "C")); -- fail + +SELECT relname, pg_get_indexdef(oid) FROM pg_class WHERE relname LIKE 'collate_test%_idx%'; + + +-- schema manipulation commands + +CREATE ROLE regress_test_role; +CREATE SCHEMA test_schema; + +CREATE COLLATION test0 (locale = 'en_US.utf8'); +CREATE COLLATION test0 (locale = 'en_US.utf8'); -- fail +CREATE COLLATION test1 (lc_collate = 'en_US.utf8', lc_ctype = 'de_DE.utf8'); +CREATE COLLATION test2 (locale = 'en_US'); -- fail +CREATE COLLATION test3 (lc_collate = 'en_US.utf8'); -- fail + +CREATE COLLATION test4 FROM nonsense; +CREATE COLLATION test5 FROM test0; + +SELECT collname, collencoding, collcollate, collctype FROM pg_collation WHERE collname LIKE 'test%' ORDER BY 1; + +ALTER COLLATION test1 RENAME TO test11; +ALTER COLLATION test0 RENAME TO test11; -- fail +ALTER COLLATION test1 RENAME TO test22; -- fail + +ALTER COLLATION test11 OWNER TO regress_test_role; +ALTER COLLATION test11 OWNER TO nonsense; +ALTER COLLATION test11 SET SCHEMA test_schema; + +COMMENT ON COLLATION test0 IS 'US English'; + +SELECT collname, nspname, obj_description(pg_collation.oid, 'pg_collation') + FROM pg_collation JOIN pg_namespace ON (collnamespace = pg_namespace.oid) + WHERE collname LIKE 'test%' + ORDER BY 1; + +DROP COLLATION test0, test_schema.test11, test5; +DROP COLLATION test0; -- fail +DROP COLLATION IF EXISTS test0; + +SELECT collname FROM pg_collation WHERE collname LIKE 'test%'; + +DROP SCHEMA test_schema; +DROP ROLE regress_test_role; + + +-- dependencies + +CREATE COLLATION test0 (locale = 'en_US.utf8'); + +CREATE TABLE collate_dep_test1 (a int, b text COLLATE test0); +CREATE DOMAIN collate_dep_dom1 AS text COLLATE test0; +CREATE TYPE collate_dep_test2 AS (x int, y text COLLATE test0); +CREATE VIEW collate_dep_test3 AS SELECT text 'foo' COLLATE test0 AS foo; +CREATE TABLE collate_dep_test4t (a int, b text); +CREATE INDEX collate_dep_test4i ON collate_dep_test4t (b COLLATE test0); + +DROP COLLATION test0 RESTRICT; -- fail +DROP COLLATION test0 CASCADE; + +\d collate_dep_test1 +\d collate_dep_test2 + +DROP TABLE collate_dep_test1, collate_dep_test4t; +DROP TYPE collate_dep_test2; diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql index d0f4e3b5e1..14fb4578c6 100644 --- a/src/test/regress/sql/plpgsql.sql +++ b/src/test/regress/sql/plpgsql.sql @@ -3375,3 +3375,117 @@ $$ language plpgsql; select unreserved_test(); drop function unreserved_test(); + +-- +-- Test FOREACH over arrays +-- + +create function foreach_test(anyarray) +returns void as $$ +declare x int; +begin + foreach x in array $1 + loop + raise notice '%', x; + end loop; + end; +$$ language plpgsql; + +select foreach_test(ARRAY[1,2,3,4]); +select foreach_test(ARRAY[[1,2],[3,4]]); + +create or replace function foreach_test(anyarray) +returns void as $$ +declare x int; +begin + foreach x slice 1 in array $1 + loop + raise notice '%', x; + end loop; + end; +$$ language plpgsql; + +-- should fail +select foreach_test(ARRAY[1,2,3,4]); +select foreach_test(ARRAY[[1,2],[3,4]]); + +create or replace function foreach_test(anyarray) +returns void as $$ +declare x int[]; +begin + foreach x slice 1 in array $1 + loop + raise notice '%', x; + end loop; + end; +$$ language plpgsql; + +select foreach_test(ARRAY[1,2,3,4]); +select foreach_test(ARRAY[[1,2],[3,4]]); + +-- higher level of slicing +create or replace function foreach_test(anyarray) +returns void as $$ +declare x int[]; +begin + foreach x slice 2 in array $1 + loop + raise notice '%', x; + end loop; + end; +$$ language plpgsql; + +-- should fail +select foreach_test(ARRAY[1,2,3,4]); +-- ok +select foreach_test(ARRAY[[1,2],[3,4]]); +select foreach_test(ARRAY[[[1,2]],[[3,4]]]); + +create type xy_tuple AS (x int, y int); + +-- iteration over array of records +create or replace function foreach_test(anyarray) +returns void as $$ +declare r record; +begin + foreach r in array $1 + loop + raise notice '%', r; + end loop; + end; +$$ language plpgsql; + +select foreach_test(ARRAY[(10,20),(40,69),(35,78)]::xy_tuple[]); +select foreach_test(ARRAY[[(10,20),(40,69)],[(35,78),(88,76)]]::xy_tuple[]); + +create or replace function foreach_test(anyarray) +returns void as $$ +declare x int; y int; +begin + foreach x, y in array $1 + loop + raise notice 'x = %, y = %', x, y; + end loop; + end; +$$ language plpgsql; + +select foreach_test(ARRAY[(10,20),(40,69),(35,78)]::xy_tuple[]); +select foreach_test(ARRAY[[(10,20),(40,69)],[(35,78),(88,76)]]::xy_tuple[]); + +-- slicing over array of composite types +create or replace function foreach_test(anyarray) +returns void as $$ +declare x xy_tuple[]; +begin + foreach x slice 1 in array $1 + loop + raise notice '%', x; + end loop; + end; +$$ language plpgsql; + +select foreach_test(ARRAY[(10,20),(40,69),(35,78)]::xy_tuple[]); +select foreach_test(ARRAY[[(10,20),(40,69)],[(35,78),(88,76)]]::xy_tuple[]); + +drop function foreach_test(anyarray); +drop type xy_tuple; diff --git a/src/tools/msvc/Install.pm b/src/tools/msvc/Install.pm index cf28da1c41..bb291b636d 100644 --- a/src/tools/msvc/Install.pm +++ b/src/tools/msvc/Install.pm @@ -56,8 +56,10 @@ sub Install my $majorver = DetermineMajorVersion(); print "Installing version $majorver for $conf in $target\n"; - EnsureDirectories($target, 'bin','lib','share','share/timezonesets','share/contrib','doc', - 'doc/contrib', 'symbols', 'share/tsearch_data'); + EnsureDirectories($target, 'bin', 'lib', 'share', 'share/timezonesets', + 'share/extension', 'share/contrib', + 'doc', 'doc/extension', 'doc/contrib', + 'symbols', 'share/tsearch_data'); CopySolutionOutput($conf, $target); lcopy($target . '/lib/libpq.dll', $target . '/bin/libpq.dll'); @@ -316,7 +318,27 @@ sub CopyContribFiles my $mf = read_file("contrib/$d/Makefile"); $mf =~ s{\\s*[\r\n]+}{}mg; + + # Note: we currently don't support setting MODULEDIR in the makefile + my $moduledir = 'contrib'; + my $flist = ''; + if ($mf =~ /^EXTENSION\s*=\s*(.*)$/m) {$flist .= $1} + if ($flist ne '') + { + $moduledir = 'extension'; + $flist = ParseAndCleanRule($flist, $mf); + + foreach my $f (split /\s+/,$flist) + { + lcopy('contrib/' . $d . '/' . $f . '.control', + $target . '/share/extension/' . $f . '.control') + || croak("Could not copy file $f.control in contrib $d"); + print '.'; + } + } + + $flist = ''; if ($mf =~ /^DATA_built\s*=\s*(.*)$/m) {$flist .= $1} if ($mf =~ /^DATA\s*=\s*(.*)$/m) {$flist .= " $1"} $flist =~ s/^\s*//; # Remove leading spaces if we had only DATA_built @@ -325,12 +347,10 @@ sub CopyContribFiles { $flist = ParseAndCleanRule($flist, $mf); - # Special case for contrib/spi - $flist = "autoinc.sql insert_username.sql moddatetime.sql refint.sql timetravel.sql" - if ($d eq 'spi'); foreach my $f (split /\s+/,$flist) { - lcopy('contrib/' . $d . '/' . $f,$target . '/share/contrib/' . basename($f)) + lcopy('contrib/' . $d . '/' . $f, + $target . '/share/' . $moduledir . '/' . basename($f)) || croak("Could not copy file $f in contrib $d"); print '.'; } @@ -344,7 +364,8 @@ sub CopyContribFiles foreach my $f (split /\s+/,$flist) { - lcopy('contrib/' . $d . '/' . $f,$target . '/share/tsearch_data/' . basename($f)) + lcopy('contrib/' . $d . '/' . $f, + $target . '/share/tsearch_data/' . basename($f)) || croak("Could not copy file $f in contrib $d"); print '.'; } @@ -362,7 +383,8 @@ sub CopyContribFiles if ($d eq 'spi'); foreach my $f (split /\s+/,$flist) { - lcopy('contrib/' . $d . '/' . $f, $target . '/doc/contrib/' . $f) + lcopy('contrib/' . $d . '/' . $f, + $target . '/doc/' . $moduledir . '/' . $f) || croak("Could not copy file $f in contrib $d"); print '.'; } @@ -433,7 +455,7 @@ sub CopyIncludeFiles 'src/include/', 'pg_config.h', 'pg_config_os.h' ); CopyFiles('Grammar header', $target . '/include/server/parser/', - 'src/backend/parser/', 'gram.h'); + 'src/backend/parser/', 'gram.h'); CopySetOfFiles('',[ glob("src\\include\\*.h") ],$target . '/include/server/'); my $D; opendir($D, 'src/include') || croak "Could not opendir on src/include!\n"; diff --git a/src/tools/msvc/Mkvcbuild.pm b/src/tools/msvc/Mkvcbuild.pm index 94c6297f14..21c11dffd7 100644 --- a/src/tools/msvc/Mkvcbuild.pm +++ b/src/tools/msvc/Mkvcbuild.pm @@ -579,10 +579,6 @@ sub GenerateContribSqlFiles $l = substr($l, 0, index($l, '$(addsuffix ')) . substr($l, $i+1); } - # Special case for contrib/spi - $l = "autoinc.sql insert_username.sql moddatetime.sql refint.sql timetravel.sql" - if ($n eq 'spi'); - foreach my $d (split /\s+/, $l) { my $in = "$d.in"; @@ -594,7 +590,6 @@ sub GenerateContribSqlFiles my $cont = Project::read_file("contrib/$n/$in"); my $dn = $out; $dn =~ s/\.sql$//; - if ($mf =~ /^MODULE_big\s*=\s*(.*)$/m) { $dn = $1 } $cont =~ s/MODULE_PATHNAME/\$libdir\/$dn/g; my $o; open($o,">contrib/$n/$out") || croak "Could not write to contrib/$n/$d"; |