summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBernd Helmle2011-04-27 20:34:33 +0000
committerBernd Helmle2011-04-27 20:34:33 +0000
commit76ae97fbf28c7e44bc7afc41b6471951266741bd (patch)
treeafad05d58a2d57dd511dc29e8db4c567bcaa0e9f
parent64853e3a5dcbb30a7604e15415c749bf9a88ee1d (diff)
parentb2ef8929ae1c1b65f4b9582409463a9a2f009706 (diff)
Merge branch 'master' of ../bernd_pg into notnull_constraint
-rw-r--r--GNUmakefile.in3
-rwxr-xr-xconfigure3
-rw-r--r--configure.in2
-rw-r--r--contrib/btree_gin/.gitignore2
-rw-r--r--contrib/btree_gin/btree_gin.c3
-rw-r--r--contrib/btree_gist/.gitignore2
-rw-r--r--contrib/btree_gist/btree_bit.c57
-rw-r--r--contrib/btree_gist/btree_bytea.c53
-rw-r--r--contrib/btree_gist/btree_numeric.c52
-rw-r--r--contrib/btree_gist/btree_text.c43
-rw-r--r--contrib/btree_gist/btree_utils_num.c8
-rw-r--r--contrib/btree_gist/btree_utils_var.c155
-rw-r--r--contrib/btree_gist/btree_utils_var.h38
-rw-r--r--contrib/citext/.gitignore2
-rw-r--r--contrib/cube/.gitignore2
-rw-r--r--contrib/dblink/.gitignore2
-rw-r--r--contrib/dict_int/.gitignore2
-rw-r--r--contrib/dict_xsyn/.gitignore2
-rw-r--r--contrib/earthdistance/.gitignore2
-rw-r--r--contrib/earthdistance/Makefile1
-rw-r--r--contrib/file_fdw/.gitignore2
-rw-r--r--contrib/hstore/.gitignore2
-rw-r--r--contrib/intarray/.gitignore2
-rw-r--r--contrib/ltree/.gitignore2
-rw-r--r--contrib/pg_trgm/.gitignore2
-rw-r--r--contrib/pg_upgrade/check.c16
-rw-r--r--contrib/pg_upgrade/file.c7
-rw-r--r--contrib/pg_upgrade/pg_upgrade.c19
-rw-r--r--contrib/pg_upgrade/pg_upgrade.h9
-rw-r--r--contrib/pg_upgrade/server.c195
-rw-r--r--contrib/pgcrypto/.gitignore2
-rw-r--r--contrib/seg/.gitignore2
-rw-r--r--contrib/tablefunc/.gitignore2
-rw-r--r--contrib/test_parser/.gitignore2
-rw-r--r--contrib/tsearch2/.gitignore2
-rw-r--r--contrib/unaccent/.gitignore2
-rw-r--r--contrib/xml2/.gitignore2
-rw-r--r--doc/src/sgml/catalogs.sgml8
-rw-r--r--doc/src/sgml/datatype.sgml8
-rw-r--r--doc/src/sgml/indices.sgml27
-rw-r--r--doc/src/sgml/ref/create_collation.sgml7
-rw-r--r--doc/src/sgml/ref/create_domain.sgml2
-rw-r--r--doc/src/sgml/ref/create_index.sgml12
-rw-r--r--doc/src/sgml/ref/create_type.sgml4
-rw-r--r--doc/src/sgml/ref/grant.sgml4
-rw-r--r--doc/src/sgml/ref/select.sgml2
-rw-r--r--doc/src/sgml/regress.sgml6
-rw-r--r--doc/src/sgml/stylesheet.dsl36
-rw-r--r--doc/src/sgml/xfunc.sgml26
-rw-r--r--src/backend/access/common/tupdesc.c6
-rw-r--r--src/backend/access/gin/ginget.c6
-rw-r--r--src/backend/access/gin/ginscan.c17
-rw-r--r--src/backend/access/gin/ginutil.c21
-rw-r--r--src/backend/access/gist/gist.c17
-rw-r--r--src/backend/access/gist/gistsplit.c21
-rw-r--r--src/backend/access/gist/gistutil.c43
-rw-r--r--src/backend/access/hash/hashutil.c8
-rw-r--r--src/backend/access/heap/heapam.c2
-rw-r--r--src/backend/bootstrap/bootparse.y3
-rw-r--r--src/backend/catalog/aclchk.c52
-rw-r--r--src/backend/catalog/dependency.c4
-rw-r--r--src/backend/catalog/heap.c35
-rw-r--r--src/backend/catalog/index.c17
-rw-r--r--src/backend/catalog/namespace.c29
-rw-r--r--src/backend/catalog/pg_enum.c3
-rw-r--r--src/backend/catalog/pg_type.c9
-rw-r--r--src/backend/catalog/toasting.c8
-rw-r--r--src/backend/commands/cluster.c3
-rw-r--r--src/backend/commands/indexcmds.c6
-rw-r--r--src/backend/commands/tablecmds.c70
-rw-r--r--src/backend/commands/typecmds.c12
-rw-r--r--src/backend/commands/user.c2
-rw-r--r--src/backend/executor/execMain.c12
-rw-r--r--src/backend/executor/nodeMergejoin.c8
-rw-r--r--src/backend/libpq/hba.c19
-rw-r--r--src/backend/main/main.c2
-rw-r--r--src/backend/optimizer/path/costsize.c118
-rw-r--r--src/backend/optimizer/plan/createplan.c69
-rw-r--r--src/backend/optimizer/plan/planagg.c8
-rw-r--r--src/backend/optimizer/plan/planner.c39
-rw-r--r--src/backend/optimizer/plan/subselect.c11
-rw-r--r--src/backend/optimizer/prep/prepunion.c4
-rw-r--r--src/backend/optimizer/util/clauses.c87
-rw-r--r--src/backend/optimizer/util/pathnode.c2
-rw-r--r--src/backend/parser/analyze.c10
-rw-r--r--src/backend/parser/gram.y8
-rw-r--r--src/backend/parser/parse_cte.c18
-rw-r--r--src/backend/parser/parse_relation.c8
-rw-r--r--src/backend/parser/parse_target.c13
-rw-r--r--src/backend/parser/parse_utilcmd.c32
-rw-r--r--src/backend/port/win32/crashdump.c9
-rw-r--r--src/backend/port/win32/security.c6
-rw-r--r--src/backend/port/win32/socket.c14
-rw-r--r--src/backend/port/win32_latch.c4
-rw-r--r--src/backend/postmaster/postmaster.c18
-rw-r--r--src/backend/storage/lmgr/predicate.c21
-rw-r--r--src/backend/tcop/postgres.c7
-rw-r--r--src/backend/tcop/utility.c18
-rw-r--r--src/backend/tsearch/ts_locale.c17
-rw-r--r--src/backend/tsearch/wparser_def.c6
-rw-r--r--src/backend/utils/adt/acl.c4
-rw-r--r--src/backend/utils/adt/array_userfuncs.c2
-rw-r--r--src/backend/utils/adt/arrayfuncs.c14
-rw-r--r--src/backend/utils/adt/dbsize.c8
-rw-r--r--src/backend/utils/adt/formatting.c16
-rw-r--r--src/backend/utils/adt/geo_ops.c6
-rw-r--r--src/backend/utils/adt/pg_locale.c186
-rw-r--r--src/backend/utils/adt/ruleutils.c9
-rw-r--r--src/backend/utils/adt/selfuncs.c4
-rw-r--r--src/backend/utils/cache/syscache.c4
-rw-r--r--src/backend/utils/init/globals.c1
-rw-r--r--src/backend/utils/init/postinit.c10
-rw-r--r--src/backend/utils/mb/mbutils.c121
-rw-r--r--src/bin/initdb/initdb.c9
-rw-r--r--src/bin/pg_basebackup/pg_basebackup.c20
-rw-r--r--src/bin/pg_ctl/pg_ctl.c4
-rw-r--r--src/bin/pg_dump/pg_dump.c21
-rw-r--r--src/include/access/gin_private.h4
-rw-r--r--src/include/access/gist_private.h3
-rw-r--r--src/include/catalog/catversion.h2
-rw-r--r--src/include/catalog/heap.h3
-rw-r--r--src/include/catalog/namespace.h1
-rw-r--r--src/include/mb/pg_wchar.h7
-rw-r--r--src/include/miscadmin.h1
-rw-r--r--src/include/nodes/parsenodes.h1
-rw-r--r--src/include/nodes/relation.h14
-rw-r--r--src/include/optimizer/clauses.h10
-rw-r--r--src/include/optimizer/cost.h4
-rw-r--r--src/include/optimizer/planmain.h8
-rw-r--r--src/include/pg_config.h.in3
-rw-r--r--src/include/pg_config.h.win323
-rw-r--r--src/include/port/win32.h4
-rw-r--r--src/include/utils/acl.h2
-rw-r--r--src/include/utils/pg_locale.h8
-rw-r--r--src/interfaces/ecpg/ecpglib/sqlda.c3
-rw-r--r--src/makefiles/pgxs.mk13
-rw-r--r--src/pl/plperl/plperl.c4
-rw-r--r--src/pl/plperl/plperl.h36
-rw-r--r--src/port/getopt.c7
-rw-r--r--src/port/noblock.c4
-rw-r--r--src/test/regress/expected/collate.linux.utf8.out3
-rw-r--r--src/test/regress/expected/delete.out23
-rw-r--r--src/test/regress/expected/foreign_data.out9
-rw-r--r--src/test/regress/expected/insert.out17
-rw-r--r--src/test/regress/expected/oidjoins.out208
-rw-r--r--src/test/regress/expected/update.out9
-rw-r--r--src/test/regress/pg_regress.c29
-rw-r--r--src/test/regress/sql/collate.linux.utf8.sql3
-rw-r--r--src/test/regress/sql/delete.sql12
-rw-r--r--src/test/regress/sql/foreign_data.sql4
-rw-r--r--src/test/regress/sql/insert.sql7
-rw-r--r--src/test/regress/sql/oidjoins.sql104
-rw-r--r--src/test/regress/sql/update.sql4
-rw-r--r--src/timezone/pgtz.c4
-rw-r--r--src/tools/editors/emacs.samples5
-rw-r--r--src/tools/findoidjoins/README68
-rw-r--r--src/tools/findoidjoins/findoidjoins.c95
-rwxr-xr-xsrc/tools/findoidjoins/make_oidjoins_check28
-rwxr-xr-xsrc/tools/msvc/pgflex.bat12
159 files changed, 1977 insertions, 1079 deletions
diff --git a/GNUmakefile.in b/GNUmakefile.in
index f3c5fe587e..79b0da42a8 100644
--- a/GNUmakefile.in
+++ b/GNUmakefile.in
@@ -60,8 +60,7 @@ 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,check-world,src/test src/pl src/interfaces/ecpg contrib,check)
$(call recurse,installcheck-world,src/test src/pl src/interfaces/ecpg contrib,installcheck)
diff --git a/configure b/configure
index 69c7418f38..b0a10fbbc1 100755
--- a/configure
+++ b/configure
@@ -18985,7 +18985,8 @@ fi
-for ac_func in cbrt dlopen fcvt fdatasync getifaddrs getpeereid getpeerucred getrlimit memmove poll pstat readlink scandir setproctitle setsid sigprocmask symlink sysconf towlower utime utimes waitpid wcstombs
+
+for ac_func in cbrt dlopen fcvt fdatasync getifaddrs getpeereid getpeerucred getrlimit memmove poll pstat readlink scandir setproctitle setsid sigprocmask symlink sysconf towlower utime utimes waitpid wcstombs wcstombs_l
do
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
{ $as_echo "$as_me:$LINENO: checking for $ac_func" >&5
diff --git a/configure.in b/configure.in
index 3c4089f38e..03ff57d4bc 100644
--- a/configure.in
+++ b/configure.in
@@ -1187,7 +1187,7 @@ PGAC_VAR_INT_TIMEZONE
AC_FUNC_ACCEPT_ARGTYPES
PGAC_FUNC_GETTIMEOFDAY_1ARG
-AC_CHECK_FUNCS([cbrt dlopen fcvt fdatasync getifaddrs getpeereid getpeerucred getrlimit memmove poll pstat readlink scandir setproctitle setsid sigprocmask symlink sysconf towlower utime utimes waitpid wcstombs])
+AC_CHECK_FUNCS([cbrt dlopen fcvt fdatasync getifaddrs getpeereid getpeerucred getrlimit memmove poll pstat readlink scandir setproctitle setsid sigprocmask symlink sysconf towlower utime utimes waitpid wcstombs wcstombs_l])
AC_REPLACE_FUNCS(fseeko)
case $host_os in
diff --git a/contrib/btree_gin/.gitignore b/contrib/btree_gin/.gitignore
index 19b6c5ba42..5dcb3ff972 100644
--- a/contrib/btree_gin/.gitignore
+++ b/contrib/btree_gin/.gitignore
@@ -1,2 +1,4 @@
# Generated subdirectories
+/log/
/results/
+/tmp_check/
diff --git a/contrib/btree_gin/btree_gin.c b/contrib/btree_gin/btree_gin.c
index 7ca153e092..5c4f58b8b1 100644
--- a/contrib/btree_gin/btree_gin.c
+++ b/contrib/btree_gin/btree_gin.c
@@ -7,7 +7,6 @@
#include "fmgr.h"
#include "access/skey.h"
-#include "catalog/pg_collation.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
#include "utils/cash.h"
@@ -123,7 +122,7 @@ gin_compare_prefix_##type(PG_FUNCTION_ARGS) \
\
cmp = DatumGetInt32(DirectFunctionCall2Coll( \
TypeInfo_##type.typecmp, \
- DEFAULT_COLLATION_OID, \
+ PG_GET_COLLATION(), \
(data->strategy == BTLessStrategyNumber || \
data->strategy == BTLessEqualStrategyNumber) \
? data->datum : a, \
diff --git a/contrib/btree_gist/.gitignore b/contrib/btree_gist/.gitignore
index 19b6c5ba42..5dcb3ff972 100644
--- a/contrib/btree_gist/.gitignore
+++ b/contrib/btree_gist/.gitignore
@@ -1,2 +1,4 @@
# Generated subdirectories
+/log/
/results/
+/tmp_check/
diff --git a/contrib/btree_gist/btree_bit.c b/contrib/btree_gist/btree_bit.c
index 9884d0fb96..8675d2488d 100644
--- a/contrib/btree_gist/btree_bit.c
+++ b/contrib/btree_gist/btree_bit.c
@@ -29,40 +29,51 @@ Datum gbt_bit_same(PG_FUNCTION_ARGS);
/* define for comparison */
static bool
-gbt_bitgt(const void *a, const void *b)
+gbt_bitgt(const void *a, const void *b, Oid collation)
{
- return (DatumGetBool(DirectFunctionCall2(bitgt, PointerGetDatum(a), PointerGetDatum(b))));
+ return DatumGetBool(DirectFunctionCall2(bitgt,
+ PointerGetDatum(a),
+ PointerGetDatum(b)));
}
static bool
-gbt_bitge(const void *a, const void *b)
+gbt_bitge(const void *a, const void *b, Oid collation)
{
- return (DatumGetBool(DirectFunctionCall2(bitge, PointerGetDatum(a), PointerGetDatum(b))));
+ return DatumGetBool(DirectFunctionCall2(bitge,
+ PointerGetDatum(a),
+ PointerGetDatum(b)));
}
static bool
-gbt_biteq(const void *a, const void *b)
+gbt_biteq(const void *a, const void *b, Oid collation)
{
- return (DatumGetBool(DirectFunctionCall2(biteq, PointerGetDatum(a), PointerGetDatum(b))));
+ return DatumGetBool(DirectFunctionCall2(biteq,
+ PointerGetDatum(a),
+ PointerGetDatum(b)));
}
static bool
-gbt_bitle(const void *a, const void *b)
+gbt_bitle(const void *a, const void *b, Oid collation)
{
- return (DatumGetBool(DirectFunctionCall2(bitle, PointerGetDatum(a), PointerGetDatum(b))));
+ return DatumGetBool(DirectFunctionCall2(bitle,
+ PointerGetDatum(a),
+ PointerGetDatum(b)));
}
static bool
-gbt_bitlt(const void *a, const void *b)
+gbt_bitlt(const void *a, const void *b, Oid collation)
{
- return (DatumGetBool(DirectFunctionCall2(bitlt, PointerGetDatum(a), PointerGetDatum(b))));
+ return DatumGetBool(DirectFunctionCall2(bitlt,
+ PointerGetDatum(a),
+ PointerGetDatum(b)));
}
static int32
-gbt_bitcmp(const bytea *a, const bytea *b)
+gbt_bitcmp(const void *a, const void *b, Oid collation)
{
- return
- (DatumGetInt32(DirectFunctionCall2(byteacmp, PointerGetDatum(a), PointerGetDatum(b))));
+ return DatumGetInt32(DirectFunctionCall2(byteacmp,
+ PointerGetDatum(a),
+ PointerGetDatum(b)));
}
@@ -134,7 +145,7 @@ gbt_bit_consistent(PG_FUNCTION_ARGS)
/* Oid subtype = PG_GETARG_OID(3); */
bool *recheck = (bool *) PG_GETARG_POINTER(4);
- bool retval = FALSE;
+ bool retval;
GBT_VARKEY *key = (GBT_VARKEY *) DatumGetPointer(entry->key);
GBT_VARKEY_R r = gbt_var_key_readable(key);
@@ -142,12 +153,14 @@ gbt_bit_consistent(PG_FUNCTION_ARGS)
*recheck = false;
if (GIST_LEAF(entry))
- retval = gbt_var_consistent(&r, query, &strategy, TRUE, &tinfo);
+ retval = gbt_var_consistent(&r, query, strategy, PG_GET_COLLATION(),
+ TRUE, &tinfo);
else
{
bytea *q = gbt_bit_xfrm((bytea *) query);
- retval = gbt_var_consistent(&r, (void *) q, &strategy, FALSE, &tinfo);
+ retval = gbt_var_consistent(&r, q, strategy, PG_GET_COLLATION(),
+ FALSE, &tinfo);
}
PG_RETURN_BOOL(retval);
}
@@ -160,7 +173,8 @@ gbt_bit_union(PG_FUNCTION_ARGS)
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
int32 *size = (int *) PG_GETARG_POINTER(1);
- PG_RETURN_POINTER(gbt_var_union(entryvec, size, &tinfo));
+ PG_RETURN_POINTER(gbt_var_union(entryvec, size, PG_GET_COLLATION(),
+ &tinfo));
}
@@ -170,7 +184,8 @@ gbt_bit_picksplit(PG_FUNCTION_ARGS)
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
- gbt_var_picksplit(entryvec, v, &tinfo);
+ gbt_var_picksplit(entryvec, v, PG_GET_COLLATION(),
+ &tinfo);
PG_RETURN_POINTER(v);
}
@@ -181,7 +196,8 @@ gbt_bit_same(PG_FUNCTION_ARGS)
Datum d2 = PG_GETARG_DATUM(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
- PG_RETURN_POINTER(gbt_var_same(result, d1, d2, &tinfo));
+ *result = gbt_var_same(d1, d2, PG_GET_COLLATION(), &tinfo);
+ PG_RETURN_POINTER(result);
}
@@ -192,5 +208,6 @@ gbt_bit_penalty(PG_FUNCTION_ARGS)
GISTENTRY *n = (GISTENTRY *) PG_GETARG_POINTER(1);
float *result = (float *) PG_GETARG_POINTER(2);
- PG_RETURN_POINTER(gbt_var_penalty(result, o, n, &tinfo));
+ PG_RETURN_POINTER(gbt_var_penalty(result, o, n, PG_GET_COLLATION(),
+ &tinfo));
}
diff --git a/contrib/btree_gist/btree_bytea.c b/contrib/btree_gist/btree_bytea.c
index 8430464612..e45509d15c 100644
--- a/contrib/btree_gist/btree_bytea.c
+++ b/contrib/btree_gist/btree_bytea.c
@@ -27,41 +27,51 @@ Datum gbt_bytea_same(PG_FUNCTION_ARGS);
/* define for comparison */
static bool
-gbt_byteagt(const void *a, const void *b)
+gbt_byteagt(const void *a, const void *b, Oid collation)
{
- return (DatumGetBool(DirectFunctionCall2(byteagt, PointerGetDatum(a), PointerGetDatum(b))));
+ return DatumGetBool(DirectFunctionCall2(byteagt,
+ PointerGetDatum(a),
+ PointerGetDatum(b)));
}
static bool
-gbt_byteage(const void *a, const void *b)
+gbt_byteage(const void *a, const void *b, Oid collation)
{
- return (DatumGetBool(DirectFunctionCall2(byteage, PointerGetDatum(a), PointerGetDatum(b))));
+ return DatumGetBool(DirectFunctionCall2(byteage,
+ PointerGetDatum(a),
+ PointerGetDatum(b)));
}
static bool
-gbt_byteaeq(const void *a, const void *b)
+gbt_byteaeq(const void *a, const void *b, Oid collation)
{
- return (DatumGetBool(DirectFunctionCall2(byteaeq, PointerGetDatum(a), PointerGetDatum(b))));
+ return DatumGetBool(DirectFunctionCall2(byteaeq,
+ PointerGetDatum(a),
+ PointerGetDatum(b)));
}
static bool
-gbt_byteale(const void *a, const void *b)
+gbt_byteale(const void *a, const void *b, Oid collation)
{
- return (DatumGetBool(DirectFunctionCall2(byteale, PointerGetDatum(a), PointerGetDatum(b))));
+ return DatumGetBool(DirectFunctionCall2(byteale,
+ PointerGetDatum(a),
+ PointerGetDatum(b)));
}
static bool
-gbt_bytealt(const void *a, const void *b)
+gbt_bytealt(const void *a, const void *b, Oid collation)
{
- return (DatumGetBool(DirectFunctionCall2(bytealt, PointerGetDatum(a), PointerGetDatum(b))));
+ return DatumGetBool(DirectFunctionCall2(bytealt,
+ PointerGetDatum(a),
+ PointerGetDatum(b)));
}
-
static int32
-gbt_byteacmp(const bytea *a, const bytea *b)
+gbt_byteacmp(const void *a, const void *b, Oid collation)
{
- return
- (DatumGetInt32(DirectFunctionCall2(byteacmp, PointerGetDatum(a), PointerGetDatum(b))));
+ return DatumGetInt32(DirectFunctionCall2(byteacmp,
+ PointerGetDatum(a),
+ PointerGetDatum(b)));
}
@@ -111,7 +121,8 @@ gbt_bytea_consistent(PG_FUNCTION_ARGS)
/* All cases served by this function are exact */
*recheck = false;
- retval = gbt_var_consistent(&r, query, &strategy, GIST_LEAF(entry), &tinfo);
+ retval = gbt_var_consistent(&r, query, strategy, PG_GET_COLLATION(),
+ GIST_LEAF(entry), &tinfo);
PG_RETURN_BOOL(retval);
}
@@ -123,7 +134,8 @@ gbt_bytea_union(PG_FUNCTION_ARGS)
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
int32 *size = (int *) PG_GETARG_POINTER(1);
- PG_RETURN_POINTER(gbt_var_union(entryvec, size, &tinfo));
+ PG_RETURN_POINTER(gbt_var_union(entryvec, size, PG_GET_COLLATION(),
+ &tinfo));
}
@@ -133,7 +145,8 @@ gbt_bytea_picksplit(PG_FUNCTION_ARGS)
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
- gbt_var_picksplit(entryvec, v, &tinfo);
+ gbt_var_picksplit(entryvec, v, PG_GET_COLLATION(),
+ &tinfo);
PG_RETURN_POINTER(v);
}
@@ -144,7 +157,8 @@ gbt_bytea_same(PG_FUNCTION_ARGS)
Datum d2 = PG_GETARG_DATUM(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
- PG_RETURN_POINTER(gbt_var_same(result, d1, d2, &tinfo));
+ *result = gbt_var_same(d1, d2, PG_GET_COLLATION(), &tinfo);
+ PG_RETURN_POINTER(result);
}
@@ -155,5 +169,6 @@ gbt_bytea_penalty(PG_FUNCTION_ARGS)
GISTENTRY *n = (GISTENTRY *) PG_GETARG_POINTER(1);
float *result = (float *) PG_GETARG_POINTER(2);
- PG_RETURN_POINTER(gbt_var_penalty(result, o, n, &tinfo));
+ PG_RETURN_POINTER(gbt_var_penalty(result, o, n, PG_GET_COLLATION(),
+ &tinfo));
}
diff --git a/contrib/btree_gist/btree_numeric.c b/contrib/btree_gist/btree_numeric.c
index fa82497e2b..e962c6dd32 100644
--- a/contrib/btree_gist/btree_numeric.c
+++ b/contrib/btree_gist/btree_numeric.c
@@ -32,41 +32,51 @@ Datum gbt_numeric_same(PG_FUNCTION_ARGS);
/* define for comparison */
static bool
-gbt_numeric_gt(const void *a, const void *b)
+gbt_numeric_gt(const void *a, const void *b, Oid collation)
{
- return (DatumGetBool(DirectFunctionCall2(numeric_gt, PointerGetDatum(a), PointerGetDatum(b))));
+ return DatumGetBool(DirectFunctionCall2(numeric_gt,
+ PointerGetDatum(a),
+ PointerGetDatum(b)));
}
static bool
-gbt_numeric_ge(const void *a, const void *b)
+gbt_numeric_ge(const void *a, const void *b, Oid collation)
{
- return (DatumGetBool(DirectFunctionCall2(numeric_ge, PointerGetDatum(a), PointerGetDatum(b))));
+ return DatumGetBool(DirectFunctionCall2(numeric_ge,
+ PointerGetDatum(a),
+ PointerGetDatum(b)));
}
static bool
-gbt_numeric_eq(const void *a, const void *b)
+gbt_numeric_eq(const void *a, const void *b, Oid collation)
{
- return (DatumGetBool(DirectFunctionCall2(numeric_eq, PointerGetDatum(a), PointerGetDatum(b))));
+ return DatumGetBool(DirectFunctionCall2(numeric_eq,
+ PointerGetDatum(a),
+ PointerGetDatum(b)));
}
static bool
-gbt_numeric_le(const void *a, const void *b)
+gbt_numeric_le(const void *a, const void *b, Oid collation)
{
- return (DatumGetBool(DirectFunctionCall2(numeric_le, PointerGetDatum(a), PointerGetDatum(b))));
+ return DatumGetBool(DirectFunctionCall2(numeric_le,
+ PointerGetDatum(a),
+ PointerGetDatum(b)));
}
static bool
-gbt_numeric_lt(const void *a, const void *b)
+gbt_numeric_lt(const void *a, const void *b, Oid collation)
{
- return (DatumGetBool(DirectFunctionCall2(numeric_lt, PointerGetDatum(a), PointerGetDatum(b))));
+ return DatumGetBool(DirectFunctionCall2(numeric_lt,
+ PointerGetDatum(a),
+ PointerGetDatum(b)));
}
-
static int32
-gbt_numeric_cmp(const bytea *a, const bytea *b)
+gbt_numeric_cmp(const void *a, const void *b, Oid collation)
{
- return
- (DatumGetInt32(DirectFunctionCall2(numeric_cmp, PointerGetDatum(a), PointerGetDatum(b))));
+ return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
+ PointerGetDatum(a),
+ PointerGetDatum(b)));
}
@@ -116,7 +126,8 @@ gbt_numeric_consistent(PG_FUNCTION_ARGS)
/* All cases served by this function are exact */
*recheck = false;
- retval = gbt_var_consistent(&r, query, &strategy, GIST_LEAF(entry), &tinfo);
+ retval = gbt_var_consistent(&r, query, strategy, PG_GET_COLLATION(),
+ GIST_LEAF(entry), &tinfo);
PG_RETURN_BOOL(retval);
}
@@ -128,7 +139,8 @@ gbt_numeric_union(PG_FUNCTION_ARGS)
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
int32 *size = (int *) PG_GETARG_POINTER(1);
- PG_RETURN_POINTER(gbt_var_union(entryvec, size, &tinfo));
+ PG_RETURN_POINTER(gbt_var_union(entryvec, size, PG_GET_COLLATION(),
+ &tinfo));
}
@@ -139,7 +151,8 @@ gbt_numeric_same(PG_FUNCTION_ARGS)
Datum d2 = PG_GETARG_DATUM(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
- PG_RETURN_POINTER(gbt_var_same(result, d1, d2, &tinfo));
+ *result = gbt_var_same(d1, d2, PG_GET_COLLATION(), &tinfo);
+ PG_RETURN_POINTER(result);
}
@@ -163,7 +176,7 @@ gbt_numeric_penalty(PG_FUNCTION_ARGS)
rk = gbt_var_key_readable(org);
uni = PointerGetDatum(gbt_var_key_copy(&rk, TRUE));
- gbt_var_bin_union(&uni, newe, &tinfo);
+ gbt_var_bin_union(&uni, newe, PG_GET_COLLATION(), &tinfo);
ok = gbt_var_key_readable(org);
uk = gbt_var_key_readable((GBT_VARKEY *) DatumGetPointer(uni));
@@ -224,6 +237,7 @@ gbt_numeric_picksplit(PG_FUNCTION_ARGS)
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
- gbt_var_picksplit(entryvec, v, &tinfo);
+ gbt_var_picksplit(entryvec, v, PG_GET_COLLATION(),
+ &tinfo);
PG_RETURN_POINTER(v);
}
diff --git a/contrib/btree_gist/btree_text.c b/contrib/btree_gist/btree_text.c
index c6b57f82de..3d4f8c13c8 100644
--- a/contrib/btree_gist/btree_text.c
+++ b/contrib/btree_gist/btree_text.c
@@ -3,7 +3,6 @@
*/
#include "btree_gist.h"
#include "btree_utils_var.h"
-#include "catalog/pg_collation.h"
#include "utils/builtins.h"
/*
@@ -31,55 +30,55 @@ Datum gbt_text_same(PG_FUNCTION_ARGS);
/* define for comparison */
static bool
-gbt_textgt(const void *a, const void *b)
+gbt_textgt(const void *a, const void *b, Oid collation)
{
return DatumGetBool(DirectFunctionCall2Coll(text_gt,
- DEFAULT_COLLATION_OID,
+ collation,
PointerGetDatum(a),
PointerGetDatum(b)));
}
static bool
-gbt_textge(const void *a, const void *b)
+gbt_textge(const void *a, const void *b, Oid collation)
{
return DatumGetBool(DirectFunctionCall2Coll(text_ge,
- DEFAULT_COLLATION_OID,
+ collation,
PointerGetDatum(a),
PointerGetDatum(b)));
}
static bool
-gbt_texteq(const void *a, const void *b)
+gbt_texteq(const void *a, const void *b, Oid collation)
{
return DatumGetBool(DirectFunctionCall2Coll(texteq,
- DEFAULT_COLLATION_OID,
+ collation,
PointerGetDatum(a),
PointerGetDatum(b)));
}
static bool
-gbt_textle(const void *a, const void *b)
+gbt_textle(const void *a, const void *b, Oid collation)
{
return DatumGetBool(DirectFunctionCall2Coll(text_le,
- DEFAULT_COLLATION_OID,
+ collation,
PointerGetDatum(a),
PointerGetDatum(b)));
}
static bool
-gbt_textlt(const void *a, const void *b)
+gbt_textlt(const void *a, const void *b, Oid collation)
{
return DatumGetBool(DirectFunctionCall2Coll(text_lt,
- DEFAULT_COLLATION_OID,
+ collation,
PointerGetDatum(a),
PointerGetDatum(b)));
}
static int32
-gbt_textcmp(const bytea *a, const bytea *b)
+gbt_textcmp(const void *a, const void *b, Oid collation)
{
return DatumGetInt32(DirectFunctionCall2Coll(bttextcmp,
- DEFAULT_COLLATION_OID,
+ collation,
PointerGetDatum(a),
PointerGetDatum(b)));
}
@@ -169,7 +168,8 @@ gbt_text_consistent(PG_FUNCTION_ARGS)
tinfo.eml = pg_database_encoding_max_length();
}
- retval = gbt_var_consistent(&r, query, &strategy, GIST_LEAF(entry), &tinfo);
+ retval = gbt_var_consistent(&r, query, strategy, PG_GET_COLLATION(),
+ GIST_LEAF(entry), &tinfo);
PG_RETURN_BOOL(retval);
}
@@ -197,7 +197,8 @@ gbt_bpchar_consistent(PG_FUNCTION_ARGS)
tinfo.eml = pg_database_encoding_max_length();
}
- retval = gbt_var_consistent(&r, trim, &strategy, GIST_LEAF(entry), &tinfo);
+ retval = gbt_var_consistent(&r, trim, strategy, PG_GET_COLLATION(),
+ GIST_LEAF(entry), &tinfo);
PG_RETURN_BOOL(retval);
}
@@ -208,7 +209,8 @@ gbt_text_union(PG_FUNCTION_ARGS)
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
int32 *size = (int *) PG_GETARG_POINTER(1);
- PG_RETURN_POINTER(gbt_var_union(entryvec, size, &tinfo));
+ PG_RETURN_POINTER(gbt_var_union(entryvec, size, PG_GET_COLLATION(),
+ &tinfo));
}
@@ -218,7 +220,8 @@ gbt_text_picksplit(PG_FUNCTION_ARGS)
GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
- gbt_var_picksplit(entryvec, v, &tinfo);
+ gbt_var_picksplit(entryvec, v, PG_GET_COLLATION(),
+ &tinfo);
PG_RETURN_POINTER(v);
}
@@ -229,7 +232,8 @@ gbt_text_same(PG_FUNCTION_ARGS)
Datum d2 = PG_GETARG_DATUM(1);
bool *result = (bool *) PG_GETARG_POINTER(2);
- PG_RETURN_POINTER(gbt_var_same(result, d1, d2, &tinfo));
+ *result = gbt_var_same(d1, d2, PG_GET_COLLATION(), &tinfo);
+ PG_RETURN_POINTER(result);
}
@@ -240,5 +244,6 @@ gbt_text_penalty(PG_FUNCTION_ARGS)
GISTENTRY *n = (GISTENTRY *) PG_GETARG_POINTER(1);
float *result = (float *) PG_GETARG_POINTER(2);
- PG_RETURN_POINTER(gbt_var_penalty(result, o, n, &tinfo));
+ PG_RETURN_POINTER(gbt_var_penalty(result, o, n, PG_GET_COLLATION(),
+ &tinfo));
}
diff --git a/contrib/btree_gist/btree_utils_num.c b/contrib/btree_gist/btree_utils_num.c
index 64c95854df..a3da5802df 100644
--- a/contrib/btree_gist/btree_utils_num.c
+++ b/contrib/btree_gist/btree_utils_num.c
@@ -184,9 +184,11 @@ gbt_num_bin_union(Datum *u, GBT_NUMKEY *e, const gbtree_ninfo *tinfo)
/*
-** The GiST consistent method
-*/
-
+ * The GiST consistent method
+ *
+ * Note: we currently assume that no datatypes that use this routine are
+ * collation-aware; so we don't bother passing collation through.
+ */
bool
gbt_num_consistent(const GBT_NUMKEY_R *key,
const void *query,
diff --git a/contrib/btree_gist/btree_utils_var.c b/contrib/btree_gist/btree_utils_var.c
index d74013af88..e73799bb21 100644
--- a/contrib/btree_gist/btree_utils_var.c
+++ b/contrib/btree_gist/btree_utils_var.c
@@ -8,11 +8,24 @@
#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"
+/* used for key sorting */
+typedef struct
+{
+ int i;
+ GBT_VARKEY *t;
+} Vsrt;
+
+typedef struct
+{
+ const gbtree_vinfo *tinfo;
+ Oid collation;
+} gbt_vsrt_arg;
+
+
PG_FUNCTION_INFO_V1(gbt_var_decompress);
Datum gbt_var_decompress(PG_FUNCTION_ARGS);
@@ -140,13 +153,11 @@ gbt_var_node_cp_len(const GBT_VARKEY *node, const gbtree_vinfo *tinfo)
/*
* returns true, if query matches prefix ( common prefix )
-*/
+ */
static bool
gbt_bytea_pf_match(const bytea *pf, const bytea *query, const gbtree_vinfo *tinfo)
{
-
bool out = FALSE;
- int32 k = 0;
int32 qlen = VARSIZE(query) - VARHDRSZ;
int32 nlen = VARSIZE(pf) - VARHDRSZ;
@@ -155,27 +166,7 @@ gbt_bytea_pf_match(const bytea *pf, const bytea *query, const gbtree_vinfo *tinf
char *q = VARDATA(query);
char *n = VARDATA(pf);
- if (tinfo->eml > 1)
- {
- out = (varstr_cmp(q, nlen, n, nlen, DEFAULT_COLLATION_OID) == 0);
- }
- else
- {
- out = TRUE;
- for (k = 0; k < nlen; k++)
- {
- if (*n != *q)
- {
- out = FALSE;
- break;
- }
- if (k < (nlen - 1))
- {
- q++;
- n++;
- }
- }
- }
+ out = (memcmp(q, n, nlen) == 0);
}
return out;
@@ -184,17 +175,14 @@ gbt_bytea_pf_match(const bytea *pf, const bytea *query, const gbtree_vinfo *tinf
/*
* returns true, if query matches node using common prefix
-*/
-
+ */
static bool
gbt_var_node_pf_match(const GBT_VARKEY_R *node, const bytea *query, const gbtree_vinfo *tinfo)
{
-
return (tinfo->trnc && (
gbt_bytea_pf_match(node->lower, query, tinfo) ||
gbt_bytea_pf_match(node->upper, query, tinfo)
));
-
}
@@ -232,9 +220,9 @@ gbt_var_node_truncate(const GBT_VARKEY *node, int32 cpf_length, const gbtree_vin
void
-gbt_var_bin_union(Datum *u, GBT_VARKEY *e, const gbtree_vinfo *tinfo)
+gbt_var_bin_union(Datum *u, GBT_VARKEY *e, Oid collation,
+ const gbtree_vinfo *tinfo)
{
-
GBT_VARKEY *nk = NULL;
GBT_VARKEY *tmp = NULL;
GBT_VARKEY_R nr;
@@ -252,14 +240,14 @@ gbt_var_bin_union(Datum *u, GBT_VARKEY *e, const gbtree_vinfo *tinfo)
GBT_VARKEY_R ro = gbt_var_key_readable((GBT_VARKEY *) DatumGetPointer(*u));
- if ((*tinfo->f_cmp) ((bytea *) ro.lower, (bytea *) eo.lower) > 0)
+ if ((*tinfo->f_cmp) (ro.lower, eo.lower, collation) > 0)
{
nr.lower = eo.lower;
nr.upper = ro.upper;
nk = gbt_var_key_copy(&nr, TRUE);
}
- if ((*tinfo->f_cmp) ((bytea *) ro.upper, (bytea *) eo.upper) < 0)
+ if ((*tinfo->f_cmp) (ro.upper, eo.upper, collation) < 0)
{
nr.upper = eo.upper;
nr.lower = ro.lower;
@@ -308,7 +296,8 @@ gbt_var_compress(GISTENTRY *entry, const gbtree_vinfo *tinfo)
GBT_VARKEY *
-gbt_var_union(const GistEntryVector *entryvec, int32 *size, const gbtree_vinfo *tinfo)
+gbt_var_union(const GistEntryVector *entryvec, int32 *size, Oid collation,
+ const gbtree_vinfo *tinfo)
{
int i = 0,
@@ -326,7 +315,7 @@ gbt_var_union(const GistEntryVector *entryvec, int32 *size, const gbtree_vinfo *
for (i = 1; i < numranges; i++)
{
cur = (GBT_VARKEY *) DatumGetPointer(entryvec->vector[i].key);
- gbt_var_bin_union(&out, cur, tinfo);
+ gbt_var_bin_union(&out, cur, collation, tinfo);
}
@@ -347,9 +336,10 @@ gbt_var_union(const GistEntryVector *entryvec, int32 *size, const gbtree_vinfo *
bool
-gbt_var_same(bool *result, const Datum d1, const Datum d2, const gbtree_vinfo *tinfo)
+gbt_var_same(Datum d1, Datum d2, Oid collation,
+ const gbtree_vinfo *tinfo)
{
-
+ bool result;
GBT_VARKEY *t1 = (GBT_VARKEY *) DatumGetPointer(d1);
GBT_VARKEY *t2 = (GBT_VARKEY *) DatumGetPointer(d2);
GBT_VARKEY_R r1,
@@ -359,22 +349,19 @@ gbt_var_same(bool *result, const Datum d1, const Datum d2, const gbtree_vinfo *t
r2 = gbt_var_key_readable(t2);
if (t1 && t2)
- {
- *result = (((*tinfo->f_cmp) ((bytea *) r1.lower, (bytea *) r2.lower) == 0
- && (*tinfo->f_cmp) ((bytea *) r1.upper, (bytea *) r2.upper) == 0) ? TRUE : FALSE);
- }
+ result = ((*tinfo->f_cmp) (r1.lower, r2.lower, collation) == 0 &&
+ (*tinfo->f_cmp) (r1.upper, r2.upper, collation) == 0);
else
- *result = (t1 == NULL && t2 == NULL) ? TRUE : FALSE;
+ result = (t1 == NULL && t2 == NULL);
- PG_RETURN_POINTER(result);
+ return result;
}
-
float *
-gbt_var_penalty(float *res, const GISTENTRY *o, const GISTENTRY *n, const gbtree_vinfo *tinfo)
+gbt_var_penalty(float *res, const GISTENTRY *o, const GISTENTRY *n,
+ Oid collation, const gbtree_vinfo *tinfo)
{
-
GBT_VARKEY *orge = (GBT_VARKEY *) DatumGetPointer(o->key);
GBT_VARKEY *newe = (GBT_VARKEY *) DatumGetPointer(n->key);
GBT_VARKEY_R ok,
@@ -394,21 +381,19 @@ gbt_var_penalty(float *res, const GISTENTRY *o, const GISTENTRY *n, const gbtree
if ((VARSIZE(ok.lower) - VARHDRSZ) == 0 && (VARSIZE(ok.upper) - VARHDRSZ) == 0)
*res = 0.0;
- else if (!(
- (
- ((*tinfo->f_cmp) (nk.lower, ok.lower) >= 0 || gbt_bytea_pf_match(ok.lower, nk.lower, tinfo)) &&
- ((*tinfo->f_cmp) (nk.upper, ok.upper) <= 0 || gbt_bytea_pf_match(ok.upper, nk.upper, tinfo))
- )
- ))
+ else if (!(((*tinfo->f_cmp) (nk.lower, ok.lower, collation) >= 0 ||
+ gbt_bytea_pf_match(ok.lower, nk.lower, tinfo)) &&
+ ((*tinfo->f_cmp) (nk.upper, ok.upper, collation) <= 0 ||
+ gbt_bytea_pf_match(ok.upper, nk.upper, tinfo))))
{
Datum d = PointerGetDatum(0);
double dres = 0.0;
int32 ol,
ul;
- gbt_var_bin_union(&d, orge, tinfo);
+ gbt_var_bin_union(&d, orge, collation, tinfo);
ol = gbt_var_node_cp_len((GBT_VARKEY *) DatumGetPointer(d), tinfo);
- gbt_var_bin_union(&d, newe, tinfo);
+ gbt_var_bin_union(&d, newe, collation, tinfo);
ul = gbt_var_node_cp_len((GBT_VARKEY *) DatumGetPointer(d), tinfo);
if (ul < ol)
@@ -444,18 +429,19 @@ gbt_vsrt_cmp(const void *a, const void *b, void *arg)
{
GBT_VARKEY_R ar = gbt_var_key_readable(((const Vsrt *) a)->t);
GBT_VARKEY_R br = gbt_var_key_readable(((const Vsrt *) b)->t);
- const gbtree_vinfo *tinfo = (const gbtree_vinfo *) arg;
+ const gbt_vsrt_arg *varg = (const gbt_vsrt_arg *) arg;
int res;
- res = (*tinfo->f_cmp) (ar.lower, br.lower);
+ res = (*varg->tinfo->f_cmp) (ar.lower, br.lower, varg->collation);
if (res == 0)
- return (*tinfo->f_cmp) (ar.upper, br.upper);
+ return (*varg->tinfo->f_cmp) (ar.upper, br.upper, varg->collation);
return res;
}
GIST_SPLITVEC *
-gbt_var_picksplit(const GistEntryVector *entryvec, GIST_SPLITVEC *v, const gbtree_vinfo *tinfo)
+gbt_var_picksplit(const GistEntryVector *entryvec, GIST_SPLITVEC *v,
+ Oid collation, const gbtree_vinfo *tinfo)
{
OffsetNumber i,
maxoff = entryvec->n - 1;
@@ -464,6 +450,7 @@ gbt_var_picksplit(const GistEntryVector *entryvec, GIST_SPLITVEC *v, const gbtre
nbytes;
char *cur;
GBT_VARKEY **sv = NULL;
+ gbt_vsrt_arg varg;
arr = (Vsrt *) palloc((maxoff + 1) * sizeof(Vsrt));
nbytes = (maxoff + 2) * sizeof(OffsetNumber);
@@ -497,11 +484,13 @@ gbt_var_picksplit(const GistEntryVector *entryvec, GIST_SPLITVEC *v, const gbtre
}
/* sort */
+ varg.tinfo = tinfo;
+ varg.collation = collation;
qsort_arg((void *) &arr[FirstOffsetNumber],
maxoff - FirstOffsetNumber + 1,
sizeof(Vsrt),
gbt_vsrt_cmp,
- (void *) tinfo);
+ (void *) &varg);
/* We do simply create two parts */
@@ -509,13 +498,13 @@ gbt_var_picksplit(const GistEntryVector *entryvec, GIST_SPLITVEC *v, const gbtre
{
if (i <= (maxoff - FirstOffsetNumber + 1) / 2)
{
- gbt_var_bin_union(&v->spl_ldatum, arr[i].t, tinfo);
+ gbt_var_bin_union(&v->spl_ldatum, arr[i].t, collation, tinfo);
v->spl_left[v->spl_nleft] = arr[i].i;
v->spl_nleft++;
}
else
{
- gbt_var_bin_union(&v->spl_rdatum, arr[i].t, tinfo);
+ gbt_var_bin_union(&v->spl_rdatum, arr[i].t, collation, tinfo);
v->spl_right[v->spl_nright] = arr[i].i;
v->spl_nright++;
}
@@ -546,63 +535,61 @@ gbt_var_picksplit(const GistEntryVector *entryvec, GIST_SPLITVEC *v, const gbtre
* The GiST consistent method
*/
bool
-gbt_var_consistent(
- GBT_VARKEY_R *key,
+gbt_var_consistent(GBT_VARKEY_R *key,
const void *query,
- const StrategyNumber *strategy,
+ StrategyNumber strategy,
+ Oid collation,
bool is_leaf,
- const gbtree_vinfo *tinfo
-)
+ const gbtree_vinfo *tinfo)
{
bool retval = FALSE;
- switch (*strategy)
+ switch (strategy)
{
case BTLessEqualStrategyNumber:
if (is_leaf)
- retval = (*tinfo->f_ge) (query, (void *) key->lower);
+ retval = (*tinfo->f_ge) (query, key->lower, collation);
else
- retval = (*tinfo->f_cmp) ((bytea *) query, key->lower) >= 0
+ retval = (*tinfo->f_cmp) (query, key->lower, collation) >= 0
|| gbt_var_node_pf_match(key, query, tinfo);
break;
case BTLessStrategyNumber:
if (is_leaf)
- retval = (*tinfo->f_gt) (query, (void *) key->lower);
+ retval = (*tinfo->f_gt) (query, key->lower, collation);
else
- retval = (*tinfo->f_cmp) ((bytea *) query, key->lower) >= 0
+ retval = (*tinfo->f_cmp) (query, key->lower, collation) >= 0
|| gbt_var_node_pf_match(key, query, tinfo);
break;
case BTEqualStrategyNumber:
if (is_leaf)
- retval = (*tinfo->f_eq) (query, (void *) key->lower);
+ retval = (*tinfo->f_eq) (query, key->lower, collation);
else
- retval = (
- (
- (*tinfo->f_cmp) (key->lower, (bytea *) query) <= 0 &&
- (*tinfo->f_cmp) ((bytea *) query, (void *) key->upper) <= 0
- ) || gbt_var_node_pf_match(key, query, tinfo)
- );
+ retval =
+ ((*tinfo->f_cmp) (key->lower, query, collation) <= 0 &&
+ (*tinfo->f_cmp) (query, key->upper, collation) <= 0) ||
+ gbt_var_node_pf_match(key, query, tinfo);
break;
case BTGreaterStrategyNumber:
if (is_leaf)
- retval = (*tinfo->f_lt) (query, (void *) key->upper);
+ retval = (*tinfo->f_lt) (query, key->upper, collation);
else
- retval = (*tinfo->f_cmp) ((bytea *) query, key->upper) <= 0
+ retval = (*tinfo->f_cmp) (query, key->upper, collation) <= 0
|| gbt_var_node_pf_match(key, query, tinfo);
break;
case BTGreaterEqualStrategyNumber:
if (is_leaf)
- retval = (*tinfo->f_le) (query, (void *) key->upper);
+ retval = (*tinfo->f_le) (query, key->upper, collation);
else
- retval = (*tinfo->f_cmp) ((bytea *) query, key->upper) <= 0
+ retval = (*tinfo->f_cmp) (query, key->upper, collation) <= 0
|| gbt_var_node_pf_match(key, query, tinfo);
break;
case BtreeGistNotEqualStrategyNumber:
- retval = !((*tinfo->f_eq) (query, key->lower) && (*tinfo->f_eq) (query, key->upper));
+ retval = !((*tinfo->f_eq) (query, key->lower, collation) &&
+ (*tinfo->f_eq) (query, key->upper, collation));
break;
default:
retval = FALSE;
}
- return (retval);
+ return retval;
}
diff --git a/contrib/btree_gist/btree_utils_var.h b/contrib/btree_gist/btree_utils_var.h
index 2c1012f323..fe91d122e6 100644
--- a/contrib/btree_gist/btree_utils_var.h
+++ b/contrib/btree_gist/btree_utils_var.h
@@ -18,18 +18,9 @@ typedef struct
*upper;
} GBT_VARKEY_R;
-/* used for key sorting */
-typedef struct
-{
- int i;
- GBT_VARKEY *t;
-} Vsrt;
-
/*
- type description
-*/
-
-
+ * type description
+ */
typedef struct
{
@@ -42,12 +33,12 @@ typedef struct
/* Methods */
- bool (*f_gt) (const void *, const void *); /* greater then */
- bool (*f_ge) (const void *, const void *); /* greater equal */
- bool (*f_eq) (const void *, const void *); /* equal */
- bool (*f_le) (const void *, const void *); /* less equal */
- bool (*f_lt) (const void *, const void *); /* less then */
- int32 (*f_cmp) (const bytea *, const bytea *); /* node compare */
+ bool (*f_gt) (const void *, const void *, Oid); /* greater than */
+ bool (*f_ge) (const void *, const void *, Oid); /* greater equal */
+ bool (*f_eq) (const void *, const void *, Oid); /* equal */
+ bool (*f_le) (const void *, const void *, Oid); /* less equal */
+ bool (*f_lt) (const void *, const void *, Oid); /* less than */
+ int32 (*f_cmp) (const void *, const void *, Oid); /* compare */
GBT_VARKEY *(*f_l2n) (GBT_VARKEY *); /* convert leaf to node */
} gbtree_vinfo;
@@ -60,21 +51,22 @@ extern GBT_VARKEY *gbt_var_key_copy(const GBT_VARKEY_R *u, bool force_node);
extern GISTENTRY *gbt_var_compress(GISTENTRY *entry, const gbtree_vinfo *tinfo);
extern GBT_VARKEY *gbt_var_union(const GistEntryVector *entryvec, int32 *size,
- const gbtree_vinfo *tinfo);
+ Oid collation, const gbtree_vinfo *tinfo);
-extern bool gbt_var_same(bool *result, const Datum d1, const Datum d2,
+extern bool gbt_var_same(Datum d1, Datum d2, Oid collation,
const gbtree_vinfo *tinfo);
extern float *gbt_var_penalty(float *res, const GISTENTRY *o, const GISTENTRY *n,
- const gbtree_vinfo *tinfo);
+ Oid collation, const gbtree_vinfo *tinfo);
extern bool gbt_var_consistent(GBT_VARKEY_R *key, const void *query,
- const StrategyNumber *strategy, bool is_leaf,
+ StrategyNumber strategy, Oid collation, bool is_leaf,
const gbtree_vinfo *tinfo);
extern GIST_SPLITVEC *gbt_var_picksplit(const GistEntryVector *entryvec, GIST_SPLITVEC *v,
- const gbtree_vinfo *tinfo);
-extern void gbt_var_bin_union(Datum *u, GBT_VARKEY *e,
+ Oid collation, const gbtree_vinfo *tinfo);
+
+extern void gbt_var_bin_union(Datum *u, GBT_VARKEY *e, Oid collation,
const gbtree_vinfo *tinfo);
#endif
diff --git a/contrib/citext/.gitignore b/contrib/citext/.gitignore
index 19b6c5ba42..5dcb3ff972 100644
--- a/contrib/citext/.gitignore
+++ b/contrib/citext/.gitignore
@@ -1,2 +1,4 @@
# Generated subdirectories
+/log/
/results/
+/tmp_check/
diff --git a/contrib/cube/.gitignore b/contrib/cube/.gitignore
index a6484a05e7..cb4c989fff 100644
--- a/contrib/cube/.gitignore
+++ b/contrib/cube/.gitignore
@@ -1,4 +1,6 @@
/cubeparse.c
/cubescan.c
# Generated subdirectories
+/log/
/results/
+/tmp_check/
diff --git a/contrib/dblink/.gitignore b/contrib/dblink/.gitignore
index 19b6c5ba42..5dcb3ff972 100644
--- a/contrib/dblink/.gitignore
+++ b/contrib/dblink/.gitignore
@@ -1,2 +1,4 @@
# Generated subdirectories
+/log/
/results/
+/tmp_check/
diff --git a/contrib/dict_int/.gitignore b/contrib/dict_int/.gitignore
index 19b6c5ba42..5dcb3ff972 100644
--- a/contrib/dict_int/.gitignore
+++ b/contrib/dict_int/.gitignore
@@ -1,2 +1,4 @@
# Generated subdirectories
+/log/
/results/
+/tmp_check/
diff --git a/contrib/dict_xsyn/.gitignore b/contrib/dict_xsyn/.gitignore
index 19b6c5ba42..5dcb3ff972 100644
--- a/contrib/dict_xsyn/.gitignore
+++ b/contrib/dict_xsyn/.gitignore
@@ -1,2 +1,4 @@
# Generated subdirectories
+/log/
/results/
+/tmp_check/
diff --git a/contrib/earthdistance/.gitignore b/contrib/earthdistance/.gitignore
index 19b6c5ba42..5dcb3ff972 100644
--- a/contrib/earthdistance/.gitignore
+++ b/contrib/earthdistance/.gitignore
@@ -1,2 +1,4 @@
# Generated subdirectories
+/log/
/results/
+/tmp_check/
diff --git a/contrib/earthdistance/Makefile b/contrib/earthdistance/Makefile
index 49f6e6675f..48a7cf8c7c 100644
--- a/contrib/earthdistance/Makefile
+++ b/contrib/earthdistance/Makefile
@@ -6,6 +6,7 @@ EXTENSION = earthdistance
DATA = earthdistance--1.0.sql earthdistance--unpackaged--1.0.sql
REGRESS = earthdistance
+REGRESS_OPTS = --extra-install=contrib/cube
LDFLAGS_SL += $(filter -lm, $(LIBS))
diff --git a/contrib/file_fdw/.gitignore b/contrib/file_fdw/.gitignore
index 19b6c5ba42..5dcb3ff972 100644
--- a/contrib/file_fdw/.gitignore
+++ b/contrib/file_fdw/.gitignore
@@ -1,2 +1,4 @@
# Generated subdirectories
+/log/
/results/
+/tmp_check/
diff --git a/contrib/hstore/.gitignore b/contrib/hstore/.gitignore
index 19b6c5ba42..5dcb3ff972 100644
--- a/contrib/hstore/.gitignore
+++ b/contrib/hstore/.gitignore
@@ -1,2 +1,4 @@
# Generated subdirectories
+/log/
/results/
+/tmp_check/
diff --git a/contrib/intarray/.gitignore b/contrib/intarray/.gitignore
index 19b6c5ba42..5dcb3ff972 100644
--- a/contrib/intarray/.gitignore
+++ b/contrib/intarray/.gitignore
@@ -1,2 +1,4 @@
# Generated subdirectories
+/log/
/results/
+/tmp_check/
diff --git a/contrib/ltree/.gitignore b/contrib/ltree/.gitignore
index 19b6c5ba42..5dcb3ff972 100644
--- a/contrib/ltree/.gitignore
+++ b/contrib/ltree/.gitignore
@@ -1,2 +1,4 @@
# Generated subdirectories
+/log/
/results/
+/tmp_check/
diff --git a/contrib/pg_trgm/.gitignore b/contrib/pg_trgm/.gitignore
index 19b6c5ba42..5dcb3ff972 100644
--- a/contrib/pg_trgm/.gitignore
+++ b/contrib/pg_trgm/.gitignore
@@ -1,2 +1,4 @@
# Generated subdirectories
+/log/
/results/
+/tmp_check/
diff --git a/contrib/pg_upgrade/check.c b/contrib/pg_upgrade/check.c
index d1dc5dbeaa..a9436ce5b2 100644
--- a/contrib/pg_upgrade/check.c
+++ b/contrib/pg_upgrade/check.c
@@ -46,7 +46,7 @@ check_old_cluster(bool live_check,
/* -- OLD -- */
if (!live_check)
- start_postmaster(&old_cluster, false);
+ start_postmaster(&old_cluster);
set_locale_and_encoding(&old_cluster);
@@ -104,7 +104,7 @@ check_old_cluster(bool live_check,
}
if (!live_check)
- stop_postmaster(false, false);
+ stop_postmaster(false);
}
@@ -134,7 +134,7 @@ report_clusters_compatible(void)
{
pg_log(PG_REPORT, "\n*Clusters are compatible*\n");
/* stops new cluster */
- stop_postmaster(false, false);
+ stop_postmaster(false);
exit(0);
}
@@ -152,7 +152,7 @@ issue_warnings(char *sequence_script_file_name)
/* old = PG 8.3 warnings? */
if (GET_MAJOR_VERSION(old_cluster.major_version) <= 803)
{
- start_postmaster(&new_cluster, true);
+ start_postmaster(&new_cluster);
/* restore proper sequence values using file created from old server */
if (sequence_script_file_name)
@@ -171,15 +171,15 @@ issue_warnings(char *sequence_script_file_name)
old_8_3_rebuild_tsvector_tables(&new_cluster, false);
old_8_3_invalidate_hash_gin_indexes(&new_cluster, false);
old_8_3_invalidate_bpchar_pattern_ops_indexes(&new_cluster, false);
- stop_postmaster(false, true);
+ stop_postmaster(false);
}
/* Create dummy large object permissions for old < PG 9.0? */
if (GET_MAJOR_VERSION(old_cluster.major_version) <= 804)
{
- start_postmaster(&new_cluster, true);
+ start_postmaster(&new_cluster);
new_9_0_populate_pg_largeobject_metadata(&new_cluster, false);
- stop_postmaster(false, true);
+ stop_postmaster(false);
}
}
@@ -264,7 +264,7 @@ check_cluster_compatibility(bool live_check)
/* Is it 9.0 but without tablespace directories? */
if (GET_MAJOR_VERSION(new_cluster.major_version) == 900 &&
- new_cluster.controldata.cat_ver < TABLE_SPACE_SUBDIRS)
+ new_cluster.controldata.cat_ver < TABLE_SPACE_SUBDIRS_CAT_VER)
pg_log(PG_FATAL, "This utility can only upgrade to PostgreSQL version 9.0 after 2010-01-11\n"
"because of backend API changes made during development.\n");
}
diff --git a/contrib/pg_upgrade/file.c b/contrib/pg_upgrade/file.c
index f8f7233593..0552541c24 100644
--- a/contrib/pg_upgrade/file.c
+++ b/contrib/pg_upgrade/file.c
@@ -12,9 +12,10 @@
#include <fcntl.h>
-static int copy_file(const char *fromfile, const char *tofile, bool force);
-#ifdef WIN32
+#ifndef WIN32
+static int copy_file(const char *fromfile, const char *tofile, bool force);
+#else
static int win32_pghardlink(const char *src, const char *dst);
#endif
@@ -126,6 +127,7 @@ linkAndUpdateFile(pageCnvCtx *pageConverter,
}
+#ifndef WIN32
static int
copy_file(const char *srcfile, const char *dstfile, bool force)
{
@@ -220,6 +222,7 @@ copy_file(const char *srcfile, const char *dstfile, bool force)
return 1;
}
+#endif
/*
diff --git a/contrib/pg_upgrade/pg_upgrade.c b/contrib/pg_upgrade/pg_upgrade.c
index e435aaef08..9f7a538765 100644
--- a/contrib/pg_upgrade/pg_upgrade.c
+++ b/contrib/pg_upgrade/pg_upgrade.c
@@ -77,7 +77,7 @@ main(int argc, char **argv)
/* -- NEW -- */
- start_postmaster(&new_cluster, false);
+ start_postmaster(&new_cluster);
check_new_cluster();
report_clusters_compatible();
@@ -88,7 +88,7 @@ main(int argc, char **argv)
disable_old_cluster();
prepare_new_cluster();
- stop_postmaster(false, false);
+ stop_postmaster(false);
/*
* Destructive Changes to New Cluster
@@ -98,10 +98,15 @@ main(int argc, char **argv)
/* New now using xids of the old system */
+ /* -- NEW -- */
+ start_postmaster(&new_cluster);
+
prepare_new_databases();
create_new_objects();
+ stop_postmaster(false);
+
transfer_all_new_dbs(&old_cluster.dbarr, &new_cluster.dbarr,
old_cluster.pgdata, new_cluster.pgdata);
@@ -216,9 +221,6 @@ prepare_new_cluster(void)
static void
prepare_new_databases(void)
{
- /* -- NEW -- */
- start_postmaster(&new_cluster, false);
-
/*
* We set autovacuum_freeze_max_age to its maximum value so autovacuum
* does not launch here and delete clog files, before the frozen xids are
@@ -252,8 +254,6 @@ prepare_new_databases(void)
/* we load this to get a current list of databases */
get_db_and_rel_infos(&new_cluster);
-
- stop_postmaster(false, false);
}
@@ -262,9 +262,6 @@ create_new_objects(void)
{
int dbnum;
- /* -- NEW -- */
- start_postmaster(&new_cluster, false);
-
prep_status("Adding support functions to new cluster");
for (dbnum = 0; dbnum < new_cluster.dbarr.ndbs; dbnum++)
@@ -290,8 +287,6 @@ create_new_objects(void)
get_db_and_rel_infos(&new_cluster);
uninstall_support_functions_from_new_cluster();
-
- stop_postmaster(false, false);
}
diff --git a/contrib/pg_upgrade/pg_upgrade.h b/contrib/pg_upgrade/pg_upgrade.h
index 5ca570eb15..04f67e1e34 100644
--- a/contrib/pg_upgrade/pg_upgrade.h
+++ b/contrib/pg_upgrade/pg_upgrade.h
@@ -58,7 +58,9 @@
#define atooid(x) ((Oid) strtoul((x), NULL, 10))
/* OID system catalog preservation added during PG 9.0 development */
-#define TABLE_SPACE_SUBDIRS 201001111
+#define TABLE_SPACE_SUBDIRS_CAT_VER 201001111
+/* postmaster/postgres -b (binary_upgrade) flag added during PG 9.1 development */
+#define BINARY_UPGRADE_SERVER_FLAG_CAT_VER 201104251
/*
* Each relation is represented by a relinfo structure.
@@ -225,7 +227,6 @@ typedef struct
int num_tablespaces;
char **libraries; /* loadable libraries */
int num_libraries;
- pgpid_t postmasterPID; /* PID of currently running postmaster */
ClusterInfo *running_cluster;
} OSInfo;
@@ -357,8 +358,8 @@ void init_tablespaces(void);
PGconn *connectToServer(ClusterInfo *cluster, const char *db_name);
PGresult *executeQueryOrDie(PGconn *conn, const char *fmt,...);
-void start_postmaster(ClusterInfo *cluster, bool quiet);
-void stop_postmaster(bool fast, bool quiet);
+void start_postmaster(ClusterInfo *cluster);
+void stop_postmaster(bool fast);
uint32 get_major_server_version(ClusterInfo *cluster);
void check_for_libpq_envvars(void);
diff --git a/contrib/pg_upgrade/server.c b/contrib/pg_upgrade/server.c
index 2a0f50eb2a..935ce32a61 100644
--- a/contrib/pg_upgrade/server.c
+++ b/contrib/pg_upgrade/server.c
@@ -9,13 +9,8 @@
#include "pg_upgrade.h"
-#define POSTMASTER_UPTIME 20
-#define STARTUP_WARNING_TRIES 2
-
-
-static pgpid_t get_postmaster_pid(const char *datadir);
-static bool test_server_conn(ClusterInfo *cluster, int timeout);
+static PGconn *get_db_conn(ClusterInfo *cluster, const char *db_name);
/*
@@ -28,14 +23,7 @@ static bool test_server_conn(ClusterInfo *cluster, int timeout);
PGconn *
connectToServer(ClusterInfo *cluster, const char *db_name)
{
- unsigned short port = cluster->port;
- char connectString[MAXPGPATH];
- PGconn *conn;
-
- snprintf(connectString, sizeof(connectString),
- "dbname = '%s' user = '%s' port = %d", db_name, os_info.user, port);
-
- conn = PQconnectdb(connectString);
+ PGconn *conn = get_db_conn(cluster, db_name);
if (conn == NULL || PQstatus(conn) != CONNECTION_OK)
{
@@ -54,6 +42,24 @@ connectToServer(ClusterInfo *cluster, const char *db_name)
/*
+ * get_db_conn()
+ *
+ * get database connection
+ */
+static PGconn *
+get_db_conn(ClusterInfo *cluster, const char *db_name)
+{
+ char conn_opts[MAXPGPATH];
+
+ snprintf(conn_opts, sizeof(conn_opts),
+ "dbname = '%s' user = '%s' port = %d", db_name, os_info.user,
+ cluster->port);
+
+ return PQconnectdb(conn_opts);
+}
+
+
+/*
* executeQueryOrDie()
*
* Formats a query string from the given arguments and executes the
@@ -91,38 +97,6 @@ executeQueryOrDie(PGconn *conn, const char *fmt,...)
/*
- * get_postmaster_pid()
- *
- * Returns the pid of the postmaster running on datadir. pid is retrieved
- * from the postmaster.pid file
- */
-static pgpid_t
-get_postmaster_pid(const char *datadir)
-{
- FILE *pidf;
- long pid;
- char pid_file[MAXPGPATH];
-
- snprintf(pid_file, sizeof(pid_file), "%s/postmaster.pid", datadir);
- pidf = fopen(pid_file, "r");
-
- if (pidf == NULL)
- return (pgpid_t) 0;
-
- if (fscanf(pidf, "%ld", &pid) != 1)
- {
- fclose(pidf);
- pg_log(PG_FATAL, "%s: invalid data in PID file \"%s\"\n",
- os_info.progname, pid_file);
- }
-
- fclose(pidf);
-
- return (pgpid_t) pid;
-}
-
-
-/*
* get_major_server_version()
*
* gets the version (in unsigned int form) for the given "datadir". Assumes
@@ -160,23 +134,28 @@ stop_postmaster_atexit(void)
stop_postmaster_on_exit(int exitstatus, void *arg)
#endif
{
- stop_postmaster(true, true);
+ stop_postmaster(true);
}
void
-start_postmaster(ClusterInfo *cluster, bool quiet)
+start_postmaster(ClusterInfo *cluster)
{
char cmd[MAXPGPATH];
- const char *bindir;
- const char *datadir;
- unsigned short port;
+ PGconn *conn;
bool exit_hook_registered = false;
-
- bindir = cluster->bindir;
- datadir = cluster->pgdata;
- port = cluster->port;
+#ifndef WIN32
+ char *output_filename = log_opts.filename;
+#else
+ /*
+ * On Win32, we can't send both pg_upgrade output and pg_ctl output to the
+ * same file because we get the error: "The process cannot access the file
+ * because it is being used by another process." so we have to send all
+ * other output to 'nul'.
+ */
+ char *output_filename = DEVNULL;
+#endif
if (!exit_hook_registered)
{
@@ -189,11 +168,6 @@ start_postmaster(ClusterInfo *cluster, bool quiet)
}
/*
- * On Win32, we can't send both pg_upgrade output and pg_ctl output to the
- * same file because we get the error: "The process cannot access the file
- * because it is being used by another process." so we have to send all
- * other output to 'nul'.
- *
* Using autovacuum=off disables cleanup vacuum and analyze, but freeze
* vacuums can still happen, so we set autovacuum_freeze_max_age to its
* maximum. We assume all datfrozenxid and relfrozen values are less than
@@ -201,36 +175,44 @@ start_postmaster(ClusterInfo *cluster, bool quiet)
* not touch them.
*/
snprintf(cmd, sizeof(cmd),
- SYSTEMQUOTE "\"%s/pg_ctl\" -l \"%s\" -D \"%s\" "
- "-o \"-p %d -c autovacuum=off "
- "-c autovacuum_freeze_max_age=2000000000\" "
- "start >> \"%s\" 2>&1" SYSTEMQUOTE,
- bindir,
-#ifndef WIN32
- log_opts.filename, datadir, port, log_opts.filename);
-#else
- DEVNULL, datadir, port, DEVNULL);
-#endif
- exec_prog(true, "%s", cmd);
+ SYSTEMQUOTE "\"%s/pg_ctl\" -w -l \"%s\" -D \"%s\" "
+ "-o \"-p %d %s\" start >> \"%s\" 2>&1" SYSTEMQUOTE,
+ cluster->bindir, output_filename, cluster->pgdata, cluster->port,
+ (cluster->controldata.cat_ver >=
+ BINARY_UPGRADE_SERVER_FLAG_CAT_VER) ? "-b" :
+ "-c autovacuum=off -c autovacuum_freeze_max_age=2000000000",
+ log_opts.filename);
- /* wait for the server to start properly */
+ exec_prog(true, "%s", cmd);
- if (test_server_conn(cluster, POSTMASTER_UPTIME) == false)
- pg_log(PG_FATAL, " Unable to start %s postmaster with the command: %s\nPerhaps pg_hba.conf was not set to \"trust\".",
+ /* Check to see if we can connect to the server; if not, report it. */
+ if ((conn = get_db_conn(cluster, "template1")) == NULL ||
+ PQstatus(conn) != CONNECTION_OK)
+ {
+ if (conn)
+ PQfinish(conn);
+ pg_log(PG_FATAL, "unable to connect to %s postmaster started with the command: %s\n"
+ "Perhaps pg_hba.conf was not set to \"trust\".",
CLUSTER_NAME(cluster), cmd);
+ }
+ PQfinish(conn);
- if ((os_info.postmasterPID = get_postmaster_pid(datadir)) == 0)
- pg_log(PG_FATAL, " Unable to get postmaster pid\n");
os_info.running_cluster = cluster;
}
void
-stop_postmaster(bool fast, bool quiet)
+stop_postmaster(bool fast)
{
char cmd[MAXPGPATH];
const char *bindir;
const char *datadir;
+#ifndef WIN32
+ char *output_filename = log_opts.filename;
+#else
+ /* See comment in start_postmaster() about why win32 output is ignored. */
+ char *output_filename = DEVNULL;
+#endif
if (os_info.running_cluster == &old_cluster)
{
@@ -245,70 +227,19 @@ stop_postmaster(bool fast, bool quiet)
else
return; /* no cluster running */
- /* See comment in start_postmaster() about why win32 output is ignored. */
snprintf(cmd, sizeof(cmd),
- SYSTEMQUOTE "\"%s/pg_ctl\" -l \"%s\" -D \"%s\" %s stop >> "
+ SYSTEMQUOTE "\"%s/pg_ctl\" -w -l \"%s\" -D \"%s\" %s stop >> "
"\"%s\" 2>&1" SYSTEMQUOTE,
- bindir,
-#ifndef WIN32
- log_opts.filename, datadir, fast ? "-m fast" : "", log_opts.filename);
-#else
- DEVNULL, datadir, fast ? "-m fast" : "", DEVNULL);
-#endif
+ bindir, output_filename, datadir, fast ? "-m fast" : "",
+ output_filename);
+
exec_prog(fast ? false : true, "%s", cmd);
- os_info.postmasterPID = 0;
os_info.running_cluster = NULL;
}
/*
- * test_server_conn()
- *
- * tests whether postmaster is running or not by trying to connect
- * to it. If connection is unsuccessfull we do a sleep of 1 sec and then
- * try the connection again. This process continues "timeout" times.
- *
- * Returns true if the connection attempt was successfull, false otherwise.
- */
-static bool
-test_server_conn(ClusterInfo *cluster, int timeout)
-{
- unsigned short port = cluster->port;
- PGconn *conn = NULL;
- char con_opts[MAX_STRING];
- int tries;
- bool ret = false;
-
- snprintf(con_opts, sizeof(con_opts),
- "dbname = 'template1' user = '%s' port = %d ", os_info.user, port);
-
- for (tries = 0; tries < timeout; tries++)
- {
- sleep(1);
- if ((conn = PQconnectdb(con_opts)) != NULL &&
- PQstatus(conn) == CONNECTION_OK)
- {
- PQfinish(conn);
- ret = true;
- break;
- }
-
- if (tries == STARTUP_WARNING_TRIES)
- prep_status("Trying to start %s server ",
- CLUSTER_NAME(cluster));
- else if (tries > STARTUP_WARNING_TRIES)
- pg_log(PG_REPORT, ".");
- }
-
- if (tries > STARTUP_WARNING_TRIES)
- check_ok();
-
- return ret;
-}
-
-
-/*
* check_for_libpq_envvars()
*
* tests whether any libpq environment variables are set.
diff --git a/contrib/pgcrypto/.gitignore b/contrib/pgcrypto/.gitignore
index 19b6c5ba42..5dcb3ff972 100644
--- a/contrib/pgcrypto/.gitignore
+++ b/contrib/pgcrypto/.gitignore
@@ -1,2 +1,4 @@
# Generated subdirectories
+/log/
/results/
+/tmp_check/
diff --git a/contrib/seg/.gitignore b/contrib/seg/.gitignore
index 102f8b3246..69e73d2096 100644
--- a/contrib/seg/.gitignore
+++ b/contrib/seg/.gitignore
@@ -1,4 +1,6 @@
/segparse.c
/segscan.c
# Generated subdirectories
+/log/
/results/
+/tmp_check/
diff --git a/contrib/tablefunc/.gitignore b/contrib/tablefunc/.gitignore
index 19b6c5ba42..5dcb3ff972 100644
--- a/contrib/tablefunc/.gitignore
+++ b/contrib/tablefunc/.gitignore
@@ -1,2 +1,4 @@
# Generated subdirectories
+/log/
/results/
+/tmp_check/
diff --git a/contrib/test_parser/.gitignore b/contrib/test_parser/.gitignore
index 19b6c5ba42..5dcb3ff972 100644
--- a/contrib/test_parser/.gitignore
+++ b/contrib/test_parser/.gitignore
@@ -1,2 +1,4 @@
# Generated subdirectories
+/log/
/results/
+/tmp_check/
diff --git a/contrib/tsearch2/.gitignore b/contrib/tsearch2/.gitignore
index 19b6c5ba42..5dcb3ff972 100644
--- a/contrib/tsearch2/.gitignore
+++ b/contrib/tsearch2/.gitignore
@@ -1,2 +1,4 @@
# Generated subdirectories
+/log/
/results/
+/tmp_check/
diff --git a/contrib/unaccent/.gitignore b/contrib/unaccent/.gitignore
index 19b6c5ba42..5dcb3ff972 100644
--- a/contrib/unaccent/.gitignore
+++ b/contrib/unaccent/.gitignore
@@ -1,2 +1,4 @@
# Generated subdirectories
+/log/
/results/
+/tmp_check/
diff --git a/contrib/xml2/.gitignore b/contrib/xml2/.gitignore
index 19b6c5ba42..5dcb3ff972 100644
--- a/contrib/xml2/.gitignore
+++ b/contrib/xml2/.gitignore
@@ -1,2 +1,4 @@
# Generated subdirectories
+/log/
/results/
+/tmp_check/
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index aa14d7d39a..b4bb6d7652 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -2172,8 +2172,8 @@
(<structfield>collname</>, <structfield>collnamespace</>).
<productname>PostgreSQL</productname> generally ignores all
collations that do not have <structfield>collencoding</> equal to
- either the current database's encoding or -1, and creation of new
- entries matching an entry with <structfield>collencoding</> = -1
+ either the current database's encoding or -1, and creation of new entries
+ with the same name as an entry with <structfield>collencoding</> = -1
is forbidden. Therefore it is sufficient to use a qualified SQL name
(<replaceable>schema</>.<replaceable>name</>) to identify a collation,
even though this is not unique according to the catalog definition.
@@ -6151,8 +6151,8 @@
of the type. If the type does not support collations, this will
be zero. A base type that supports collations will have
<symbol>DEFAULT_COLLATION_OID</symbol> here. A domain over a
- collatable type can have some other collation OID, if one was defined
- for the domain.
+ collatable type can have some other collation OID, if one was
+ specified for the domain.
</para></entry>
</row>
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index 13b888dff8..bc1ec3f821 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -527,10 +527,10 @@ NUMERIC
<note>
<para>
- The maximum allowed precision when explicitely specified in the
- type declaration is 1000; otherwise the current implementation
- of the <type>NUMERIC</type> is subject to the limits described
- in <xref linkend="datatype-numeric-table">.
+ The maximum allowed precision when explicitly specified in the
+ type declaration is 1000; <type>NUMERIC</type> without a specified
+ precision is subject to the limits described in <xref
+ linkend="datatype-numeric-table">.
</para>
</note>
diff --git a/doc/src/sgml/indices.sgml b/doc/src/sgml/indices.sgml
index 15fbe0d614..2dedb153c0 100644
--- a/doc/src/sgml/indices.sgml
+++ b/doc/src/sgml/indices.sgml
@@ -1004,12 +1004,11 @@ SELECT am.amname AS index_method,
<sect1 id="indexes-collations">
- <title>Collations and Indexes</title>
+ <title>Indexes and Collations</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.
+ An index can support only one collation per index column.
+ If multiple collations are of interest, multiple indexes may be needed.
</para>
<para>
@@ -1022,23 +1021,21 @@ CREATE TABLE test1c (
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
+ The index automatically uses the collation of the
+ underlying column. So a query of the form
<programlisting>
-SELECT * FROM test1c WHERE content = <replaceable>constant</replaceable>;
+SELECT * FROM test1c WHERE content &gt; <replaceable>constant</replaceable>;
</programlisting>
- could use the index.
- </para>
-
- <para>
- If in addition, a query of the form, say,
+ could use the index, because the comparison will by default use the
+ collation of the column. However, this index cannot accelerate queries
+ that involve some other collation. So if queries of the form, say,
<programlisting>
SELECT * FROM test1c WHERE content &gt; <replaceable>constant</replaceable> COLLATE "y";
</programlisting>
- is of interest, an additional index could be created that supports
- the <literal>"y"</literal> collation, like so:
+ are also of interest, an additional index could be created that supports
+ the <literal>"y"</literal> collation, like this:
<programlisting>
-CREATE INDEX test1c_content_index ON test1c (content COLLATE "y");
+CREATE INDEX test1c_content_y_index ON test1c (content COLLATE "y");
</programlisting>
</para>
</sect1>
diff --git a/doc/src/sgml/ref/create_collation.sgml b/doc/src/sgml/ref/create_collation.sgml
index fc79225001..c853576814 100644
--- a/doc/src/sgml/ref/create_collation.sgml
+++ b/doc/src/sgml/ref/create_collation.sgml
@@ -108,8 +108,8 @@ CREATE COLLATION <replaceable>name</replaceable> FROM <replaceable>existing_coll
<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.
+ will have the same properties as the existing one, but it
+ will be an independent object.
</para>
</listitem>
</varlistentry>
@@ -134,7 +134,8 @@ CREATE COLLATION <replaceable>name</replaceable> FROM <replaceable>existing_coll
<title>Examples</title>
<para>
- To create a collation from the locale <literal>fr_FR.utf8</literal>
+ To create a collation from the operating system locale
+ <literal>fr_FR.utf8</literal>
(assuming the current database encoding is <literal>UTF8</literal>):
<programlisting>
CREATE COLLATION french (LOCALE = 'fr_FR.utf8');
diff --git a/doc/src/sgml/ref/create_domain.sgml b/doc/src/sgml/ref/create_domain.sgml
index 2300edefe3..8db90f911f 100644
--- a/doc/src/sgml/ref/create_domain.sgml
+++ b/doc/src/sgml/ref/create_domain.sgml
@@ -90,7 +90,7 @@ CREATE DOMAIN <replaceable class="parameter">name</replaceable> [ AS ] <replacea
<para>
An optional collation for the domain. If no collation is
specified, the underlying data type's default collation is used.
- The underlying type must be collatable when <literal>COLLATE</>
+ The underlying type must be collatable if <literal>COLLATE</>
is specified.
</para>
</listitem>
diff --git a/doc/src/sgml/ref/create_index.sgml b/doc/src/sgml/ref/create_index.sgml
index 8ec7abbbd4..43b6499603 100644
--- a/doc/src/sgml/ref/create_index.sgml
+++ b/doc/src/sgml/ref/create_index.sgml
@@ -188,9 +188,8 @@ CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ <replaceable class="parameter">name</
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.
+ indexed. Indexes with non-default collations can be useful for
+ queries that involve expressions using non-default collations.
</para>
</listitem>
</varlistentry>
@@ -538,6 +537,13 @@ CREATE INDEX ON films ((lower(title)));
</para>
<para>
+ To create an index with non-default collation:
+<programlisting>
+CREATE INDEX title_idx_german ON films (title COLLATE "de_DE");
+</programlisting>
+ </para>
+
+ <para>
To create an index with non-default sort ordering of nulls:
<programlisting>
CREATE INDEX title_idx_nulls_low ON films (title NULLS FIRST);
diff --git a/doc/src/sgml/ref/create_type.sgml b/doc/src/sgml/ref/create_type.sgml
index 98e1764b1e..70b0325f0e 100644
--- a/doc/src/sgml/ref/create_type.sgml
+++ b/doc/src/sgml/ref/create_type.sgml
@@ -355,10 +355,10 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
</para>
<para>
- If the optional
+ If the optional boolean
parameter <replaceable class="parameter">collatable</replaceable>
is true, column definitions and expressions of the type may carry
- collation information and allow the use of
+ collation information through 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
diff --git a/doc/src/sgml/ref/grant.sgml b/doc/src/sgml/ref/grant.sgml
index 72ecc45520..93e83320cc 100644
--- a/doc/src/sgml/ref/grant.sgml
+++ b/doc/src/sgml/ref/grant.sgml
@@ -80,8 +80,8 @@ GRANT <replaceable class="PARAMETER">role_name</replaceable> [, ...] TO <replace
<para>
The <command>GRANT</command> command has two basic variants: one
- that grants privileges on a database object (table, column, view, sequence,
- database, foreign-data wrapper, foreign server, function,
+ that grants privileges on a database object (table, column, view, foreign
+ table, sequence, database, foreign-data wrapper, foreign server, function,
procedural language, schema, or tablespace), and one that grants
membership in a role. These variants are similar in many ways, but
they are different enough to be described separately.
diff --git a/doc/src/sgml/ref/select.sgml b/doc/src/sgml/ref/select.sgml
index 8bf7a06022..5ff1ee834e 100644
--- a/doc/src/sgml/ref/select.sgml
+++ b/doc/src/sgml/ref/select.sgml
@@ -1090,7 +1090,7 @@ OFFSET <replaceable class="parameter">start</replaceable>
</para>
<para>
- SQL:2008 introduced a different syntax to achieve the same thing,
+ SQL:2008 introduced a different syntax to achieve the same result,
which <productname>PostgreSQL</> also supports. It is:
<synopsis>
OFFSET <replaceable class="parameter">start</replaceable> { ROW | ROWS }
diff --git a/doc/src/sgml/regress.sgml b/doc/src/sgml/regress.sgml
index 874d45615e..60250bb7a9 100644
--- a/doc/src/sgml/regress.sgml
+++ b/doc/src/sgml/regress.sgml
@@ -238,7 +238,7 @@ gmake check LANG=C ENCODING=EUC_JP
</sect2>
<sect2>
- <title>Extra tests</title>
+ <title>Extra Tests</title>
<para>
The regression test suite contains a few test files that are not
@@ -253,8 +253,8 @@ gmake check EXTRA_TESTS=numeric_big
<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.
+ The <literal>collate.linux.utf8</> test works only on Linux/glibc
+ platforms, and only when run in a database that uses UTF-8 encoding.
</para>
</sect2>
</sect1>
diff --git a/doc/src/sgml/stylesheet.dsl b/doc/src/sgml/stylesheet.dsl
index b95b357294..637758ff42 100644
--- a/doc/src/sgml/stylesheet.dsl
+++ b/doc/src/sgml/stylesheet.dsl
@@ -163,6 +163,22 @@
;; Add more here if needed...
+;; Replace a sequence of whitespace in a string by a single space
+(define (normalize-whitespace str #!optional (whitespace '(#\space #\U-000D)))
+ (let loop ((characters (string->list str))
+ (result '())
+ (prev-was-space #f))
+ (if (null? characters)
+ (list->string (reverse result))
+ (let ((c (car characters))
+ (rest (cdr characters)))
+ (if (member c whitespace)
+ (if prev-was-space
+ (loop rest result #t)
+ (loop rest (cons #\space result) #t))
+ (loop rest (cons c result) #f))))))
+
+
<!-- HTML output customization ..................................... -->
<![ %output-html; [
@@ -414,6 +430,26 @@
(literal "")))))
+;; Changed to strip and normalize index term content (overrides
+;; dbindex.dsl)
+(define (htmlindexterm)
+ (let* ((attr (gi (current-node)))
+ (content (data (current-node)))
+ (string (strip (normalize-whitespace content))) ;; changed
+ (sortas (attribute-string (normalize "sortas"))))
+ (make sequence
+ (make formatting-instruction data: attr)
+ (if sortas
+ (make sequence
+ (make formatting-instruction data: "[")
+ (make formatting-instruction data: sortas)
+ (make formatting-instruction data: "]"))
+ (empty-sosofo))
+ (make formatting-instruction data: " ")
+ (make formatting-instruction data: string)
+ (htmlnewline))))
+
+
]]> <!-- %output-html -->
diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index 58b83bbf12..a6d2a1355c 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -1741,6 +1741,15 @@ typedef struct
itself.
</para>
+ <para>
+ Another important point is to avoid leaving any uninitialized bits
+ within data type values; for example, take care to zero out any
+ alignment padding bytes that might be present in structs. Without
+ this, logically-equivalent constants of your data type might be
+ seen as unequal by the planner, leading to inefficient (though not
+ incorrect) plans.
+ </para>
+
<warning>
<para>
<emphasis>Never</> modify the contents of a pass-by-reference input
@@ -1784,7 +1793,7 @@ typedef struct {
char buffer[40]; /* our source data */
...
text *destination = (text *) palloc(VARHDRSZ + 40);
-destination->length = VARHDRSZ + 40;
+SET_VARSIZE(destination, VARHDRSZ + 40);
memcpy(destination->data, buffer, 40);
...
]]>
@@ -1793,6 +1802,8 @@ memcpy(destination->data, buffer, 40);
<literal>VARHDRSZ</> is the same as <literal>sizeof(int4)</>, but
it's considered good style to use the macro <literal>VARHDRSZ</>
to refer to the size of the overhead for a variable-length type.
+ Also, the length field <emphasis>must</> be set using the
+ <literal>SET_VARSIZE</> macro, not by simple assignment.
</para>
<para>
@@ -2406,13 +2417,16 @@ concat_text(PG_FUNCTION_ARGS)
<listitem>
<para>
- Always zero the bytes of your structures using
- <function>memset</function>. Without this, it's difficult to
+ Always zero the bytes of your structures using <function>memset</>
+ (or allocate them with <function>palloc0</> in the first place).
+ Even if you assign to each field of your structure, there might be
+ alignment padding (holes in the structure) that contain
+ garbage values. Without this, it's difficult to
support hash indexes or hash joins, as you must pick out only
the significant bits of your data structure to compute a hash.
- Even if you initialize all fields of your structure, there might be
- alignment padding (holes in the structure) that contain
- garbage values.
+ The planner also sometimes relies on comparing constants via
+ bitwise equality, so you can get undesirable planning results if
+ logically-equivalent values aren't bitwise equal.
</para>
</listitem>
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index c06a0271ca..16979c4ea7 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -360,6 +360,8 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2)
return false;
if (attr1->attinhcount != attr2->attinhcount)
return false;
+ if (attr1->attcollation != attr2->attcollation)
+ return false;
/* attacl and attoptions are not even present... */
}
@@ -611,7 +613,9 @@ BuildDescForRelation(List *schema)
* BuildDescFromLists
*
* Build a TupleDesc given lists of column names (as String nodes),
- * column type OIDs, and column typmods. No constraints are generated.
+ * column type OIDs, typmods, and collation OIDs.
+ *
+ * No constraints are generated.
*
* This is essentially a cut-down version of BuildDescForRelation for use
* with functions returning RECORD.
diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c
index 227f84d988..5b35b50034 100644
--- a/src/backend/access/gin/ginget.c
+++ b/src/backend/access/gin/ginget.c
@@ -56,7 +56,7 @@ callConsistentFn(GinState *ginstate, GinScanKey key)
key->recheckCurItem = true;
return DatumGetBool(FunctionCall8Coll(&ginstate->consistentFn[key->attnum - 1],
- ginstate->compareCollation[key->attnum - 1],
+ ginstate->supportCollation[key->attnum - 1],
PointerGetDatum(key->entryRes),
UInt16GetDatum(key->strategy),
key->query,
@@ -252,7 +252,7 @@ collectMatchBitmap(GinBtreeData *btree, GinBtreeStack *stack,
*----------
*/
cmp = DatumGetInt32(FunctionCall4Coll(&btree->ginstate->comparePartialFn[attnum - 1],
- btree->ginstate->compareCollation[attnum - 1],
+ btree->ginstate->supportCollation[attnum - 1],
scanEntry->queryKey,
idatum,
UInt16GetDatum(scanEntry->strategy),
@@ -1178,7 +1178,7 @@ matchPartialInPendingList(GinState *ginstate, Page page,
*----------
*/
cmp = DatumGetInt32(FunctionCall4Coll(&ginstate->comparePartialFn[entry->attnum - 1],
- ginstate->compareCollation[entry->attnum - 1],
+ ginstate->supportCollation[entry->attnum - 1],
entry->queryKey,
datum[off - 1],
UInt16GetDatum(entry->strategy),
diff --git a/src/backend/access/gin/ginscan.c b/src/backend/access/gin/ginscan.c
index 37b08c0df6..d9f5b8c012 100644
--- a/src/backend/access/gin/ginscan.c
+++ b/src/backend/access/gin/ginscan.c
@@ -305,14 +305,15 @@ ginNewScanKey(IndexScanDesc scan)
/* OK to call the extractQueryFn */
queryValues = (Datum *)
- DatumGetPointer(FunctionCall7(&so->ginstate.extractQueryFn[skey->sk_attno - 1],
- skey->sk_argument,
- PointerGetDatum(&nQueryValues),
- UInt16GetDatum(skey->sk_strategy),
- PointerGetDatum(&partial_matches),
- PointerGetDatum(&extra_data),
- PointerGetDatum(&nullFlags),
- PointerGetDatum(&searchMode)));
+ DatumGetPointer(FunctionCall7Coll(&so->ginstate.extractQueryFn[skey->sk_attno - 1],
+ so->ginstate.supportCollation[skey->sk_attno - 1],
+ skey->sk_argument,
+ PointerGetDatum(&nQueryValues),
+ UInt16GetDatum(skey->sk_strategy),
+ PointerGetDatum(&partial_matches),
+ PointerGetDatum(&extra_data),
+ PointerGetDatum(&nullFlags),
+ PointerGetDatum(&searchMode)));
/*
* If bogus searchMode is returned, treat as GIN_SEARCH_MODE_ALL; note
diff --git a/src/backend/access/gin/ginutil.c b/src/backend/access/gin/ginutil.c
index a712331cf4..1ae51b1060 100644
--- a/src/backend/access/gin/ginutil.c
+++ b/src/backend/access/gin/ginutil.c
@@ -93,17 +93,17 @@ initGinState(GinState *state, Relation index)
* while doing comparisons. However, we may have a collatable storage
* type for a noncollatable indexed data type (for instance, hstore
* uses text index entries). If there's no index collation then
- * specify default collation in case the comparison function needs
- * collation. This is harmless if the comparison function doesn't
+ * specify default collation in case the support functions need
+ * collation. This is harmless if the support functions don't
* care about collation, so we just do it unconditionally. (We could
* alternatively call get_typcollation, but that seems like expensive
* overkill --- there aren't going to be any cases where a GIN storage
* type has a nondefault collation.)
*/
if (OidIsValid(index->rd_indcollation[i]))
- state->compareCollation[i] = index->rd_indcollation[i];
+ state->supportCollation[i] = index->rd_indcollation[i];
else
- state->compareCollation[i] = DEFAULT_COLLATION_OID;
+ state->supportCollation[i] = DEFAULT_COLLATION_OID;
}
}
@@ -293,7 +293,7 @@ ginCompareEntries(GinState *ginstate, OffsetNumber attnum,
/* both not null, so safe to call the compareFn */
return DatumGetInt32(FunctionCall2Coll(&ginstate->compareFn[attnum - 1],
- ginstate->compareCollation[attnum - 1],
+ ginstate->supportCollation[attnum - 1],
a, b));
}
@@ -399,10 +399,11 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
/* OK, call the opclass's extractValueFn */
nullFlags = NULL; /* in case extractValue doesn't set it */
entries = (Datum *)
- DatumGetPointer(FunctionCall3(&ginstate->extractValueFn[attnum - 1],
- value,
- PointerGetDatum(nentries),
- PointerGetDatum(&nullFlags)));
+ DatumGetPointer(FunctionCall3Coll(&ginstate->extractValueFn[attnum - 1],
+ ginstate->supportCollation[attnum - 1],
+ value,
+ PointerGetDatum(nentries),
+ PointerGetDatum(&nullFlags)));
/*
* Generate a placeholder if the item contained no keys.
@@ -453,7 +454,7 @@ ginExtractEntries(GinState *ginstate, OffsetNumber attnum,
}
arg.cmpDatumFunc = &ginstate->compareFn[attnum - 1];
- arg.collation = ginstate->compareCollation[attnum - 1];
+ arg.collation = ginstate->supportCollation[attnum - 1];
arg.haveDups = false;
qsort_arg(keydata, *nentries, sizeof(keyEntryData),
cmpEntries, (void *) &arg);
diff --git a/src/backend/access/gist/gist.c b/src/backend/access/gist/gist.c
index fae3464600..4881a7dd48 100644
--- a/src/backend/access/gist/gist.c
+++ b/src/backend/access/gist/gist.c
@@ -17,6 +17,7 @@
#include "access/genam.h"
#include "access/gist_private.h"
#include "catalog/index.h"
+#include "catalog/pg_collation.h"
#include "miscadmin.h"
#include "storage/bufmgr.h"
#include "storage/indexfsm.h"
@@ -1394,6 +1395,22 @@ initGISTstate(GISTSTATE *giststate, Relation index)
CurrentMemoryContext);
else
giststate->distanceFn[i].fn_oid = InvalidOid;
+
+ /*
+ * If the index column has a specified collation, we should honor that
+ * while doing comparisons. However, we may have a collatable storage
+ * type for a noncollatable indexed data type. If there's no index
+ * collation then specify default collation in case the support
+ * functions need collation. This is harmless if the support
+ * functions don't care about collation, so we just do it
+ * unconditionally. (We could alternatively call get_typcollation,
+ * but that seems like expensive overkill --- there aren't going to be
+ * any cases where a GIST storage type has a nondefault collation.)
+ */
+ if (OidIsValid(index->rd_indcollation[i]))
+ giststate->supportCollation[i] = index->rd_indcollation[i];
+ else
+ giststate->supportCollation[i] = DEFAULT_COLLATION_OID;
}
}
diff --git a/src/backend/access/gist/gistsplit.c b/src/backend/access/gist/gistsplit.c
index f65c493e98..bd846cecca 100644
--- a/src/backend/access/gist/gistsplit.c
+++ b/src/backend/access/gist/gistsplit.c
@@ -325,16 +325,18 @@ genericPickSplit(GISTSTATE *giststate, GistEntryVector *entryvec, GIST_SPLITVEC
evec->n = v->spl_nleft;
memcpy(evec->vector, entryvec->vector + FirstOffsetNumber,
sizeof(GISTENTRY) * evec->n);
- v->spl_ldatum = FunctionCall2(&giststate->unionFn[attno],
- PointerGetDatum(evec),
- PointerGetDatum(&nbytes));
+ v->spl_ldatum = FunctionCall2Coll(&giststate->unionFn[attno],
+ giststate->supportCollation[attno],
+ PointerGetDatum(evec),
+ PointerGetDatum(&nbytes));
evec->n = v->spl_nright;
memcpy(evec->vector, entryvec->vector + FirstOffsetNumber + v->spl_nleft,
sizeof(GISTENTRY) * evec->n);
- v->spl_rdatum = FunctionCall2(&giststate->unionFn[attno],
- PointerGetDatum(evec),
- PointerGetDatum(&nbytes));
+ v->spl_rdatum = FunctionCall2Coll(&giststate->unionFn[attno],
+ giststate->supportCollation[attno],
+ PointerGetDatum(evec),
+ PointerGetDatum(&nbytes));
}
/*
@@ -361,9 +363,10 @@ gistUserPicksplit(Relation r, GistEntryVector *entryvec, int attno, GistSplitVec
sv->spl_ldatum = v->spl_lattr[attno];
sv->spl_rdatum = v->spl_rattr[attno];
- FunctionCall2(&giststate->picksplitFn[attno],
- PointerGetDatum(entryvec),
- PointerGetDatum(sv));
+ FunctionCall2Coll(&giststate->picksplitFn[attno],
+ giststate->supportCollation[attno],
+ PointerGetDatum(entryvec),
+ PointerGetDatum(sv));
if (sv->spl_nleft == 0 || sv->spl_nright == 0)
{
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index e8bbd564c7..e61b676628 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -207,9 +207,10 @@ gistMakeUnionItVec(GISTSTATE *giststate, IndexTuple *itvec, int len, int startke
}
/* Make union and store in attr array */
- attr[i] = FunctionCall2(&giststate->unionFn[i],
- PointerGetDatum(evec),
- PointerGetDatum(&attrsize));
+ attr[i] = FunctionCall2Coll(&giststate->unionFn[i],
+ giststate->supportCollation[i],
+ PointerGetDatum(evec),
+ PointerGetDatum(&attrsize));
isnull[i] = FALSE;
}
@@ -271,9 +272,10 @@ gistMakeUnionKey(GISTSTATE *giststate, int attno,
}
*dstisnull = FALSE;
- *dst = FunctionCall2(&giststate->unionFn[attno],
- PointerGetDatum(evec),
- PointerGetDatum(&dstsize));
+ *dst = FunctionCall2Coll(&giststate->unionFn[attno],
+ giststate->supportCollation[attno],
+ PointerGetDatum(evec),
+ PointerGetDatum(&dstsize));
}
}
@@ -282,9 +284,10 @@ gistKeyIsEQ(GISTSTATE *giststate, int attno, Datum a, Datum b)
{
bool result;
- FunctionCall3(&giststate->equalFn[attno],
- a, b,
- PointerGetDatum(&result));
+ FunctionCall3Coll(&giststate->equalFn[attno],
+ giststate->supportCollation[attno],
+ a, b,
+ PointerGetDatum(&result));
return result;
}
@@ -442,8 +445,9 @@ gistdentryinit(GISTSTATE *giststate, int nkey, GISTENTRY *e,
gistentryinit(*e, k, r, pg, o, l);
dep = (GISTENTRY *)
- DatumGetPointer(FunctionCall1(&giststate->decompressFn[nkey],
- PointerGetDatum(e)));
+ DatumGetPointer(FunctionCall1Coll(&giststate->decompressFn[nkey],
+ giststate->supportCollation[nkey],
+ PointerGetDatum(e)));
/* decompressFn may just return the given pointer */
if (dep != e)
gistentryinit(*e, dep->key, dep->rel, dep->page, dep->offset,
@@ -468,8 +472,9 @@ gistcentryinit(GISTSTATE *giststate, int nkey,
gistentryinit(*e, k, r, pg, o, l);
cep = (GISTENTRY *)
- DatumGetPointer(FunctionCall1(&giststate->compressFn[nkey],
- PointerGetDatum(e)));
+ DatumGetPointer(FunctionCall1Coll(&giststate->compressFn[nkey],
+ giststate->supportCollation[nkey],
+ PointerGetDatum(e)));
/* compressFn may just return the given pointer */
if (cep != e)
gistentryinit(*e, cep->key, cep->rel, cep->page, cep->offset,
@@ -519,11 +524,13 @@ gistpenalty(GISTSTATE *giststate, int attno,
{
float penalty = 0.0;
- if (giststate->penaltyFn[attno].fn_strict == FALSE || (isNullOrig == FALSE && isNullAdd == FALSE))
- FunctionCall3(&giststate->penaltyFn[attno],
- PointerGetDatum(orig),
- PointerGetDatum(add),
- PointerGetDatum(&penalty));
+ if (giststate->penaltyFn[attno].fn_strict == FALSE ||
+ (isNullOrig == FALSE && isNullAdd == FALSE))
+ FunctionCall3Coll(&giststate->penaltyFn[attno],
+ giststate->supportCollation[attno],
+ PointerGetDatum(orig),
+ PointerGetDatum(add),
+ PointerGetDatum(&penalty));
else if (isNullOrig && isNullAdd)
penalty = 0.0;
else
diff --git a/src/backend/access/hash/hashutil.c b/src/backend/access/hash/hashutil.c
index ac238d9f7d..6283f4a82b 100644
--- a/src/backend/access/hash/hashutil.c
+++ b/src/backend/access/hash/hashutil.c
@@ -80,11 +80,13 @@ uint32
_hash_datum2hashkey(Relation rel, Datum key)
{
FmgrInfo *procinfo;
+ Oid collation;
/* XXX assumes index has only one attribute */
procinfo = index_getprocinfo(rel, 1, HASHPROC);
+ collation = rel->rd_indcollation[0];
- return DatumGetUInt32(FunctionCall1(procinfo, key));
+ return DatumGetUInt32(FunctionCall1Coll(procinfo, collation, key));
}
/*
@@ -98,6 +100,7 @@ uint32
_hash_datum2hashkey_type(Relation rel, Datum key, Oid keytype)
{
RegProcedure hash_proc;
+ Oid collation;
/* XXX assumes index has only one attribute */
hash_proc = get_opfamily_proc(rel->rd_opfamily[0],
@@ -108,8 +111,9 @@ _hash_datum2hashkey_type(Relation rel, Datum key, Oid keytype)
elog(ERROR, "missing support function %d(%u,%u) for index \"%s\"",
HASHPROC, keytype, keytype,
RelationGetRelationName(rel));
+ collation = rel->rd_indcollation[0];
- return DatumGetUInt32(OidFunctionCall1(hash_proc, key));
+ return DatumGetUInt32(OidFunctionCall1Coll(hash_proc, collation, key));
}
/*
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 1fbd8b39b4..346d6b964d 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -2859,7 +2859,7 @@ l2:
* Any existing SIREAD locks on the old tuple must be linked to the new
* tuple for conflict detection purposes.
*/
- PredicateLockTupleRowVersionLink(relation, &oldtup, newtup);
+ PredicateLockTupleRowVersionLink(relation, &oldtup, heaptup);
if (newbuf != buffer)
LockBuffer(newbuf, BUFFER_LOCK_UNLOCK);
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index 75a137bbd7..a9d2428698 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -247,8 +247,7 @@ Boot_CreateStmt:
ONCOMMIT_NOOP,
(Datum) 0,
false,
- true,
- false);
+ true);
elog(DEBUG4, "relation created with oid %u", id);
}
do_end();
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index db58ec29f6..df32731b87 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -274,9 +274,6 @@ restrict_and_check_grant(bool is_grant, AclMode avail_goptions, bool all_privs,
case ACL_KIND_FOREIGN_SERVER:
whole_mask = ACL_ALL_RIGHTS_FOREIGN_SERVER;
break;
- case ACL_KIND_FOREIGN_TABLE:
- whole_mask = ACL_ALL_RIGHTS_FOREIGN_TABLE;
- break;
default:
elog(ERROR, "unrecognized object kind: %d", objkind);
/* not reached, but keep compiler quiet */
@@ -480,10 +477,6 @@ ExecuteGrantStmt(GrantStmt *stmt)
all_privileges = ACL_ALL_RIGHTS_FOREIGN_SERVER;
errormsg = gettext_noop("invalid privilege type %s for foreign server");
break;
- case ACL_OBJECT_FOREIGN_TABLE:
- all_privileges = ACL_ALL_RIGHTS_FOREIGN_TABLE;
- errormsg = gettext_noop("invalid privilege type %s for foreign table");
- break;
default:
elog(ERROR, "unrecognized GrantStmt.objtype: %d",
(int) stmt->objtype);
@@ -554,7 +547,6 @@ ExecGrantStmt_oids(InternalGrant *istmt)
{
case ACL_OBJECT_RELATION:
case ACL_OBJECT_SEQUENCE:
- case ACL_OBJECT_FOREIGN_TABLE:
ExecGrant_Relation(istmt);
break;
case ACL_OBJECT_DATABASE:
@@ -604,7 +596,6 @@ objectNamesToOids(GrantObjectType objtype, List *objnames)
{
case ACL_OBJECT_RELATION:
case ACL_OBJECT_SEQUENCE:
- case ACL_OBJECT_FOREIGN_TABLE:
foreach(cell, objnames)
{
RangeVar *relvar = (RangeVar *) lfirst(cell);
@@ -1702,21 +1693,11 @@ ExecGrant_Relation(InternalGrant *istmt)
errmsg("\"%s\" is not a sequence",
NameStr(pg_class_tuple->relname))));
- /* Used GRANT FOREIGN TABLE on a non-foreign-table? */
- if (istmt->objtype == ACL_OBJECT_FOREIGN_TABLE &&
- pg_class_tuple->relkind != RELKIND_FOREIGN_TABLE)
- ereport(ERROR,
- (errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a foreign table",
- NameStr(pg_class_tuple->relname))));
-
/* Adjust the default permissions based on object type */
if (istmt->all_privs && istmt->privileges == ACL_NO_RIGHTS)
{
if (pg_class_tuple->relkind == RELKIND_SEQUENCE)
this_privileges = ACL_ALL_RIGHTS_SEQUENCE;
- else if (pg_class_tuple->relkind == RELKIND_FOREIGN_TABLE)
- this_privileges = ACL_ALL_RIGHTS_FOREIGN_TABLE;
else
this_privileges = ACL_ALL_RIGHTS_RELATION;
}
@@ -1752,16 +1733,6 @@ ExecGrant_Relation(InternalGrant *istmt)
this_privileges &= (AclMode) ACL_ALL_RIGHTS_SEQUENCE;
}
}
- else if (pg_class_tuple->relkind == RELKIND_FOREIGN_TABLE)
- {
- if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_FOREIGN_TABLE))
- {
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_GRANT_OPERATION),
- errmsg("foreign table \"%s\" only supports SELECT privileges",
- NameStr(pg_class_tuple->relname))));
- }
- }
else
{
if (this_privileges & ~((AclMode) ACL_ALL_RIGHTS_RELATION))
@@ -1819,9 +1790,6 @@ ExecGrant_Relation(InternalGrant *istmt)
case RELKIND_SEQUENCE:
old_acl = acldefault(ACL_OBJECT_SEQUENCE, ownerId);
break;
- case RELKIND_FOREIGN_TABLE:
- old_acl = acldefault(ACL_OBJECT_FOREIGN_TABLE, ownerId);
- break;
default:
old_acl = acldefault(ACL_OBJECT_RELATION, ownerId);
break;
@@ -1866,9 +1834,6 @@ ExecGrant_Relation(InternalGrant *istmt)
case RELKIND_SEQUENCE:
aclkind = ACL_KIND_SEQUENCE;
break;
- case RELKIND_FOREIGN_TABLE:
- aclkind = ACL_KIND_FOREIGN_TABLE;
- break;
default:
aclkind = ACL_KIND_CLASS;
break;
@@ -1963,16 +1928,6 @@ ExecGrant_Relation(InternalGrant *istmt)
this_privileges &= (AclMode) ACL_SELECT;
}
- else if (pg_class_tuple->relkind == RELKIND_FOREIGN_TABLE &&
- this_privileges & ~((AclMode) ACL_SELECT))
- {
- /* Foreign tables have the same restriction as sequences. */
- ereport(WARNING,
- (errcode(ERRCODE_INVALID_GRANT_OPERATION),
- errmsg("foreign table \"%s\" only supports SELECT column privileges",
- NameStr(pg_class_tuple->relname))));
- this_privileges &= (AclMode) ACL_SELECT;
- }
expand_col_privileges(col_privs->cols, relOid,
this_privileges,
@@ -3147,8 +3102,6 @@ static const char *const no_priv_msg[MAX_ACL_KIND] =
gettext_noop("permission denied for foreign-data wrapper %s"),
/* ACL_KIND_FOREIGN_SERVER */
gettext_noop("permission denied for foreign server %s"),
- /* ACL_KIND_FOREIGN_TABLE */
- gettext_noop("permission denied for foreign table %s"),
/* ACL_KIND_EXTENSION */
gettext_noop("permission denied for extension %s"),
};
@@ -3193,8 +3146,6 @@ static const char *const not_owner_msg[MAX_ACL_KIND] =
gettext_noop("must be owner of foreign-data wrapper %s"),
/* ACL_KIND_FOREIGN_SERVER */
gettext_noop("must be owner of foreign server %s"),
- /* ACL_KIND_FOREIGN_TABLE */
- gettext_noop("must be owner of foreign table %s"),
/* ACL_KIND_EXTENSION */
gettext_noop("must be owner of extension %s"),
};
@@ -3491,9 +3442,6 @@ pg_class_aclmask(Oid table_oid, Oid roleid,
case RELKIND_SEQUENCE:
acl = acldefault(ACL_OBJECT_SEQUENCE, ownerId);
break;
- case RELKIND_FOREIGN_TABLE:
- acl = acldefault(ACL_OBJECT_FOREIGN_TABLE, ownerId);
- break;
default:
acl = acldefault(ACL_OBJECT_RELATION, ownerId);
break;
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index b3ed946530..c459c1e221 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -2227,14 +2227,16 @@ getObjectDescription(const ObjectAddress *object)
case OCLASS_COLLATION:
{
HeapTuple collTup;
+ Form_pg_collation coll;
collTup = SearchSysCache1(COLLOID,
ObjectIdGetDatum(object->objectId));
if (!HeapTupleIsValid(collTup))
elog(ERROR, "cache lookup failed for collation %u",
object->objectId);
+ coll = (Form_pg_collation) GETSTRUCT(collTup);
appendStringInfo(&buffer, _("collation %s"),
- NameStr(((Form_pg_collation) GETSTRUCT(collTup))->collname));
+ NameStr(coll->collname));
ReleaseSysCache(collTup);
break;
}
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index a63e0aa655..3183ff8fd5 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -644,7 +644,7 @@ AddNewAttributeTuples(Oid new_rel_oid,
/*
* First we add the user attributes. This is also a convenient place to
- * add dependencies on their datatypes.
+ * add dependencies on their datatypes and collations.
*/
for (i = 0; i < natts; i++)
{
@@ -666,7 +666,9 @@ AddNewAttributeTuples(Oid new_rel_oid,
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
- if (OidIsValid(attr->attcollation))
+ /* The default collation is pinned, so don't bother recording it */
+ if (OidIsValid(attr->attcollation) &&
+ attr->attcollation != DEFAULT_COLLATION_OID)
{
referenced.classId = CollationRelationId;
referenced.objectId = attr->attcollation;
@@ -921,7 +923,7 @@ AddNewRelationType(const char *typeName,
-1, /* typmod */
0, /* array dimensions for typBaseType */
false, /* Type NOT NULL */
- InvalidOid); /* typcollation */
+ InvalidOid); /* rowtypes never have a collation */
}
/* --------------------------------
@@ -971,8 +973,7 @@ heap_create_with_catalog(const char *relname,
OnCommitAction oncommit,
Datum reloptions,
bool use_user_acl,
- bool allow_system_table_mods,
- bool if_not_exists)
+ bool allow_system_table_mods)
{
Relation pg_class_desc;
Relation new_rel_desc;
@@ -992,26 +993,14 @@ heap_create_with_catalog(const char *relname,
CheckAttributeNamesTypes(tupdesc, relkind, allow_system_table_mods);
/*
- * If the relation already exists, it's an error, unless the user
- * specifies "IF NOT EXISTS". In that case, we just print a notice and do
- * nothing further.
+ * This would fail later on anyway, if the relation already exists. But
+ * by catching it here we can emit a nicer error message.
*/
existing_relid = get_relname_relid(relname, relnamespace);
if (existing_relid != InvalidOid)
- {
- if (if_not_exists)
- {
- ereport(NOTICE,
- (errcode(ERRCODE_DUPLICATE_TABLE),
- errmsg("relation \"%s\" already exists, skipping",
- relname)));
- heap_close(pg_class_desc, RowExclusiveLock);
- return InvalidOid;
- }
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_TABLE),
errmsg("relation \"%s\" already exists", relname)));
- }
/*
* Since we are going to create a rowtype as well, also check for
@@ -1051,7 +1040,8 @@ heap_create_with_catalog(const char *relname,
* Use binary-upgrade override for pg_class.oid/relfilenode, if
* supplied.
*/
- if (OidIsValid(binary_upgrade_next_heap_pg_class_oid) &&
+ if (IsBinaryUpgrade &&
+ OidIsValid(binary_upgrade_next_heap_pg_class_oid) &&
(relkind == RELKIND_RELATION || relkind == RELKIND_SEQUENCE ||
relkind == RELKIND_VIEW || relkind == RELKIND_COMPOSITE_TYPE ||
relkind == RELKIND_FOREIGN_TABLE))
@@ -1059,7 +1049,8 @@ heap_create_with_catalog(const char *relname,
relid = binary_upgrade_next_heap_pg_class_oid;
binary_upgrade_next_heap_pg_class_oid = InvalidOid;
}
- else if (OidIsValid(binary_upgrade_next_toast_pg_class_oid) &&
+ else if (IsBinaryUpgrade &&
+ OidIsValid(binary_upgrade_next_toast_pg_class_oid) &&
relkind == RELKIND_TOASTVALUE)
{
relid = binary_upgrade_next_toast_pg_class_oid;
@@ -1183,7 +1174,7 @@ heap_create_with_catalog(const char *relname,
-1, /* typmod */
0, /* array dimensions for typBaseType */
false, /* Type NOT NULL */
- InvalidOid); /* typcollation */
+ InvalidOid); /* rowtypes never have a collation */
pfree(relarrayname);
}
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index c79402c72a..a0898e0048 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -351,7 +351,6 @@ ConstructTupleDescriptor(Relation heapRelation,
to->atthasdef = false;
to->attislocal = true;
to->attinhcount = 0;
-
to->attcollation = collationObjectId[i];
}
else
@@ -388,7 +387,6 @@ ConstructTupleDescriptor(Relation heapRelation,
to->attcacheoff = -1;
to->atttypmod = -1;
to->attislocal = true;
-
to->attcollation = collationObjectId[i];
ReleaseSysCache(tuple);
@@ -653,6 +651,7 @@ UpdateIndexRelation(Oid indexoid,
* indexColNames: column names to use for index (List of char *)
* accessMethodObjectId: OID of index AM to use
* tableSpaceId: OID of tablespace to use
+ * collationObjectId: array of collation OIDs, one per index column
* classObjectId: array of index opclass OIDs, one per index column
* coloptions: array of per-index-column indoption settings
* reloptions: AM-specific options
@@ -790,7 +789,8 @@ index_create(Relation heapRelation,
* Use binary-upgrade override for pg_class.oid/relfilenode, if
* supplied.
*/
- if (OidIsValid(binary_upgrade_next_index_pg_class_oid))
+ if (IsBinaryUpgrade &&
+ OidIsValid(binary_upgrade_next_index_pg_class_oid))
{
indexRelationId = binary_upgrade_next_index_pg_class_oid;
binary_upgrade_next_index_pg_class_oid = InvalidOid;
@@ -871,7 +871,8 @@ index_create(Relation heapRelation,
* ----------------
*/
UpdateIndexRelation(indexRelationId, heapRelationId, indexInfo,
- collationObjectId, classObjectId, coloptions, isprimary, is_exclusion,
+ collationObjectId, classObjectId, coloptions,
+ isprimary, is_exclusion,
!deferrable,
!concurrent);
@@ -965,9 +966,11 @@ index_create(Relation heapRelation,
}
/* Store dependency on collations */
+ /* The default collation is pinned, so don't bother recording it */
for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
{
- if (OidIsValid(collationObjectId[i]))
+ if (OidIsValid(collationObjectId[i]) &&
+ collationObjectId[i] != DEFAULT_COLLATION_OID)
{
referenced.classId = CollationRelationId;
referenced.objectId = collationObjectId[i];
@@ -2445,8 +2448,8 @@ validate_index(Oid heapId, Oid indexId, Snapshot snapshot)
ivinfo.num_heap_tuples = heapRelation->rd_rel->reltuples;
ivinfo.strategy = NULL;
- state.tuplesort = tuplesort_begin_datum(TIDOID,
- TIDLessOperator, InvalidOid, false,
+ state.tuplesort = tuplesort_begin_datum(TIDOID, TIDLessOperator,
+ InvalidOid, false,
maintenance_work_mem,
false);
state.htups = state.itups = state.tups_inserted = 0;
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index f8fd827693..d803d28a06 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -361,6 +361,35 @@ RangeVarGetCreationNamespace(const RangeVar *newRelation)
}
/*
+ * RangeVarGetAndCheckCreationNamespace
+ * As RangeVarGetCreationNamespace, but with a permissions check.
+ */
+Oid
+RangeVarGetAndCheckCreationNamespace(const RangeVar *newRelation)
+{
+ Oid namespaceId;
+
+ namespaceId = RangeVarGetCreationNamespace(newRelation);
+
+ /*
+ * Check we have permission to create there. Skip check if bootstrapping,
+ * since permissions machinery may not be working yet.
+ */
+ if (!IsBootstrapProcessingMode())
+ {
+ AclResult aclresult;
+
+ aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(),
+ ACL_CREATE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+ get_namespace_name(namespaceId));
+ }
+
+ return namespaceId;
+}
+
+/*
* RelnameGetRelid
* Try to resolve an unqualified relation name.
* Returns OID if relation found in search path, else InvalidOid.
diff --git a/src/backend/catalog/pg_enum.c b/src/backend/catalog/pg_enum.c
index 08d8aa13f3..61a9322d90 100644
--- a/src/backend/catalog/pg_enum.c
+++ b/src/backend/catalog/pg_enum.c
@@ -21,6 +21,7 @@
#include "catalog/pg_enum.h"
#include "catalog/pg_type.h"
#include "storage/lmgr.h"
+#include "miscadmin.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/rel.h"
@@ -311,7 +312,7 @@ restart:
}
/* Get a new OID for the new label */
- if (OidIsValid(binary_upgrade_next_pg_enum_oid))
+ if (IsBinaryUpgrade && OidIsValid(binary_upgrade_next_pg_enum_oid))
{
/*
* Use binary-upgrade override for pg_enum.oid, if supplied. During
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 9e35e73f9c..de5c63defe 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -125,7 +125,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
tup = heap_form_tuple(tupDesc, values, nulls);
/* Use binary-upgrade override for pg_type.oid, if supplied. */
- if (OidIsValid(binary_upgrade_next_pg_type_oid))
+ if (IsBinaryUpgrade && OidIsValid(binary_upgrade_next_pg_type_oid))
{
HeapTupleSetOid(tup, binary_upgrade_next_pg_type_oid);
binary_upgrade_next_pg_type_oid = InvalidOid;
@@ -430,7 +430,7 @@ TypeCreate(Oid newTypeOid,
if (OidIsValid(newTypeOid))
HeapTupleSetOid(tup, newTypeOid);
/* Use binary-upgrade override for pg_type.oid, if supplied. */
- else if (OidIsValid(binary_upgrade_next_pg_type_oid))
+ else if (IsBinaryUpgrade && OidIsValid(binary_upgrade_next_pg_type_oid))
{
HeapTupleSetOid(tup, binary_upgrade_next_pg_type_oid);
binary_upgrade_next_pg_type_oid = InvalidOid;
@@ -643,8 +643,9 @@ GenerateTypeDependencies(Oid typeNamespace,
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
- /* Normal dependency from a domain to its base type's collation. */
- if (OidIsValid(typeCollation))
+ /* Normal dependency from a domain to its collation. */
+ /* We know the default collation is pinned, so don't bother recording it */
+ if (OidIsValid(typeCollation) && typeCollation != DEFAULT_COLLATION_OID)
{
referenced.classId = CollationRelationId;
referenced.objectId = typeCollation;
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 85fe57fb2a..a8cf0dbe2f 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -157,7 +157,8 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio
* creation even if it seems not to need one.
*/
if (!needs_toast_table(rel) &&
- !OidIsValid(binary_upgrade_next_toast_pg_class_oid))
+ (!IsBinaryUpgrade ||
+ !OidIsValid(binary_upgrade_next_toast_pg_class_oid)))
return false;
/*
@@ -202,7 +203,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio
namespaceid = PG_TOAST_NAMESPACE;
/* Use binary-upgrade override for pg_type.oid, if supplied. */
- if (OidIsValid(binary_upgrade_next_toast_pg_type_oid))
+ if (IsBinaryUpgrade && OidIsValid(binary_upgrade_next_toast_pg_type_oid))
{
toast_typid = binary_upgrade_next_toast_pg_type_oid;
binary_upgrade_next_toast_pg_type_oid = InvalidOid;
@@ -226,8 +227,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio
ONCOMMIT_NOOP,
reloptions,
false,
- true,
- false);
+ true);
Assert(toast_relid != InvalidOid);
/* make the toast relation visible, else heap_open will fail */
diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c
index ff228b7d53..191ef543cd 100644
--- a/src/backend/commands/cluster.c
+++ b/src/backend/commands/cluster.c
@@ -646,8 +646,7 @@ make_new_heap(Oid OIDOldHeap, Oid NewTableSpace)
ONCOMMIT_NOOP,
reloptions,
false,
- true,
- false);
+ true);
Assert(OIDNewHeap != InvalidOid);
ReleaseSysCache(tuple);
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 53a6aafbbf..ff84045d4f 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -350,7 +350,8 @@ DefineIndex(RangeVar *heapRelation,
collationObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
classObjectId = (Oid *) palloc(numberOfAttributes * sizeof(Oid));
coloptions = (int16 *) palloc(numberOfAttributes * sizeof(int16));
- ComputeIndexAttrs(indexInfo, collationObjectId, classObjectId, coloptions, attributeList,
+ ComputeIndexAttrs(indexInfo, collationObjectId, classObjectId,
+ coloptions, attributeList,
exclusionOpNames, relationId,
accessMethodName, accessMethodId,
amcanorder, isconstraint);
@@ -395,7 +396,8 @@ DefineIndex(RangeVar *heapRelation,
indexRelationId =
index_create(rel, indexRelationName, indexRelationId,
indexInfo, indexColNames,
- accessMethodId, tablespaceId, collationObjectId, classObjectId,
+ accessMethodId, tablespaceId,
+ collationObjectId, classObjectId,
coloptions, reloptions, primary,
isconstraint, deferrable, initdeferred,
allowSystemTableMods,
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index bc9772708c..1f8f1ebd4f 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -318,7 +318,8 @@ static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recu
static void ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
ColumnDef *colDef, bool isOid,
bool recurse, bool recursing, LOCKMODE lockmode);
-static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid, Oid collid);
+static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
+static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
static void ATPrepAddOids(List **wqueue, Relation rel, bool recurse,
AlterTableCmd *cmd, LOCKMODE lockmode);
static void ATExecDropNotNull(Relation rel, const char *colName, DropBehavior behavior,
@@ -482,22 +483,10 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
errmsg("cannot create temporary table within security-restricted operation")));
/*
- * Look up the namespace in which we are supposed to create the relation.
- * Check we have permission to create there. Skip check if bootstrapping,
- * since permissions machinery may not be working yet.
+ * Look up the namespace in which we are supposed to create the relation,
+ * and check we have permission to create there.
*/
- namespaceId = RangeVarGetCreationNamespace(stmt->relation);
-
- if (!IsBootstrapProcessingMode())
- {
- AclResult aclresult;
-
- aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(),
- ACL_CREATE);
- if (aclresult != ACLCHECK_OK)
- aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
- get_namespace_name(namespaceId));
- }
+ namespaceId = RangeVarGetAndCheckCreationNamespace(stmt->relation);
/*
* Select tablespace to use. If not specified, use default tablespace
@@ -705,16 +694,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId)
stmt->oncommit,
reloptions,
true,
- allowSystemTableMods,
- stmt->if_not_exists);
-
- /*
- * If heap_create_with_catalog returns InvalidOid, it means that the user
- * specified "IF NOT EXISTS" and the relation already exists. In that
- * case we do nothing further.
- */
- if (relationId == InvalidOid)
- return InvalidOid;
+ allowSystemTableMods);
/* Store inheritance information for new rel. */
StoreCatalogInheritance(relationId, inheritOids);
@@ -2601,8 +2581,7 @@ RenameRelation(Oid myrelid, const char *newrelname, ObjectType reltype)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a foreign table",
- RelationGetRelationName(targetrelation)),
- errhint("Use ALTER FOREIGN TABLE instead.")));
+ RelationGetRelationName(targetrelation))));
/*
* Don't allow ALTER TABLE on composite types. We want people to use ALTER
@@ -4855,7 +4834,8 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
/*
* Add needed dependency entries for the new column.
*/
- add_column_datatype_dependency(myrelid, newattnum, attribute.atttypid, attribute.attcollation);
+ add_column_datatype_dependency(myrelid, newattnum, attribute.atttypid);
+ add_column_collation_dependency(myrelid, newattnum, attribute.attcollation);
/*
* Propagate to children as appropriate. Unlike most other ALTER
@@ -4906,7 +4886,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
* Install a column's dependency on its datatype.
*/
static void
-add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid, Oid collid)
+add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
{
ObjectAddress myself,
referenced;
@@ -4918,9 +4898,23 @@ add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid, Oid collid)
referenced.objectId = typid;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+}
+
+/*
+ * Install a column's dependency on its collation.
+ */
+static void
+add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
+{
+ ObjectAddress myself,
+ referenced;
- if (collid)
+ /* We know the default collation is pinned, so don't bother recording it */
+ if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
{
+ myself.classId = RelationRelationId;
+ myself.objectId = relid;
+ myself.objectSubId = attnum;
referenced.classId = CollationRelationId;
referenced.objectId = collid;
referenced.objectSubId = 0;
@@ -7690,7 +7684,8 @@ ATPrepAlterColumnType(List **wqueue,
else
{
transform = (Node *) makeVar(1, attnum,
- attTup->atttypid, attTup->atttypmod, attTup->attcollation,
+ attTup->atttypid, attTup->atttypmod,
+ attTup->attcollation,
0);
}
@@ -8071,7 +8066,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, and possibly an associated collation.
+ * want to remove, and possibly a collation dependency.
*/
ScanKeyInit(&key[0],
Anum_pg_depend_classid,
@@ -8110,8 +8105,8 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
heap_close(depRel, RowExclusiveLock);
/*
- * Here we go --- change the recorded column type. (Note heapTup is a
- * copy of the syscache entry, so okay to scribble on.)
+ * Here we go --- change the recorded column type and collation. (Note
+ * heapTup is a copy of the syscache entry, so okay to scribble on.)
*/
attTup->atttypid = targettype;
attTup->atttypmod = targettypmod;
@@ -8131,8 +8126,9 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
heap_close(attrelation, RowExclusiveLock);
- /* Install dependency on new datatype */
- add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype, targetcollid);
+ /* Install dependencies on new datatype and collation */
+ add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
+ add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
/*
* Drop any pg_statistic entry for the column, since it's now wrong type
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index c0a6eead77..1cd386d25f 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -575,7 +575,7 @@ DefineType(List *names, List *parameters)
-1, /* typMod (Domains only) */
0, /* Array Dimensions of typbasetype */
false, /* Type NOT NULL */
- collation);
+ collation); /* type's collation */
/*
* Create the array type that goes with it.
@@ -615,7 +615,7 @@ DefineType(List *names, List *parameters)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- collation);
+ collation); /* type's collation */
pfree(array_type);
}
@@ -1088,7 +1088,7 @@ DefineDomain(CreateDomainStmt *stmt)
basetypeMod, /* typeMod value */
typNDims, /* Array dimensions for base type */
typNotNull, /* Type NOT NULL */
- domaincoll);
+ domaincoll); /* type's collation */
/*
* Process constraints which refer to the domain ID returned by TypeCreate
@@ -1202,7 +1202,7 @@ DefineEnum(CreateEnumStmt *stmt)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- InvalidOid); /* typcollation */
+ InvalidOid); /* type's collation */
/* Enter the enum's values into pg_enum */
EnumValuesCreate(enumTypeOid, stmt->vals);
@@ -1242,7 +1242,7 @@ DefineEnum(CreateEnumStmt *stmt)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- InvalidOid); /* typcollation */
+ InvalidOid); /* type's collation */
pfree(enumArrayName);
}
@@ -1573,7 +1573,7 @@ AssignTypeArrayOid(void)
Oid type_array_oid;
/* Use binary-upgrade override for pg_type.typarray, if supplied. */
- if (OidIsValid(binary_upgrade_next_array_pg_type_oid))
+ if (IsBinaryUpgrade && OidIsValid(binary_upgrade_next_array_pg_type_oid))
{
type_array_oid = binary_upgrade_next_array_pg_type_oid;
binary_upgrade_next_array_pg_type_oid = InvalidOid;
diff --git a/src/backend/commands/user.c b/src/backend/commands/user.c
index 3f7d4992af..838d6eba20 100644
--- a/src/backend/commands/user.c
+++ b/src/backend/commands/user.c
@@ -388,7 +388,7 @@ CreateRole(CreateRoleStmt *stmt)
* pg_largeobject_metadata contains pg_authid.oid's, so we use the
* binary-upgrade override, if specified.
*/
- if (OidIsValid(binary_upgrade_next_pg_authid_oid))
+ if (IsBinaryUpgrade && OidIsValid(binary_upgrade_next_pg_authid_oid))
{
HeapTupleSetOid(tuple, binary_upgrade_next_pg_authid_oid);
binary_upgrade_next_pg_authid_oid = InvalidOid;
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 86ec987019..620efda838 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -2341,7 +2341,6 @@ OpenIntoRel(QueryDesc *queryDesc)
Oid namespaceId;
Oid tablespaceId;
Datum reloptions;
- AclResult aclresult;
Oid intoRelationId;
TupleDesc tupdesc;
DR_intorel *myState;
@@ -2378,13 +2377,7 @@ OpenIntoRel(QueryDesc *queryDesc)
* Find namespace to create in, check its permissions
*/
intoName = into->rel->relname;
- namespaceId = RangeVarGetCreationNamespace(into->rel);
-
- aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(),
- ACL_CREATE);
- if (aclresult != ACLCHECK_OK)
- aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
- get_namespace_name(namespaceId));
+ namespaceId = RangeVarGetAndCheckCreationNamespace(into->rel);
/*
* Select tablespace to use. If not specified, use default tablespace
@@ -2444,8 +2437,7 @@ OpenIntoRel(QueryDesc *queryDesc)
into->onCommit,
reloptions,
true,
- allowSystemTableMods,
- false);
+ allowSystemTableMods);
Assert(intoRelationId != InvalidOid);
FreeTupleDesc(tupdesc);
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 208c0fb76a..7d27123cf0 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -170,13 +170,13 @@ typedef enum
* the two expressions from the original clause.
*
* In addition to the expressions themselves, the planner passes the btree
- * opfamily OID, btree strategy number (BTLessStrategyNumber or
+ * opfamily OID, collation OID, btree strategy number (BTLessStrategyNumber or
* BTGreaterStrategyNumber), and nulls-first flag that identify the intended
* sort ordering for each merge key. The mergejoinable operator is an
- * equality operator in this opfamily, and the two inputs are guaranteed to be
+ * equality operator in the opfamily, and the two inputs are guaranteed to be
* ordered in either increasing or decreasing (respectively) order according
- * to this opfamily, with nulls at the indicated end of the range. This
- * allows us to obtain the needed comparison function from the opfamily.
+ * to the opfamily and collation, with nulls at the indicated end of the range.
+ * This allows us to obtain the needed comparison function from the opfamily.
*/
static MergeJoinClause
MJExamineQuals(List *mergeclauses,
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index f25505feb3..c17863fce5 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -28,6 +28,7 @@
#include "catalog/pg_collation.h"
#include "libpq/ip.h"
#include "libpq/libpq.h"
+#include "postmaster/postmaster.h"
#include "regex/regex.h"
#include "replication/walsender.h"
#include "storage/fd.h"
@@ -832,12 +833,24 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
if (token[4] == 's') /* "hostssl" */
{
+ /* SSL support must be actually active, else complain */
#ifdef USE_SSL
- parsedline->conntype = ctHostSSL;
+ if (EnableSSL)
+ parsedline->conntype = ctHostSSL;
+ else
+ {
+ ereport(LOG,
+ (errcode(ERRCODE_CONFIG_FILE_ERROR),
+ errmsg("hostssl requires SSL to be turned on"),
+ errhint("Set ssl = on in postgresql.conf."),
+ errcontext("line %d of configuration file \"%s\"",
+ line_num, HbaFileName)));
+ return false;
+ }
#else
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("hostssl not supported on this platform"),
+ errmsg("hostssl is not supported by this build"),
errhint("Compile with --with-openssl to use SSL connections."),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
@@ -1135,7 +1148,7 @@ parse_hba_line(List *line, int line_num, HbaLine *parsedline)
{
ereport(LOG,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("invalid authentication method \"%s\": not supported on this platform",
+ errmsg("invalid authentication method \"%s\": not supported by this build",
token),
errcontext("line %d of configuration file \"%s\"",
line_num, HbaFileName)));
diff --git a/src/backend/main/main.c b/src/backend/main/main.c
index c4ef56dc6c..51959371ce 100644
--- a/src/backend/main/main.c
+++ b/src/backend/main/main.c
@@ -392,7 +392,7 @@ get_current_username(const char *progname)
/* Allocate new memory because later getpwuid() calls can overwrite it. */
return strdup(pw->pw_name);
#else
- long namesize = 256 /* UNLEN */ + 1;
+ unsigned long namesize = 256 /* UNLEN */ + 1;
char *name;
name = malloc(namesize);
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index d345522866..c4404b1bd1 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -1335,25 +1335,40 @@ cost_material(Path *path,
* Determines and returns the cost of performing an Agg plan node,
* including the cost of its input.
*
+ * aggcosts can be NULL when there are no actual aggregate functions (i.e.,
+ * we are using a hashed Agg node just to do grouping).
+ *
* Note: when aggstrategy == AGG_SORTED, caller must ensure that input costs
* are for appropriately-sorted input.
*/
void
cost_agg(Path *path, PlannerInfo *root,
- AggStrategy aggstrategy, int numAggs,
+ AggStrategy aggstrategy, const AggClauseCosts *aggcosts,
int numGroupCols, double numGroups,
Cost input_startup_cost, Cost input_total_cost,
double input_tuples)
{
Cost startup_cost;
Cost total_cost;
+ AggClauseCosts dummy_aggcosts;
+
+ /* Use all-zero per-aggregate costs if NULL is passed */
+ if (aggcosts == NULL)
+ {
+ Assert(aggstrategy == AGG_HASHED);
+ MemSet(&dummy_aggcosts, 0, sizeof(AggClauseCosts));
+ aggcosts = &dummy_aggcosts;
+ }
/*
- * We charge one cpu_operator_cost per aggregate function per input tuple,
- * and another one per output tuple (corresponding to transfn and finalfn
- * calls respectively). If we are grouping, we charge an additional
- * cpu_operator_cost per grouping column per input tuple for grouping
- * comparisons.
+ * The transCost.per_tuple component of aggcosts should be charged once
+ * per input tuple, corresponding to the costs of evaluating the aggregate
+ * transfns and their input expressions (with any startup cost of course
+ * charged but once). The finalCost component is charged once per output
+ * tuple, corresponding to the costs of evaluating the finalfns.
+ *
+ * If we are grouping, we charge an additional cpu_operator_cost per
+ * grouping column per input tuple for grouping comparisons.
*
* We will produce a single output tuple if not grouping, and a tuple per
* group otherwise. We charge cpu_tuple_cost for each output tuple.
@@ -1366,15 +1381,13 @@ cost_agg(Path *path, PlannerInfo *root,
* there's roundoff error we might do the wrong thing. So be sure that
* the computations below form the same intermediate values in the same
* order.
- *
- * Note: ideally we should use the pg_proc.procost costs of each
- * aggregate's component functions, but for now that seems like an
- * excessive amount of work.
*/
if (aggstrategy == AGG_PLAIN)
{
startup_cost = input_total_cost;
- startup_cost += cpu_operator_cost * (input_tuples + 1) * numAggs;
+ startup_cost += aggcosts->transCost.startup;
+ startup_cost += aggcosts->transCost.per_tuple * input_tuples;
+ startup_cost += aggcosts->finalCost;
/* we aren't grouping */
total_cost = startup_cost + cpu_tuple_cost;
}
@@ -1384,19 +1397,21 @@ cost_agg(Path *path, PlannerInfo *root,
startup_cost = input_startup_cost;
total_cost = input_total_cost;
/* calcs phrased this way to match HASHED case, see note above */
- total_cost += cpu_operator_cost * input_tuples * numGroupCols;
- total_cost += cpu_operator_cost * input_tuples * numAggs;
- total_cost += cpu_operator_cost * numGroups * numAggs;
+ total_cost += aggcosts->transCost.startup;
+ total_cost += aggcosts->transCost.per_tuple * input_tuples;
+ total_cost += (cpu_operator_cost * numGroupCols) * input_tuples;
+ total_cost += aggcosts->finalCost * numGroups;
total_cost += cpu_tuple_cost * numGroups;
}
else
{
/* must be AGG_HASHED */
startup_cost = input_total_cost;
- startup_cost += cpu_operator_cost * input_tuples * numGroupCols;
- startup_cost += cpu_operator_cost * input_tuples * numAggs;
+ startup_cost += aggcosts->transCost.startup;
+ startup_cost += aggcosts->transCost.per_tuple * input_tuples;
+ startup_cost += (cpu_operator_cost * numGroupCols) * input_tuples;
total_cost = startup_cost;
- total_cost += cpu_operator_cost * numGroups * numAggs;
+ total_cost += aggcosts->finalCost * numGroups;
total_cost += cpu_tuple_cost * numGroups;
}
@@ -1413,25 +1428,53 @@ cost_agg(Path *path, PlannerInfo *root,
*/
void
cost_windowagg(Path *path, PlannerInfo *root,
- int numWindowFuncs, int numPartCols, int numOrderCols,
+ List *windowFuncs, int numPartCols, int numOrderCols,
Cost input_startup_cost, Cost input_total_cost,
double input_tuples)
{
Cost startup_cost;
Cost total_cost;
+ ListCell *lc;
startup_cost = input_startup_cost;
total_cost = input_total_cost;
/*
- * We charge one cpu_operator_cost per window function per tuple (often a
- * drastic underestimate, but without a way to gauge how many tuples the
- * window function will fetch, it's hard to do better). We also charge
- * cpu_operator_cost per grouping column per tuple for grouping
- * comparisons, plus cpu_tuple_cost per tuple for general overhead.
- */
- total_cost += cpu_operator_cost * input_tuples * numWindowFuncs;
- total_cost += cpu_operator_cost * input_tuples * (numPartCols + numOrderCols);
+ * Window functions are assumed to cost their stated execution cost, plus
+ * the cost of evaluating their input expressions, per tuple. Since they
+ * may in fact evaluate their inputs at multiple rows during each cycle,
+ * this could be a drastic underestimate; but without a way to know how
+ * many rows the window function will fetch, it's hard to do better. In
+ * any case, it's a good estimate for all the built-in window functions,
+ * so we'll just do this for now.
+ */
+ foreach(lc, windowFuncs)
+ {
+ WindowFunc *wfunc = (WindowFunc *) lfirst(lc);
+ Cost wfunccost;
+ QualCost argcosts;
+
+ Assert(IsA(wfunc, WindowFunc));
+
+ wfunccost = get_func_cost(wfunc->winfnoid) * cpu_operator_cost;
+
+ /* also add the input expressions' cost to per-input-row costs */
+ cost_qual_eval_node(&argcosts, (Node *) wfunc->args, root);
+ startup_cost += argcosts.startup;
+ wfunccost += argcosts.per_tuple;
+
+ total_cost += wfunccost * input_tuples;
+ }
+
+ /*
+ * We also charge cpu_operator_cost per grouping column per tuple for
+ * grouping comparisons, plus cpu_tuple_cost per tuple for general
+ * overhead.
+ *
+ * XXX this neglects costs of spooling the data to disk when it overflows
+ * work_mem. Sooner or later that should get accounted for.
+ */
+ total_cost += cpu_operator_cost * (numPartCols + numOrderCols) * input_tuples;
total_cost += cpu_tuple_cost * input_tuples;
path->startup_cost = startup_cost;
@@ -2640,17 +2683,12 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
* Vars and Consts are charged zero, and so are boolean operators (AND,
* OR, NOT). Simplistic, but a lot better than no model at all.
*
- * Note that Aggref and WindowFunc nodes are (and should be) treated like
- * Vars --- whatever execution cost they have is absorbed into
- * plan-node-specific costing. As far as expression evaluation is
- * concerned they're just like Vars.
- *
* Should we try to account for the possibility of short-circuit
* evaluation of AND/OR? Probably *not*, because that would make the
* results depend on the clause ordering, and we are not in any position
* to expect that the current ordering of the clauses is the one that's
- * going to end up being used. (Is it worth applying order_qual_clauses
- * much earlier in the planning process to fix this?)
+ * going to end up being used. The above per-RestrictInfo caching would
+ * not mix well with trying to re-order clauses anyway.
*/
if (IsA(node, FuncExpr))
{
@@ -2679,6 +2717,20 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
context->total.per_tuple += get_func_cost(saop->opfuncid) *
cpu_operator_cost * estimate_array_length(arraynode) * 0.5;
}
+ else if (IsA(node, Aggref) ||
+ IsA(node, WindowFunc))
+ {
+ /*
+ * Aggref and WindowFunc nodes are (and should be) treated like Vars,
+ * ie, zero execution cost in the current model, because they behave
+ * essentially like Vars in execQual.c. We disregard the costs of
+ * their input expressions for the same reason. The actual execution
+ * costs of the aggregate/window functions and their arguments have to
+ * be factored into plan-node-specific costing of the Agg or WindowAgg
+ * plan node.
+ */
+ return false; /* don't recurse into children */
+ }
else if (IsA(node, CoerceViaIO))
{
CoerceViaIO *iocoerce = (CoerceViaIO *) node;
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 1a9540ce06..ac80511478 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -108,7 +108,8 @@ 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 *funccolcollations);
+ 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,
@@ -143,9 +144,9 @@ static MergeJoin *make_mergejoin(List *tlist,
bool *mergenullsfirst,
Plan *lefttree, Plan *righttree,
JoinType jointype);
-static Sort *
-make_sort(PlannerInfo *root, Plan *lefttree, int numCols,
-AttrNumber *sortColIdx, Oid *sortOperators, Oid *collations, bool *nullsFirst,
+static Sort *make_sort(PlannerInfo *root, Plan *lefttree, int numCols,
+ AttrNumber *sortColIdx, Oid *sortOperators,
+ Oid *collations, bool *nullsFirst,
double limit_tuples);
static Plan *prepare_sort_from_pathkeys(PlannerInfo *root,
Plan *lefttree, List *pathkeys,
@@ -738,7 +739,8 @@ create_merge_append_plan(PlannerInfo *root, MergeAppendPath *best_path)
/* 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, collations, nullsFirst,
+ sortColIdx, sortOperators,
+ collations, nullsFirst,
best_path->limit_tuples);
subplans = lappend(subplans, subplan);
@@ -937,11 +939,11 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path)
build_relation_tlist(best_path->path.parent),
NIL,
AGG_HASHED,
+ NULL,
numGroupCols,
groupColIdx,
groupOperators,
numGroups,
- 0,
subplan);
}
else
@@ -2013,10 +2015,10 @@ create_mergejoin_plan(PlannerInfo *root,
}
/*
- * Compute the opfamily/strategy/nullsfirst arrays needed by the executor.
- * The information is in the pathkeys for the two inputs, but we need to
- * be careful about the possibility of mergeclauses sharing a pathkey
- * (compare find_mergeclauses_for_pathkeys()).
+ * Compute the opfamily/collation/strategy/nullsfirst arrays needed by the
+ * executor. The information is in the pathkeys for the two inputs, but
+ * we need to be careful about the possibility of mergeclauses sharing a
+ * pathkey (compare find_mergeclauses_for_pathkeys()).
*/
nClauses = list_length(mergeclauses);
Assert(nClauses == list_length(best_path->path_mergeclauses));
@@ -3316,13 +3318,14 @@ make_mergejoin(List *tlist,
/*
* make_sort --- basic routine to build a Sort plan node
*
- * Caller must have built the sortColIdx, sortOperators, and nullsFirst
- * arrays already. limit_tuples is as for cost_sort (in particular, pass
- * -1 if no limit)
+ * Caller must have built the sortColIdx, sortOperators, collations, and
+ * nullsFirst arrays already.
+ * limit_tuples is as for cost_sort (in particular, pass -1 if no limit)
*/
static Sort *
make_sort(PlannerInfo *root, Plan *lefttree, int numCols,
-AttrNumber *sortColIdx, Oid *sortOperators, Oid *collations, bool *nullsFirst,
+ AttrNumber *sortColIdx, Oid *sortOperators,
+ Oid *collations, bool *nullsFirst,
double limit_tuples)
{
Sort *node = makeNode(Sort);
@@ -3378,6 +3381,11 @@ add_sort_column(AttrNumber colIdx, Oid sortOp, Oid coll, bool nulls_first,
* values that < considers equal. We need not check nulls_first
* however because a lower-order column with the same sortop but
* opposite nulls direction is redundant.
+ *
+ * We could probably consider sort keys with the same sortop and
+ * different collations to be redundant too, but for the moment
+ * treat them as not redundant. This will be needed if we ever
+ * support collations with different notions of equality.
*/
if (sortColIdx[i] == colIdx &&
sortOperators[numCols] == sortOp &&
@@ -3410,8 +3418,9 @@ add_sort_column(AttrNumber colIdx, Oid sortOp, Oid coll, bool nulls_first,
* 'adjust_tlist_in_place' is TRUE if lefttree must be modified in-place
*
* We must convert the pathkey information into arrays of sort key column
- * numbers and sort operator OIDs, which is the representation the executor
- * wants. These are returned into the output parameters *p_numsortkeys etc.
+ * numbers, sort operator OIDs, collation OIDs, and nulls-first flags,
+ * which is the representation the executor wants. These are returned into
+ * the output parameters *p_numsortkeys etc.
*
* If the pathkeys include expressions that aren't simple Vars, we will
* usually need to add resjunk items to the input plan's targetlist to
@@ -3610,7 +3619,8 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
pathkey->pk_eclass->ec_collation,
pathkey->pk_nulls_first,
numsortkeys,
- sortColIdx, sortOperators, collations, nullsFirst);
+ sortColIdx, sortOperators,
+ collations, nullsFirst);
}
Assert(numsortkeys > 0);
@@ -3655,7 +3665,8 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
/* Now build the Sort node */
return make_sort(root, lefttree, numsortkeys,
- sortColIdx, sortOperators, collations, nullsFirst, limit_tuples);
+ sortColIdx, sortOperators, collations,
+ nullsFirst, limit_tuples);
}
/*
@@ -3701,13 +3712,15 @@ make_sort_from_sortclauses(PlannerInfo *root, List *sortcls, Plan *lefttree)
exprCollation((Node *) tle->expr),
sortcl->nulls_first,
numsortkeys,
- sortColIdx, sortOperators, collations, nullsFirst);
+ sortColIdx, sortOperators,
+ collations, nullsFirst);
}
Assert(numsortkeys > 0);
return make_sort(root, lefttree, numsortkeys,
- sortColIdx, sortOperators, collations, nullsFirst, -1.0);
+ sortColIdx, sortOperators, collations,
+ nullsFirst, -1.0);
}
/*
@@ -3763,14 +3776,16 @@ make_sort_from_groupcols(PlannerInfo *root,
exprCollation((Node *) tle->expr),
grpcl->nulls_first,
numsortkeys,
- sortColIdx, sortOperators, collations, nullsFirst);
+ sortColIdx, sortOperators,
+ collations, nullsFirst);
grpno++;
}
Assert(numsortkeys > 0);
return make_sort(root, lefttree, numsortkeys,
- sortColIdx, sortOperators, collations, nullsFirst, -1.0);
+ sortColIdx, sortOperators, collations,
+ nullsFirst, -1.0);
}
static Material *
@@ -3826,9 +3841,9 @@ materialize_finished_plan(Plan *subplan)
Agg *
make_agg(PlannerInfo *root, List *tlist, List *qual,
- AggStrategy aggstrategy,
+ AggStrategy aggstrategy, const AggClauseCosts *aggcosts,
int numGroupCols, AttrNumber *grpColIdx, Oid *grpOperators,
- long numGroups, int numAggs,
+ long numGroups,
Plan *lefttree)
{
Agg *node = makeNode(Agg);
@@ -3844,7 +3859,7 @@ make_agg(PlannerInfo *root, List *tlist, List *qual,
copy_plan_costsize(plan, lefttree); /* only care about copying size */
cost_agg(&agg_path, root,
- aggstrategy, numAggs,
+ aggstrategy, aggcosts,
numGroupCols, numGroups,
lefttree->startup_cost,
lefttree->total_cost,
@@ -3892,7 +3907,7 @@ make_agg(PlannerInfo *root, List *tlist, List *qual,
WindowAgg *
make_windowagg(PlannerInfo *root, List *tlist,
- int numWindowFuncs, Index winref,
+ List *windowFuncs, Index winref,
int partNumCols, AttrNumber *partColIdx, Oid *partOperators,
int ordNumCols, AttrNumber *ordColIdx, Oid *ordOperators,
int frameOptions, Node *startOffset, Node *endOffset,
@@ -3916,7 +3931,7 @@ make_windowagg(PlannerInfo *root, List *tlist,
copy_plan_costsize(plan, lefttree); /* only care about copying size */
cost_windowagg(&windowagg_path, root,
- numWindowFuncs, partNumCols, ordNumCols,
+ windowFuncs, partNumCols, ordNumCols,
lefttree->startup_cost,
lefttree->total_cost,
lefttree->plan_rows);
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index 7fce92c2f1..2f5955706a 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -187,11 +187,13 @@ preprocess_minmax_aggregates(PlannerInfo *root, List *tlist)
* Should we skip even trying to build the standard plan, if
* preprocess_minmax_aggregates succeeds?
*
- * We are passed the preprocessed tlist, as well as the best path devised for
+ * We are passed the preprocessed tlist, as well as the estimated costs for
+ * doing the aggregates the regular way, and the best path devised for
* computing the input of a standard Agg node.
*/
Plan *
-optimize_minmax_aggregates(PlannerInfo *root, List *tlist, Path *best_path)
+optimize_minmax_aggregates(PlannerInfo *root, List *tlist,
+ const AggClauseCosts *aggcosts, Path *best_path)
{
Query *parse = root->parse;
Cost total_cost;
@@ -221,7 +223,7 @@ optimize_minmax_aggregates(PlannerInfo *root, List *tlist, Path *best_path)
total_cost += mminfo->pathcost;
}
- cost_agg(&agg_p, root, AGG_PLAIN, list_length(root->minmax_aggs),
+ cost_agg(&agg_p, root, AGG_PLAIN, aggcosts,
0, 0,
best_path->startup_cost, best_path->total_cost,
best_path->parent->rows);
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 4b0b633c03..7b2b40f629 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -74,7 +74,7 @@ static bool choose_hashed_grouping(PlannerInfo *root,
double tuple_fraction, double limit_tuples,
double path_rows, int path_width,
Path *cheapest_path, Path *sorted_path,
- double dNumGroups, AggClauseCounts *agg_counts);
+ double dNumGroups, AggClauseCosts *agg_costs);
static bool choose_hashed_distinct(PlannerInfo *root,
double tuple_fraction, double limit_tuples,
double path_rows, int path_width,
@@ -979,7 +979,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
Path *sorted_path;
Path *best_path;
long numGroups = 0;
- AggClauseCounts agg_counts;
+ AggClauseCosts agg_costs;
int numGroupCols;
double path_rows;
int path_width;
@@ -987,7 +987,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
WindowFuncLists *wflists = NULL;
List *activeWindows = NIL;
- MemSet(&agg_counts, 0, sizeof(AggClauseCounts));
+ MemSet(&agg_costs, 0, sizeof(AggClauseCosts));
/* A recursive query should always have setOperations */
Assert(!root->hasRecursion);
@@ -1034,12 +1034,12 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
if (parse->hasAggs)
{
/*
- * Will need actual number of aggregates for estimating costs.
+ * Collect statistics about aggregates for estimating costs.
* Note: we do not attempt to detect duplicate aggregates here; a
- * somewhat-overestimated count is okay for our present purposes.
+ * somewhat-overestimated cost is okay for our present purposes.
*/
- count_agg_clauses((Node *) tlist, &agg_counts);
- count_agg_clauses(parse->havingQual, &agg_counts);
+ count_agg_clauses(root, (Node *) tlist, &agg_costs);
+ count_agg_clauses(root, parse->havingQual, &agg_costs);
/*
* Preprocess MIN/MAX aggregates, if any. Note: be careful about
@@ -1176,7 +1176,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
tuple_fraction, limit_tuples,
path_rows, path_width,
cheapest_path, sorted_path,
- dNumGroups, &agg_counts);
+ dNumGroups, &agg_costs);
/* Also convert # groups to long int --- but 'ware overflow! */
numGroups = (long) Min(dNumGroups, (double) LONG_MAX);
}
@@ -1219,6 +1219,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
*/
result_plan = optimize_minmax_aggregates(root,
tlist,
+ &agg_costs,
best_path);
if (result_plan != NULL)
{
@@ -1330,11 +1331,11 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
tlist,
(List *) parse->havingQual,
AGG_HASHED,
+ &agg_costs,
numGroupCols,
groupColIdx,
extract_grouping_ops(parse->groupClause),
numGroups,
- agg_counts.numAggs,
result_plan);
/* Hashed aggregation produces randomly-ordered results */
current_pathkeys = NIL;
@@ -1373,11 +1374,11 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
tlist,
(List *) parse->havingQual,
aggstrategy,
+ &agg_costs,
numGroupCols,
groupColIdx,
extract_grouping_ops(parse->groupClause),
numGroups,
- agg_counts.numAggs,
result_plan);
}
else if (parse->groupClause)
@@ -1559,7 +1560,7 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
result_plan = (Plan *)
make_windowagg(root,
(List *) copyObject(window_tlist),
- list_length(wflists->windowFuncs[wc->winref]),
+ wflists->windowFuncs[wc->winref],
wc->winref,
partNumCols,
partColIdx,
@@ -1625,12 +1626,12 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
result_plan->targetlist,
NIL,
AGG_HASHED,
+ NULL,
list_length(parse->distinctClause),
extract_grouping_cols(parse->distinctClause,
result_plan->targetlist),
extract_grouping_ops(parse->distinctClause),
numDistinctRows,
- 0,
result_plan);
/* Hashed aggregation produces randomly-ordered results */
current_pathkeys = NIL;
@@ -2213,7 +2214,7 @@ choose_hashed_grouping(PlannerInfo *root,
double tuple_fraction, double limit_tuples,
double path_rows, int path_width,
Path *cheapest_path, Path *sorted_path,
- double dNumGroups, AggClauseCounts *agg_counts)
+ double dNumGroups, AggClauseCosts *agg_costs)
{
Query *parse = root->parse;
int numGroupCols = list_length(parse->groupClause);
@@ -2231,7 +2232,7 @@ choose_hashed_grouping(PlannerInfo *root,
* the hash table, and/or running many sorts in parallel, either of which
* seems like a certain loser.)
*/
- can_hash = (agg_counts->numOrderedAggs == 0 &&
+ can_hash = (agg_costs->numOrderedAggs == 0 &&
grouping_is_hashable(parse->groupClause));
can_sort = grouping_is_sortable(parse->groupClause);
@@ -2261,9 +2262,9 @@ choose_hashed_grouping(PlannerInfo *root,
/* Estimate per-hash-entry space at tuple width... */
hashentrysize = MAXALIGN(path_width) + MAXALIGN(sizeof(MinimalTupleData));
/* plus space for pass-by-ref transition values... */
- hashentrysize += agg_counts->transitionSpace;
+ hashentrysize += agg_costs->transitionSpace;
/* plus the per-hash-entry overhead */
- hashentrysize += hash_agg_entry_size(agg_counts->numAggs);
+ hashentrysize += hash_agg_entry_size(agg_costs->numAggs);
if (hashentrysize * dNumGroups > work_mem * 1024L)
return false;
@@ -2297,7 +2298,7 @@ choose_hashed_grouping(PlannerInfo *root,
* These path variables are dummies that just hold cost fields; we don't
* make actual Paths for these steps.
*/
- cost_agg(&hashed_p, root, AGG_HASHED, agg_counts->numAggs,
+ cost_agg(&hashed_p, root, AGG_HASHED, agg_costs,
numGroupCols, dNumGroups,
cheapest_path->startup_cost, cheapest_path->total_cost,
path_rows);
@@ -2328,7 +2329,7 @@ choose_hashed_grouping(PlannerInfo *root,
}
if (parse->hasAggs)
- cost_agg(&sorted_p, root, AGG_SORTED, agg_counts->numAggs,
+ cost_agg(&sorted_p, root, AGG_SORTED, agg_costs,
numGroupCols, dNumGroups,
sorted_p.startup_cost, sorted_p.total_cost,
path_rows);
@@ -2447,7 +2448,7 @@ choose_hashed_distinct(PlannerInfo *root,
* These path variables are dummies that just hold cost fields; we don't
* make actual Paths for these steps.
*/
- cost_agg(&hashed_p, root, AGG_HASHED, 0,
+ cost_agg(&hashed_p, root, AGG_HASHED, NULL,
numDistinctCols, dNumDistinctRows,
cheapest_startup_cost, cheapest_total_cost,
path_rows);
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index f2b586d19c..493bc86299 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -281,7 +281,7 @@ SS_assign_special_param(PlannerInfo *root)
}
/*
- * Get the datatype of the first column of the plan's output.
+ * Get the datatype/typmod/collation of the first column of the plan's output.
*
* This information is stored for ARRAY_SUBLINK execution and for
* exprType()/exprTypmod()/exprCollation(), which have no way to get at the
@@ -290,7 +290,8 @@ SS_assign_special_param(PlannerInfo *root)
* always.
*/
static void
-get_first_col_type(Plan *plan, Oid *coltype, int32 *coltypmod, Oid *colcollation)
+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)
@@ -478,7 +479,8 @@ 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, &splan->firstColCollation);
+ get_first_col_type(plan, &splan->firstColType, &splan->firstColTypmod,
+ &splan->firstColCollation);
splan->useHashTable = false;
splan->unknownEqFalse = unknownEqFalse;
splan->setParam = NIL;
@@ -976,7 +978,8 @@ SS_process_ctes(PlannerInfo *root)
splan->subLinkType = CTE_SUBLINK;
splan->testexpr = NULL;
splan->paramIds = NIL;
- get_first_col_type(plan, &splan->firstColType, &splan->firstColTypmod, &splan->firstColCollation);
+ get_first_col_type(plan, &splan->firstColType, &splan->firstColTypmod,
+ &splan->firstColCollation);
splan->useHashTable = false;
splan->unknownEqFalse = false;
splan->setParam = NIL;
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 76adb7cdae..fcff015cdf 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -732,12 +732,12 @@ make_union_unique(SetOperationStmt *op, Plan *plan,
plan->targetlist,
NIL,
AGG_HASHED,
+ NULL,
list_length(groupList),
extract_grouping_cols(groupList,
plan->targetlist),
extract_grouping_ops(groupList),
numGroups,
- 0,
plan);
/* Hashed aggregation produces randomly-ordered results */
*sortClauses = NIL;
@@ -814,7 +814,7 @@ choose_hashed_setop(PlannerInfo *root, List *groupClauses,
* These path variables are dummies that just hold cost fields; we don't
* make actual Paths for these steps.
*/
- cost_agg(&hashed_p, root, AGG_HASHED, 0,
+ cost_agg(&hashed_p, root, AGG_HASHED, NULL,
numGroupCols, dNumGroups,
input_plan->startup_cost, input_plan->total_cost,
input_plan->plan_rows);
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index b3c2aec97b..8b0d8623db 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -50,6 +50,12 @@
typedef struct
{
+ PlannerInfo *root;
+ AggClauseCosts *costs;
+} count_agg_clauses_context;
+
+typedef struct
+{
ParamListInfo boundParams;
PlannerGlobal *glob;
List *active_fns;
@@ -79,7 +85,8 @@ typedef struct
static bool contain_agg_clause_walker(Node *node, void *context);
static bool pull_agg_clause_walker(Node *node, List **context);
-static bool count_agg_clauses_walker(Node *node, AggClauseCounts *counts);
+static bool count_agg_clauses_walker(Node *node,
+ count_agg_clauses_context *context);
static bool find_window_functions_walker(Node *node, WindowFuncLists *lists);
static bool expression_returns_set_rows_walker(Node *node, double *count);
static bool contain_subplans_walker(Node *node, void *context);
@@ -448,48 +455,80 @@ pull_agg_clause_walker(Node *node, List **context)
/*
* count_agg_clauses
- * Recursively count the Aggref nodes in an expression tree.
+ * Recursively count the Aggref nodes in an expression tree, and
+ * accumulate other cost information about them too.
*
* Note: this also checks for nested aggregates, which are an error.
*
- * We not only count the nodes, but attempt to estimate the total space
- * needed for their transition state values if all are evaluated in parallel
- * (as would be done in a HashAgg plan). See AggClauseCounts for the exact
- * set of statistics returned.
+ * We not only count the nodes, but estimate their execution costs, and
+ * attempt to estimate the total space needed for their transition state
+ * values if all are evaluated in parallel (as would be done in a HashAgg
+ * plan). See AggClauseCosts for the exact set of statistics collected.
*
- * NOTE that the counts are ADDED to those already in *counts ... so the
- * caller is responsible for zeroing the struct initially.
+ * NOTE that the counts/costs are ADDED to those already in *costs ... so
+ * the caller is responsible for zeroing the struct initially.
*
* This does not descend into subqueries, and so should be used only after
* reduction of sublinks to subplans, or in contexts where it's known there
* are no subqueries. There mustn't be outer-aggregate references either.
*/
void
-count_agg_clauses(Node *clause, AggClauseCounts *counts)
+count_agg_clauses(PlannerInfo *root, Node *clause, AggClauseCosts *costs)
{
- /* no setup needed */
- count_agg_clauses_walker(clause, counts);
+ count_agg_clauses_context context;
+
+ context.root = root;
+ context.costs = costs;
+ (void) count_agg_clauses_walker(clause, &context);
}
static bool
-count_agg_clauses_walker(Node *node, AggClauseCounts *counts)
+count_agg_clauses_walker(Node *node, count_agg_clauses_context *context)
{
if (node == NULL)
return false;
if (IsA(node, Aggref))
{
Aggref *aggref = (Aggref *) node;
- Oid *inputTypes;
- int numArguments;
+ AggClauseCosts *costs = context->costs;
HeapTuple aggTuple;
Form_pg_aggregate aggform;
+ Oid aggtransfn;
+ Oid aggfinalfn;
Oid aggtranstype;
+ QualCost argcosts;
+ Oid *inputTypes;
+ int numArguments;
ListCell *l;
Assert(aggref->agglevelsup == 0);
- counts->numAggs++;
+
+ /* fetch info about aggregate from pg_aggregate */
+ aggTuple = SearchSysCache1(AGGFNOID,
+ ObjectIdGetDatum(aggref->aggfnoid));
+ if (!HeapTupleIsValid(aggTuple))
+ elog(ERROR, "cache lookup failed for aggregate %u",
+ aggref->aggfnoid);
+ aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);
+ aggtransfn = aggform->aggtransfn;
+ aggfinalfn = aggform->aggfinalfn;
+ aggtranstype = aggform->aggtranstype;
+ ReleaseSysCache(aggTuple);
+
+ /* count it */
+ costs->numAggs++;
if (aggref->aggorder != NIL || aggref->aggdistinct != NIL)
- counts->numOrderedAggs++;
+ costs->numOrderedAggs++;
+
+ /* add component function execution costs to appropriate totals */
+ costs->transCost.per_tuple += get_func_cost(aggtransfn) * cpu_operator_cost;
+ if (OidIsValid(aggfinalfn))
+ costs->finalCost += get_func_cost(aggfinalfn) * cpu_operator_cost;
+
+ /* also add the input expressions' cost to per-input-row costs */
+ cost_qual_eval_node(&argcosts, (Node *) aggref->args, context->root);
+ costs->transCost.startup += argcosts.startup;
+ costs->transCost.per_tuple += argcosts.per_tuple;
/* extract argument types (ignoring any ORDER BY expressions) */
inputTypes = (Oid *) palloc(sizeof(Oid) * list_length(aggref->args));
@@ -502,16 +541,6 @@ count_agg_clauses_walker(Node *node, AggClauseCounts *counts)
inputTypes[numArguments++] = exprType((Node *) tle->expr);
}
- /* fetch aggregate transition datatype from pg_aggregate */
- aggTuple = SearchSysCache1(AGGFNOID,
- ObjectIdGetDatum(aggref->aggfnoid));
- if (!HeapTupleIsValid(aggTuple))
- elog(ERROR, "cache lookup failed for aggregate %u",
- aggref->aggfnoid);
- aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);
- aggtranstype = aggform->aggtranstype;
- ReleaseSysCache(aggTuple);
-
/* resolve actual type of transition state, if polymorphic */
if (IsPolymorphicType(aggtranstype))
{
@@ -554,7 +583,7 @@ count_agg_clauses_walker(Node *node, AggClauseCounts *counts)
avgwidth = get_typavgwidth(aggtranstype, aggtranstypmod);
avgwidth = MAXALIGN(avgwidth);
- counts->transitionSpace += avgwidth + 2 * sizeof(void *);
+ costs->transitionSpace += avgwidth + 2 * sizeof(void *);
}
else if (aggtranstype == INTERNALOID)
{
@@ -566,7 +595,7 @@ count_agg_clauses_walker(Node *node, AggClauseCounts *counts)
* being kept in a private memory context, as is done by
* array_agg() for instance.
*/
- counts->transitionSpace += ALLOCSET_DEFAULT_INITSIZE;
+ costs->transitionSpace += ALLOCSET_DEFAULT_INITSIZE;
}
/*
@@ -585,7 +614,7 @@ count_agg_clauses_walker(Node *node, AggClauseCounts *counts)
}
Assert(!IsA(node, SubLink));
return expression_tree_walker(node, count_agg_clauses_walker,
- (void *) counts);
+ (void *) context);
}
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 55218b5869..161d5ab122 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -1103,7 +1103,7 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
all_hash = false; /* don't try to hash */
else
cost_agg(&agg_path, root,
- AGG_HASHED, 0,
+ AGG_HASHED, NULL,
numCols, pathnode->rows,
subpath->startup_cost,
subpath->total_cost,
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 4947a7d837..b203287047 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -1357,9 +1357,9 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
/*
* Generate dummy targetlist for outer query using column names of
- * leftmost select and common datatypes of topmost set operation. Also
- * make lists of the dummy vars and their names for use in parsing ORDER
- * BY.
+ * leftmost select and common datatypes/collations of topmost set
+ * operation. Also make lists of the dummy vars and their names for use
+ * in parsing ORDER BY.
*
* Note: we use leftmostRTI as the varno of the dummy variables. It
* shouldn't matter too much which RT index they have, as long as they
@@ -1371,7 +1371,9 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
targetnames = NIL;
left_tlist = list_head(leftmostQuery->targetList);
- forthree(lct, sostmt->colTypes, lcm, sostmt->colTypmods, lcc, sostmt->colCollations)
+ forthree(lct, sostmt->colTypes,
+ lcm, sostmt->colTypmods,
+ lcc, sostmt->colCollations)
{
Oid colType = lfirst_oid(lct);
int32 colTypmod = lfirst_int(lcm);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 1e4f8f698b..933a1a2ff9 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -5408,14 +5408,6 @@ privilege_target:
n->objs = $3;
$$ = n;
}
- | FOREIGN TABLE qualified_name_list
- {
- PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
- n->targtype = ACL_TARGET_OBJECT;
- n->objtype = ACL_OBJECT_FOREIGN_TABLE;
- n->objs = $3;
- $$ = n;
- }
| FUNCTION function_with_argtypes_list
{
PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
diff --git a/src/backend/parser/parse_cte.c b/src/backend/parser/parse_cte.c
index 41097263b4..ec6afd83b6 100644
--- a/src/backend/parser/parse_cte.c
+++ b/src/backend/parser/parse_cte.c
@@ -286,10 +286,10 @@ analyzeCTE(ParseState *pstate, CommonTableExpr *cte)
else
{
/*
- * Verify that the previously determined output column types match
- * what the query really produced. We have to check this because the
- * recursive term could have overridden the non-recursive term, and we
- * don't have any easy way to fix that.
+ * Verify that the previously determined output column types and
+ * collations match what the query really produced. We have to check
+ * this because the recursive term could have overridden the
+ * non-recursive term, and we don't have any easy way to fix that.
*/
ListCell *lctlist,
*lctyp,
@@ -366,11 +366,11 @@ analyzeCTETargetList(ParseState *pstate, CommonTableExpr *cte, List *tlist)
Assert(cte->ctecolnames == NIL);
/*
- * We need to determine column names and types. The alias column names
- * override anything coming from the query itself. (Note: the SQL spec
- * says that the alias list must be empty or exactly as long as the output
- * column set; but we allow it to be shorter for consistency with Alias
- * handling.)
+ * We need to determine column names, types, and collations. The alias
+ * column names override anything coming from the query itself. (Note:
+ * the SQL spec says that the alias list must be empty or exactly as long
+ * as the output column set; but we allow it to be shorter for consistency
+ * with Alias handling.)
*/
cte->ctecolnames = copyObject(cte->aliascolnames);
cte->ctecoltypes = cte->ctecoltypmods = cte->ctecolcollations = NIL;
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 2a94f73a9a..5359e691dd 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -1174,7 +1174,8 @@ addRangeTableEntryForFunction(ParseState *pstate,
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);
+ rte->funccolcollations = lappend_oid(rte->funccolcollations,
+ attrcollation);
}
}
else
@@ -1902,7 +1903,8 @@ expandTupleDesc(TupleDesc tupdesc, Alias *eref,
Var *varnode;
varnode = makeVar(rtindex, attr->attnum,
- attr->atttypid, attr->atttypmod, attr->attcollation,
+ attr->atttypid, attr->atttypmod,
+ attr->attcollation,
sublevels_up);
varnode->location = location;
@@ -2009,7 +2011,7 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
/*
* get_rte_attribute_type
- * Get attribute type information from a RangeTblEntry
+ * Get attribute type/typmod/collation information from a RangeTblEntry
*/
void
get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 3f630147b0..e6f9e36bbc 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -372,7 +372,7 @@ transformAssignedExpr(ParseState *pstate,
Oid type_id; /* type of value provided */
Oid attrtype; /* type of target column */
int32 attrtypmod;
- Oid attrcollation;
+ Oid attrcollation; /* collation of target column */
Relation rd = pstate->p_target_relation;
Assert(rd != NULL);
@@ -388,11 +388,12 @@ transformAssignedExpr(ParseState *pstate,
/*
* If the expression is a DEFAULT placeholder, insert the attribute's
- * type/typmod into it so that exprType will report the right things. (We
- * expect that the eventually substituted default expression will in fact
- * have this type and typmod.) Also, reject trying to update a subfield
- * or array element with DEFAULT, since there can't be any default for
- * portions of a column.
+ * type/typmod/collation into it so that exprType etc will report the
+ * right things. (We expect that the eventually substituted default
+ * expression will in fact have this type and typmod. The collation
+ * likely doesn't matter, but let's set it correctly anyway.) Also,
+ * reject trying to update a subfield or array element with DEFAULT, since
+ * there can't be any default for portions of a column.
*/
if (expr && IsA(expr, SetToDefault))
{
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 04289c799d..7624b1ee7f 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -149,6 +149,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
List *result;
List *save_alist;
ListCell *elements;
+ Oid namespaceid;
/*
* We must not scribble on the passed-in CreateStmt, so copy it. (This is
@@ -157,6 +158,33 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
stmt = (CreateStmt *) copyObject(stmt);
/*
+ * Look up the creation namespace. This also checks permissions on the
+ * target namespace, so that we throw any permissions error as early as
+ * possible.
+ */
+ namespaceid = RangeVarGetAndCheckCreationNamespace(stmt->relation);
+
+ /*
+ * If the relation already exists and the user specified "IF NOT EXISTS",
+ * bail out with a NOTICE.
+ */
+ if (stmt->if_not_exists)
+ {
+ Oid existing_relid;
+
+ existing_relid = get_relname_relid(stmt->relation->relname,
+ namespaceid);
+ if (existing_relid != InvalidOid)
+ {
+ ereport(NOTICE,
+ (errcode(ERRCODE_DUPLICATE_TABLE),
+ errmsg("relation \"%s\" already exists, skipping",
+ stmt->relation->relname)));
+ return NIL;
+ }
+ }
+
+ /*
* If the target relation name isn't schema-qualified, make it so. This
* prevents some corner cases in which added-on rewritten commands might
* think they should apply to other relations that have the same name and
@@ -165,11 +193,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
*/
if (stmt->relation->schemaname == NULL
&& stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
- {
- Oid namespaceid = RangeVarGetCreationNamespace(stmt->relation);
-
stmt->relation->schemaname = get_namespace_name(namespaceid);
- }
/* Set up pstate and CreateStmtContext */
pstate = make_parsestate(NULL);
diff --git a/src/backend/port/win32/crashdump.c b/src/backend/port/win32/crashdump.c
index 98bde14804..89509990c5 100644
--- a/src/backend/port/win32/crashdump.c
+++ b/src/backend/port/win32/crashdump.c
@@ -135,7 +135,8 @@ crashDumpHandler(struct _EXCEPTION_POINTERS * pExceptionInfo)
systemTicks = GetTickCount();
snprintf(dumpPath, _MAX_PATH,
- "crashdumps\\postgres-pid%0i-%0i.mdmp", selfPid, systemTicks);
+ "crashdumps\\postgres-pid%0i-%0i.mdmp",
+ (int) selfPid, (int) systemTicks);
dumpPath[_MAX_PATH - 1] = '\0';
dumpFile = CreateFile(dumpPath, GENERIC_WRITE, FILE_SHARE_WRITE,
@@ -143,8 +144,8 @@ crashDumpHandler(struct _EXCEPTION_POINTERS * pExceptionInfo)
NULL);
if (dumpFile == INVALID_HANDLE_VALUE)
{
- write_stderr("could not open crash dump file %s for writing: error code %d\n",
- dumpPath, GetLastError());
+ write_stderr("could not open crash dump file %s for writing: error code %u\n",
+ dumpPath, (unsigned int) GetLastError());
return EXCEPTION_CONTINUE_SEARCH;
}
@@ -153,7 +154,7 @@ crashDumpHandler(struct _EXCEPTION_POINTERS * pExceptionInfo)
write_stderr("wrote crash dump to %s\n", dumpPath);
else
write_stderr("could not write crash dump to %s: error code %08x\n",
- dumpPath, GetLastError());
+ dumpPath, (unsigned int) GetLastError());
CloseHandle(dumpFile);
}
diff --git a/src/backend/port/win32/security.c b/src/backend/port/win32/security.c
index f5e16052d8..e2a2406fe6 100644
--- a/src/backend/port/win32/security.c
+++ b/src/backend/port/win32/security.c
@@ -48,7 +48,7 @@ pgwin32_is_admin(void)
if (!pgwin32_get_dynamic_tokeninfo(AccessToken, TokenGroups,
&InfoBuffer, errbuf, sizeof(errbuf)))
{
- write_stderr(errbuf);
+ write_stderr("%s", errbuf);
exit(1);
}
@@ -138,7 +138,7 @@ pgwin32_is_service(void)
if (!pgwin32_get_dynamic_tokeninfo(AccessToken, TokenUser, &InfoBuffer,
errbuf, sizeof(errbuf)))
{
- fprintf(stderr, errbuf);
+ fprintf(stderr, "%s", errbuf);
return -1;
}
@@ -169,7 +169,7 @@ pgwin32_is_service(void)
if (!pgwin32_get_dynamic_tokeninfo(AccessToken, TokenGroups, &InfoBuffer,
errbuf, sizeof(errbuf)))
{
- fprintf(stderr, errbuf);
+ fprintf(stderr, "%s", errbuf);
return -1;
}
diff --git a/src/backend/port/win32/socket.c b/src/backend/port/win32/socket.c
index dbbd4a35d1..e2ae0f8e4f 100644
--- a/src/backend/port/win32/socket.c
+++ b/src/backend/port/win32/socket.c
@@ -369,8 +369,18 @@ pgwin32_recv(SOCKET s, char *buf, int len, int f)
return -1;
}
+/*
+ * The second argument to send() is defined by SUS to be a "const void *"
+ * and so we use the same signature here to keep compilers happy when
+ * handling callers.
+ *
+ * But the buf member of a WSABUF struct is defined as "char *", so we cast
+ * the second argument to that here when assigning it, also to keep compilers
+ * happy.
+ */
+
int
-pgwin32_send(SOCKET s, char *buf, int len, int flags)
+pgwin32_send(SOCKET s, const void *buf, int len, int flags)
{
WSABUF wbuf;
int r;
@@ -380,7 +390,7 @@ pgwin32_send(SOCKET s, char *buf, int len, int flags)
return -1;
wbuf.len = len;
- wbuf.buf = buf;
+ wbuf.buf = (char *) buf;
/*
* Readiness of socket to send data to UDP socket may be not true: socket
diff --git a/src/backend/port/win32_latch.c b/src/backend/port/win32_latch.c
index b158300d57..3509302aaa 100644
--- a/src/backend/port/win32_latch.c
+++ b/src/backend/port/win32_latch.c
@@ -94,7 +94,7 @@ WaitLatchOrSocket(volatile Latch *latch, SOCKET sock, bool forRead,
DWORD rc;
HANDLE events[3];
HANDLE latchevent;
- HANDLE sockevent;
+ HANDLE sockevent = WSA_INVALID_EVENT; /* silence compiler */
int numevents;
int result = 0;
@@ -161,7 +161,7 @@ WaitLatchOrSocket(volatile Latch *latch, SOCKET sock, bool forRead,
break;
}
else if (rc != WAIT_OBJECT_0)
- elog(ERROR, "unexpected return code from WaitForMultipleObjects(): %d", rc);
+ elog(ERROR, "unexpected return code from WaitForMultipleObjects(): %d", (int) rc);
}
/* Clean up the handle we created for the socket */
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 6e7f66472f..c0cf0336a1 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -529,7 +529,7 @@ PostmasterMain(int argc, char *argv[])
* tcop/postgres.c (the option sets should not conflict) and with the
* common help() function in main/main.c.
*/
- while ((opt = getopt(argc, argv, "A:B:c:D:d:EeFf:h:ijk:lN:nOo:Pp:r:S:sTt:W:-:")) != -1)
+ while ((opt = getopt(argc, argv, "A:B:bc:D:d:EeFf:h:ijk:lN:nOo:Pp:r:S:sTt:W:-:")) != -1)
{
switch (opt)
{
@@ -541,6 +541,11 @@ PostmasterMain(int argc, char *argv[])
SetConfigOption("shared_buffers", optarg, PGC_POSTMASTER, PGC_S_ARGV);
break;
+ case 'b':
+ /* Undocumented flag used for binary upgrades */
+ IsBinaryUpgrade = true;
+ break;
+
case 'D':
userDoption = optarg;
break;
@@ -1480,8 +1485,13 @@ ServerLoop(void)
if (WalWriterPID == 0 && pmState == PM_RUN)
WalWriterPID = StartWalWriter();
- /* If we have lost the autovacuum launcher, try to start a new one */
- if (AutoVacPID == 0 &&
+ /*
+ * If we have lost the autovacuum launcher, try to start a new one.
+ * We don't want autovacuum to run in binary upgrade mode because
+ * autovacuum might update relfrozenxid for empty tables before
+ * the physical files are put in place.
+ */
+ if (!IsBinaryUpgrade && AutoVacPID == 0 &&
(AutoVacuumingActive() || start_autovac_launcher) &&
pmState == PM_RUN)
{
@@ -2413,7 +2423,7 @@ reaper(SIGNAL_ARGS)
*/
if (WalWriterPID == 0)
WalWriterPID = StartWalWriter();
- if (AutoVacuumingActive() && AutoVacPID == 0)
+ if (!IsBinaryUpgrade && AutoVacuumingActive() && AutoVacPID == 0)
AutoVacPID = StartAutoVacLauncher();
if (XLogArchivingActive() && PgArchPID == 0)
PgArchPID = pgarch_start();
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index f02d5d5a1e..48ff9cc151 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -2275,6 +2275,18 @@ PredicateLockTupleRowVersionLink(const Relation relation,
TransactionId oldxmin,
newxmin;
+ /*
+ * Bail out quickly if there are no serializable transactions
+ * running.
+ *
+ * It's safe to do this check without taking any additional
+ * locks. Even if a serializable transaction starts concurrently,
+ * we know it can't take any SIREAD locks on the modified tuple
+ * because the caller is holding the associated buffer page lock.
+ */
+ if (!TransactionIdIsValid(PredXact->SxactGlobalXmin))
+ return;
+
oldblk = ItemPointerGetBlockNumber(&(oldTuple->t_self));
oldoff = ItemPointerGetOffsetNumber(&(oldTuple->t_self));
oldxmin = HeapTupleHeaderGetXmin(oldTuple->t_data);
@@ -2633,6 +2645,15 @@ PredicateLockPageSplit(const Relation relation, const BlockNumber oldblkno,
PREDICATELOCKTARGETTAG newtargettag;
bool success;
+ /*
+ * Bail out quickly if there are no serializable transactions
+ * running. As with PredicateLockTupleRowVersionLink, it's safe to
+ * check this without taking locks because the caller is holding
+ * the buffer page lock.
+ */
+ if (!TransactionIdIsValid(PredXact->SxactGlobalXmin))
+ return;
+
if (SkipSplitTracking(relation))
return;
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 59b7666c10..a07661f02a 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3238,7 +3238,7 @@ process_postgres_switches(int argc, char *argv[], GucContext ctx)
* postmaster/postmaster.c (the option sets should not conflict) and with
* the common help() function in main/main.c.
*/
- while ((flag = getopt(argc, argv, "A:B:c:D:d:EeFf:h:ijk:lN:nOo:Pp:r:S:sTt:v:W:-:")) != -1)
+ while ((flag = getopt(argc, argv, "A:B:bc:D:d:EeFf:h:ijk:lN:nOo:Pp:r:S:sTt:v:W:-:")) != -1)
{
switch (flag)
{
@@ -3250,6 +3250,11 @@ process_postgres_switches(int argc, char *argv[], GucContext ctx)
SetConfigOption("shared_buffers", optarg, ctx, gucsource);
break;
+ case 'b':
+ /* Undocumented flag used for binary upgrades */
+ IsBinaryUpgrade = true;
+ break;
+
case 'D':
if (secure)
userDoption = strdup(optarg);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index b1fd5ec04f..0559998c71 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -530,13 +530,6 @@ standard_ProcessUtility(Node *parsetree,
InvalidOid);
/*
- * If "IF NOT EXISTS" was specified and the relation
- * already exists, do nothing further.
- */
- if (relOid == InvalidOid)
- continue;
-
- /*
* Let AlterTableCreateToastTable decide if this one
* needs a secondary relation too.
*/
@@ -559,15 +552,8 @@ standard_ProcessUtility(Node *parsetree,
relOid = DefineRelation((CreateStmt *) stmt,
RELKIND_FOREIGN_TABLE,
InvalidOid);
-
- /*
- * Unless "IF NOT EXISTS" was specified and the
- * relation already exists, create the
- * pg_foreign_table entry.
- */
- if (relOid != InvalidOid)
- CreateForeignTable((CreateForeignTableStmt *) stmt,
- relOid);
+ CreateForeignTable((CreateForeignTableStmt *) stmt,
+ relOid);
}
else
{
diff --git a/src/backend/tsearch/ts_locale.c b/src/backend/tsearch/ts_locale.c
index b8ae0fe65e..dcaf18b00c 100644
--- a/src/backend/tsearch/ts_locale.c
+++ b/src/backend/tsearch/ts_locale.c
@@ -29,11 +29,12 @@ t_isdigit(const char *ptr)
int clen = pg_mblen(ptr);
wchar_t character[2];
Oid collation = DEFAULT_COLLATION_OID; /* TODO */
+ pg_locale_t mylocale = 0; /* TODO */
if (clen == 1 || lc_ctype_is_c(collation))
return isdigit(TOUCHAR(ptr));
- char2wchar(character, 2, ptr, clen, collation);
+ char2wchar(character, 2, ptr, clen, mylocale);
return iswdigit((wint_t) character[0]);
}
@@ -44,11 +45,12 @@ t_isspace(const char *ptr)
int clen = pg_mblen(ptr);
wchar_t character[2];
Oid collation = DEFAULT_COLLATION_OID; /* TODO */
+ pg_locale_t mylocale = 0; /* TODO */
if (clen == 1 || lc_ctype_is_c(collation))
return isspace(TOUCHAR(ptr));
- char2wchar(character, 2, ptr, clen, collation);
+ char2wchar(character, 2, ptr, clen, mylocale);
return iswspace((wint_t) character[0]);
}
@@ -59,11 +61,12 @@ t_isalpha(const char *ptr)
int clen = pg_mblen(ptr);
wchar_t character[2];
Oid collation = DEFAULT_COLLATION_OID; /* TODO */
+ pg_locale_t mylocale = 0; /* TODO */
if (clen == 1 || lc_ctype_is_c(collation))
return isalpha(TOUCHAR(ptr));
- char2wchar(character, 2, ptr, clen, collation);
+ char2wchar(character, 2, ptr, clen, mylocale);
return iswalpha((wint_t) character[0]);
}
@@ -74,11 +77,12 @@ t_isprint(const char *ptr)
int clen = pg_mblen(ptr);
wchar_t character[2];
Oid collation = DEFAULT_COLLATION_OID; /* TODO */
+ pg_locale_t mylocale = 0; /* TODO */
if (clen == 1 || lc_ctype_is_c(collation))
return isprint(TOUCHAR(ptr));
- char2wchar(character, 2, ptr, clen, collation);
+ char2wchar(character, 2, ptr, clen, mylocale);
return iswprint((wint_t) character[0]);
}
@@ -246,6 +250,7 @@ lowerstr_with_len(const char *str, int len)
#ifdef USE_WIDE_UPPER_LOWER
Oid collation = DEFAULT_COLLATION_OID; /* TODO */
+ pg_locale_t mylocale = 0; /* TODO */
#endif
if (len == 0)
@@ -272,7 +277,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, collation);
+ wlen = char2wchar(wstr, len + 1, str, len, mylocale);
Assert(wlen <= len);
while (*wptr)
@@ -287,7 +292,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, collation);
+ wlen = wchar2char(out, wstr, len, mylocale);
pfree(wstr);
diff --git a/src/backend/tsearch/wparser_def.c b/src/backend/tsearch/wparser_def.c
index 47d777a3e6..3176ddc696 100644
--- a/src/backend/tsearch/wparser_def.c
+++ b/src/backend/tsearch/wparser_def.c
@@ -300,13 +300,14 @@ TParserInit(char *str, int len)
if (prs->charmaxlen > 1)
{
Oid collation = DEFAULT_COLLATION_OID; /* TODO */
+ pg_locale_t mylocale = 0; /* TODO */
prs->usewide = true;
if (lc_ctype_is_c(collation))
{
/*
* char2wchar doesn't work for C-locale and sizeof(pg_wchar) could
- * be not equal to sizeof(wchar_t)
+ * be different from sizeof(wchar_t)
*/
prs->pgwstr = (pg_wchar *) palloc(sizeof(pg_wchar) * (prs->lenstr + 1));
pg_mb2wchar_with_len(prs->str, prs->pgwstr, prs->lenstr);
@@ -314,7 +315,8 @@ 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, collation);
+ char2wchar(prs->wstr, prs->lenstr + 1, prs->str, prs->lenstr,
+ mylocale);
}
}
else
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index dc75ad86ce..4a3e241c41 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -782,10 +782,6 @@ acldefault(GrantObjectType objtype, Oid ownerId)
world_default = ACL_NO_RIGHTS;
owner_default = ACL_ALL_RIGHTS_FOREIGN_SERVER;
break;
- case ACL_OBJECT_FOREIGN_TABLE:
- world_default = ACL_NO_RIGHTS;
- owner_default = ACL_ALL_RIGHTS_FOREIGN_TABLE;
- break;
default:
elog(ERROR, "unrecognized objtype: %d", (int) objtype);
world_default = ACL_NO_RIGHTS; /* keep compiler quiet */
diff --git a/src/backend/utils/adt/array_userfuncs.c b/src/backend/utils/adt/array_userfuncs.c
index 499d3578b3..274e867fdd 100644
--- a/src/backend/utils/adt/array_userfuncs.c
+++ b/src/backend/utils/adt/array_userfuncs.c
@@ -375,7 +375,7 @@ array_cat(PG_FUNCTION_ARGS)
dataoffset = 0; /* marker for no null bitmap */
nbytes = ndatabytes + ARR_OVERHEAD_NONULLS(ndims);
}
- result = (ArrayType *) palloc(nbytes);
+ result = (ArrayType *) palloc0(nbytes);
SET_VARSIZE(result, nbytes);
result->ndim = ndims;
result->dataoffset = dataoffset;
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index a234f35eb8..f5218111db 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -1339,7 +1339,7 @@ array_recv(PG_FUNCTION_ARGS)
dataoffset = 0; /* marker for no null bitmap */
nbytes += ARR_OVERHEAD_NONULLS(ndim);
}
- retval = (ArrayType *) palloc(nbytes);
+ retval = (ArrayType *) palloc0(nbytes);
SET_VARSIZE(retval, nbytes);
retval->ndim = ndim;
retval->dataoffset = dataoffset;
@@ -1977,7 +1977,7 @@ array_get_slice(ArrayType *array,
bytes += ARR_OVERHEAD_NONULLS(ndim);
}
- newarray = (ArrayType *) palloc(bytes);
+ newarray = (ArrayType *) palloc0(bytes);
SET_VARSIZE(newarray, bytes);
newarray->ndim = ndim;
newarray->dataoffset = dataoffset;
@@ -2230,7 +2230,7 @@ array_set(ArrayType *array,
/*
* OK, create the new array and fill in header/dimensions
*/
- newarray = (ArrayType *) palloc(newsize);
+ newarray = (ArrayType *) palloc0(newsize);
SET_VARSIZE(newarray, newsize);
newarray->ndim = ndim;
newarray->dataoffset = newhasnulls ? overheadlen : 0;
@@ -2560,7 +2560,7 @@ array_set_slice(ArrayType *array,
newsize = overheadlen + olddatasize - olditemsize + newitemsize;
- newarray = (ArrayType *) palloc(newsize);
+ newarray = (ArrayType *) palloc0(newsize);
SET_VARSIZE(newarray, newsize);
newarray->ndim = ndim;
newarray->dataoffset = newhasnulls ? overheadlen : 0;
@@ -2819,7 +2819,7 @@ array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType,
dataoffset = 0; /* marker for no null bitmap */
nbytes += ARR_OVERHEAD_NONULLS(ndim);
}
- result = (ArrayType *) palloc(nbytes);
+ result = (ArrayType *) palloc0(nbytes);
SET_VARSIZE(result, nbytes);
result->ndim = ndim;
result->dataoffset = dataoffset;
@@ -2955,7 +2955,7 @@ construct_md_array(Datum *elems,
dataoffset = 0; /* marker for no null bitmap */
nbytes += ARR_OVERHEAD_NONULLS(ndims);
}
- result = (ArrayType *) palloc(nbytes);
+ result = (ArrayType *) palloc0(nbytes);
SET_VARSIZE(result, nbytes);
result->ndim = ndims;
result->dataoffset = dataoffset;
@@ -2979,7 +2979,7 @@ construct_empty_array(Oid elmtype)
{
ArrayType *result;
- result = (ArrayType *) palloc(sizeof(ArrayType));
+ result = (ArrayType *) palloc0(sizeof(ArrayType));
SET_VARSIZE(result, sizeof(ArrayType));
result->ndim = 0;
result->dataoffset = 0;
diff --git a/src/backend/utils/adt/dbsize.c b/src/backend/utils/adt/dbsize.c
index 73a6ad3280..aa4066f9f0 100644
--- a/src/backend/utils/adt/dbsize.c
+++ b/src/backend/utils/adt/dbsize.c
@@ -490,9 +490,15 @@ pg_size_pretty(PG_FUNCTION_ARGS)
(size + mult / 2) / mult);
else
{
+ /* Here we have to worry about avoiding overflow */
+ int64 val;
+
mult *= 1024;
+ val = size / mult;
+ if ((size % mult) >= (mult / 2))
+ val++;
snprintf(buf, sizeof(buf), INT64_FORMAT " TB",
- (size + mult / 2) / mult);
+ val);
}
}
}
diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c
index f895bbbb8b..726a1f4552 100644
--- a/src/backend/utils/adt/formatting.c
+++ b/src/backend/utils/adt/formatting.c
@@ -1454,6 +1454,10 @@ str_numth(char *dest, char *num, int type)
return dest;
}
+/*****************************************************************************
+ * upper/lower/initcap functions
+ *****************************************************************************/
+
/*
* If the system provides the needed functions for wide-character manipulation
* (which are all standardized by C99), then we implement upper/lower/initcap
@@ -1527,7 +1531,7 @@ str_tolower(const char *buff, size_t nbytes, Oid collid)
/* Output workspace cannot have more codes than input bytes */
workspace = (wchar_t *) palloc((nbytes + 1) * sizeof(wchar_t));
- char2wchar(workspace, nbytes + 1, buff, nbytes, collid);
+ char2wchar(workspace, nbytes + 1, buff, nbytes, mylocale);
for (curr_char = 0; workspace[curr_char] != 0; curr_char++)
{
@@ -1543,7 +1547,7 @@ str_tolower(const char *buff, size_t nbytes, Oid collid)
result_size = curr_char * pg_database_encoding_max_length() + 1;
result = palloc(result_size);
- wchar2char(result, workspace, result_size, collid);
+ wchar2char(result, workspace, result_size, mylocale);
pfree(workspace);
}
#endif /* USE_WIDE_UPPER_LOWER */
@@ -1648,7 +1652,7 @@ str_toupper(const char *buff, size_t nbytes, Oid collid)
/* Output workspace cannot have more codes than input bytes */
workspace = (wchar_t *) palloc((nbytes + 1) * sizeof(wchar_t));
- char2wchar(workspace, nbytes + 1, buff, nbytes, collid);
+ char2wchar(workspace, nbytes + 1, buff, nbytes, mylocale);
for (curr_char = 0; workspace[curr_char] != 0; curr_char++)
{
@@ -1664,7 +1668,7 @@ str_toupper(const char *buff, size_t nbytes, Oid collid)
result_size = curr_char * pg_database_encoding_max_length() + 1;
result = palloc(result_size);
- wchar2char(result, workspace, result_size, collid);
+ wchar2char(result, workspace, result_size, mylocale);
pfree(workspace);
}
#endif /* USE_WIDE_UPPER_LOWER */
@@ -1781,7 +1785,7 @@ str_initcap(const char *buff, size_t nbytes, Oid collid)
/* Output workspace cannot have more codes than input bytes */
workspace = (wchar_t *) palloc((nbytes + 1) * sizeof(wchar_t));
- char2wchar(workspace, nbytes + 1, buff, nbytes, collid);
+ char2wchar(workspace, nbytes + 1, buff, nbytes, mylocale);
for (curr_char = 0; workspace[curr_char] != 0; curr_char++)
{
@@ -1809,7 +1813,7 @@ str_initcap(const char *buff, size_t nbytes, Oid collid)
result_size = curr_char * pg_database_encoding_max_length() + 1;
result = palloc(result_size);
- wchar2char(result, workspace, result_size, collid);
+ wchar2char(result, workspace, result_size, mylocale);
pfree(workspace);
}
#endif /* USE_WIDE_UPPER_LOWER */
diff --git a/src/backend/utils/adt/geo_ops.c b/src/backend/utils/adt/geo_ops.c
index b8cf9a8d06..2cf2f64411 100644
--- a/src/backend/utils/adt/geo_ops.c
+++ b/src/backend/utils/adt/geo_ops.c
@@ -1478,6 +1478,8 @@ path_recv(PG_FUNCTION_ARGS)
SET_VARSIZE(path, size);
path->npts = npts;
path->closed = (closed ? 1 : 0);
+ /* prevent instability in unused pad bytes */
+ path->dummy = 0;
for (i = 0; i < npts; i++)
{
@@ -4253,6 +4255,8 @@ path_add(PG_FUNCTION_ARGS)
SET_VARSIZE(result, size);
result->npts = (p1->npts + p2->npts);
result->closed = p1->closed;
+ /* prevent instability in unused pad bytes */
+ result->dummy = 0;
for (i = 0; i < p1->npts; i++)
{
@@ -4486,6 +4490,8 @@ poly_path(PG_FUNCTION_ARGS)
SET_VARSIZE(path, size);
path->npts = poly->npts;
path->closed = TRUE;
+ /* prevent instability in unused pad bytes */
+ path->dummy = 0;
for (i = 0; i < poly->npts; i++)
{
diff --git a/src/backend/utils/adt/pg_locale.c b/src/backend/utils/adt/pg_locale.c
index 0e6723d469..7c941dd991 100644
--- a/src/backend/utils/adt/pg_locale.c
+++ b/src/backend/utils/adt/pg_locale.c
@@ -63,7 +63,16 @@
#include "utils/syscache.h"
#ifdef WIN32
+/*
+ * This Windows file defines StrNCpy. We don't need it here, so we undefine
+ * it to keep the compiler quiet, and undefine it again after the file is
+ * included, so we don't accidentally use theirs.
+ */
+#undef StrNCpy
#include <shlwapi.h>
+#ifdef StrNCpy
+#undef STrNCpy
+#endif
#endif
#define MAX_L10N_DATA 80
@@ -555,7 +564,9 @@ strftime_win32(char *dst, size_t dstlen, const wchar_t *format, const struct tm
dst[len] = '\0';
if (encoding != PG_UTF8)
{
- char *convstr = pg_do_encoding_conversion(dst, len, PG_UTF8, encoding);
+ char *convstr =
+ (char *) pg_do_encoding_conversion((unsigned char *) dst,
+ len, PG_UTF8, encoding);
if (dst != convstr)
{
@@ -1030,3 +1041,176 @@ pg_newlocale_from_collation(Oid collid)
return cache_entry->locale;
}
+
+
+/*
+ * These functions convert from/to libc's wchar_t, *not* pg_wchar_t.
+ * Therefore we keep them here rather than with the mbutils code.
+ */
+
+#ifdef USE_WIDE_UPPER_LOWER
+
+/*
+ * wchar2char --- convert wide characters to multibyte format
+ *
+ * This has the same API as the standard wcstombs_l() function; in particular,
+ * tolen is the maximum number of bytes to store at *to, and *from must be
+ * zero-terminated. The output will be zero-terminated iff there is room.
+ */
+size_t
+wchar2char(char *to, const wchar_t *from, size_t tolen, pg_locale_t locale)
+{
+ size_t result;
+
+ if (tolen == 0)
+ return 0;
+
+#ifdef WIN32
+
+ /*
+ * On Windows, the "Unicode" locales assume UTF16 not UTF8 encoding, and
+ * for some reason mbstowcs and wcstombs won't do this for us, so we use
+ * MultiByteToWideChar().
+ */
+ if (GetDatabaseEncoding() == PG_UTF8)
+ {
+ result = WideCharToMultiByte(CP_UTF8, 0, from, -1, to, tolen,
+ NULL, NULL);
+ /* A zero return is failure */
+ if (result <= 0)
+ result = -1;
+ else
+ {
+ Assert(result <= tolen);
+ /* Microsoft counts the zero terminator in the result */
+ result--;
+ }
+ }
+ else
+#endif /* WIN32 */
+ if (locale == (pg_locale_t) 0)
+ {
+ /* Use wcstombs directly for the default locale */
+ result = wcstombs(to, from, tolen);
+ }
+ else
+ {
+#ifdef HAVE_LOCALE_T
+#ifdef HAVE_WCSTOMBS_L
+ /* Use wcstombs_l for nondefault locales */
+ result = wcstombs_l(to, from, tolen, locale);
+#else /* !HAVE_WCSTOMBS_L */
+ /* We have to temporarily set the locale as current ... ugh */
+ locale_t save_locale = uselocale(locale);
+
+ result = wcstombs(to, from, tolen);
+
+ uselocale(save_locale);
+#endif /* HAVE_WCSTOMBS_L */
+#else /* !HAVE_LOCALE_T */
+ /* Can't have locale != 0 without HAVE_LOCALE_T */
+ elog(ERROR, "wcstombs_l is not available");
+ result = 0; /* keep compiler quiet */
+#endif /* HAVE_LOCALE_T */
+ }
+
+ return result;
+}
+
+/*
+ * char2wchar --- convert multibyte characters to wide characters
+ *
+ * This has almost the API of mbstowcs_l(), except that *from need not be
+ * null-terminated; instead, the number of input bytes is specified as
+ * fromlen. Also, we ereport() rather than returning -1 for invalid
+ * input encoding. tolen is the maximum number of wchar_t's to store at *to.
+ * 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,
+ pg_locale_t locale)
+{
+ size_t result;
+
+ if (tolen == 0)
+ return 0;
+
+#ifdef WIN32
+ /* See WIN32 "Unicode" comment above */
+ if (GetDatabaseEncoding() == PG_UTF8)
+ {
+ /* Win32 API does not work for zero-length input */
+ if (fromlen == 0)
+ result = 0;
+ else
+ {
+ result = MultiByteToWideChar(CP_UTF8, 0, from, fromlen, to, tolen - 1);
+ /* A zero return is failure */
+ if (result == 0)
+ result = -1;
+ }
+
+ if (result != -1)
+ {
+ Assert(result < tolen);
+ /* Append trailing null wchar (MultiByteToWideChar() does not) */
+ to[result] = 0;
+ }
+ }
+ else
+#endif /* WIN32 */
+ {
+ /* mbstowcs requires ending '\0' */
+ char *str = pnstrdup(from, fromlen);
+
+ if (locale == (pg_locale_t) 0)
+ {
+ /* Use mbstowcs directly for the default locale */
+ result = mbstowcs(to, str, tolen);
+ }
+ else
+ {
+#ifdef HAVE_LOCALE_T
+#ifdef HAVE_WCSTOMBS_L
+ /* Use mbstowcs_l for nondefault locales */
+ result = mbstowcs_l(to, str, tolen, locale);
+#else /* !HAVE_WCSTOMBS_L */
+ /* We have to temporarily set the locale as current ... ugh */
+ locale_t save_locale = uselocale(locale);
+
+ result = mbstowcs(to, str, tolen);
+
+ uselocale(save_locale);
+#endif /* HAVE_WCSTOMBS_L */
+#else /* !HAVE_LOCALE_T */
+ /* Can't have locale != 0 without HAVE_LOCALE_T */
+ elog(ERROR, "mbstowcs_l is not available");
+ result = 0; /* keep compiler quiet */
+#endif /* HAVE_LOCALE_T */
+ }
+
+ pfree(str);
+ }
+
+ if (result == -1)
+ {
+ /*
+ * Invalid multibyte character encountered. We try to give a useful
+ * error message by letting pg_verifymbstr check the string. But it's
+ * possible that the string is OK to us, and not OK to mbstowcs ---
+ * this suggests that the LC_CTYPE locale is different from the
+ * database encoding. Give a generic error message if verifymbstr
+ * can't find anything wrong.
+ */
+ pg_verifymbstr(from, fromlen, false); /* might not return */
+ /* but if it does ... */
+ ereport(ERROR,
+ (errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE),
+ errmsg("invalid multibyte character for locale"),
+ errhint("The server's LC_CTYPE locale is probably incompatible with the database encoding.")));
+ }
+
+ return result;
+}
+
+#endif /* USE_WIDE_UPPER_LOWER */
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index c2bb30f24c..af6737f7a5 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -235,7 +235,8 @@ 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, List *collations,
+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);
@@ -6655,7 +6656,8 @@ 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, List *collations,
+get_from_clause_coldeflist(List *names,
+ List *types, List *typmods, List *collations,
deparse_context *context)
{
StringInfo buf = context->buf;
@@ -6689,7 +6691,8 @@ get_from_clause_coldeflist(List *names, List *types, List *typmods, List *collat
appendStringInfo(buf, "%s %s",
quote_identifier(attname),
format_type_with_typemod(atttypid, atttypmod));
- if (attcollation && attcollation != DEFAULT_COLLATION_OID)
+ if (OidIsValid(attcollation) &&
+ attcollation != get_typcollation(atttypid))
appendStringInfo(buf, " COLLATE %s",
generate_collation_name(attcollation));
i++;
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index 4d800f8a00..fa8cecafcb 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -6458,6 +6458,10 @@ gincostestimate(PG_FUNCTION_ARGS)
numDataPages = Min(numDataPages, numPages - numEntryPages);
}
+ /* In an empty index, numEntries could be zero. Avoid divide-by-zero */
+ if (numEntries < 1)
+ numEntries = 1;
+
/*
* Include predicate in selectivityQuals (should match
* genericcostestimate)
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 2b5e37e2f0..75f510c164 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -277,7 +277,7 @@ static const struct cachedesc cacheinfo[] = {
Anum_pg_collation_collnamespace,
0
},
- 256
+ 64
},
{CollationRelationId, /* COLLOID */
CollationOidIndexId,
@@ -288,7 +288,7 @@ static const struct cachedesc cacheinfo[] = {
0,
0
},
- 256
+ 64
},
{ConversionRelationId, /* CONDEFAULT */
ConversionDefaultIndexId,
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 984ffd0c73..c4c41544a2 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -85,6 +85,7 @@ pid_t PostmasterPid = 0;
*/
bool IsPostmasterEnvironment = false;
bool IsUnderPostmaster = false;
+bool IsBinaryUpgrade = false;
bool ExitOnAnyError = false;
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index a4c5d4c69a..1f6fba5f75 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -626,6 +626,16 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
}
/*
+ * Binary upgrades only allowed super-user connections
+ */
+ if (IsBinaryUpgrade && !am_superuser)
+ {
+ ereport(FATAL,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ errmsg("must be superuser to connect in binary upgrade mode")));
+ }
+
+ /*
* The last few connections slots are reserved for superusers. Although
* replication connections currently require superuser privileges, we
* don't allow them to consume the reserved slots, which are intended for
diff --git a/src/backend/utils/mb/mbutils.c b/src/backend/utils/mb/mbutils.c
index 3cb7ce3269..848c26f41f 100644
--- a/src/backend/utils/mb/mbutils.c
+++ b/src/backend/utils/mb/mbutils.c
@@ -13,7 +13,6 @@
#include "mb/pg_wchar.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
-#include "utils/pg_locale.h"
#include "utils/syscache.h"
/*
@@ -689,126 +688,6 @@ perform_default_encoding_conversion(const char *src, int len, bool is_client_to_
}
-
-#ifdef USE_WIDE_UPPER_LOWER
-
-/*
- * wchar2char --- convert wide characters to multibyte format
- *
- * This has the same API as the standard wcstombs() function; in particular,
- * tolen is the maximum number of bytes to store at *to, and *from must be
- * zero-terminated. The output will be zero-terminated iff there is room.
- */
-size_t
-wchar2char(char *to, const wchar_t *from, size_t tolen, Oid collation)
-{
- size_t result;
-
- if (tolen == 0)
- return 0;
-
-#ifdef WIN32
-
- /*
- * On Windows, the "Unicode" locales assume UTF16 not UTF8 encoding, and
- * for some reason mbstowcs and wcstombs won't do this for us, so we use
- * MultiByteToWideChar().
- */
- if (GetDatabaseEncoding() == PG_UTF8)
- {
- result = WideCharToMultiByte(CP_UTF8, 0, from, -1, to, tolen,
- NULL, NULL);
- /* A zero return is failure */
- if (result <= 0)
- result = -1;
- else
- {
- Assert(result <= tolen);
- /* Microsoft counts the zero terminator in the result */
- result--;
- }
- }
- else
-#endif /* WIN32 */
- {
- Assert(!lc_ctype_is_c(collation));
- result = wcstombs(to, from, tolen);
- }
- return result;
-}
-
-/*
- * char2wchar --- convert multibyte characters to wide characters
- *
- * This has almost the API of mbstowcs(), except that *from need not be
- * null-terminated; instead, the number of input bytes is specified as
- * fromlen. Also, we ereport() rather than returning -1 for invalid
- * input encoding. tolen is the maximum number of wchar_t's to store at *to.
- * 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, Oid collation)
-{
- size_t result;
-
- if (tolen == 0)
- return 0;
-
-#ifdef WIN32
- /* See WIN32 "Unicode" comment above */
- if (GetDatabaseEncoding() == PG_UTF8)
- {
- /* Win32 API does not work for zero-length input */
- if (fromlen == 0)
- result = 0;
- else
- {
- result = MultiByteToWideChar(CP_UTF8, 0, from, fromlen, to, tolen - 1);
- /* A zero return is failure */
- if (result == 0)
- result = -1;
- }
-
- if (result != -1)
- {
- Assert(result < tolen);
- /* Append trailing null wchar (MultiByteToWideChar() does not) */
- to[result] = 0;
- }
- }
- else
-#endif /* WIN32 */
- {
- /* mbstowcs requires ending '\0' */
- char *str = pnstrdup(from, fromlen);
-
- Assert(!lc_ctype_is_c(collation));
- result = mbstowcs(to, str, tolen);
- pfree(str);
- }
-
- if (result == -1)
- {
- /*
- * Invalid multibyte character encountered. We try to give a useful
- * error message by letting pg_verifymbstr check the string. But it's
- * possible that the string is OK to us, and not OK to mbstowcs ---
- * this suggests that the LC_CTYPE locale is different from the
- * database encoding. Give a generic error message if verifymbstr
- * can't find anything wrong.
- */
- pg_verifymbstr(from, fromlen, false); /* might not return */
- /* but if it does ... */
- ereport(ERROR,
- (errcode(ERRCODE_CHARACTER_NOT_IN_REPERTOIRE),
- errmsg("invalid multibyte character for locale"),
- errhint("The server's LC_CTYPE locale is probably incompatible with the database encoding.")));
- }
-
- return result;
-}
-#endif
-
/* convert a multibyte string to a wchar */
int
pg_mb2wchar(const char *from, pg_wchar *to)
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index d26ff63e1d..3b321494e4 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -1561,7 +1561,7 @@ normalize_locale_name(char *new, const char *old)
static void
setup_collation(void)
{
-#ifdef HAVE_LOCALE_T
+#if defined(HAVE_LOCALE_T) && !defined(WIN32)
int i;
FILE *locale_a_handle;
char localebuf[NAMEDATALEN];
@@ -1687,10 +1687,10 @@ setup_collation(void)
printf(_("No usable system locales were found.\n"));
printf(_("Use the option \"--debug\" to see details.\n"));
}
-#else /* not HAVE_LOCALE_T */
+#else /* not HAVE_LOCALE_T && not WIN32 */
printf(_("not supported on this platform\n"));
fflush(stdout);
-#endif /* not HAVE_LOCALE_T */
+#endif /* not HAVE_LOCALE_T && not WIN32*/
}
/*
@@ -2387,7 +2387,10 @@ setlocales(void)
#ifdef WIN32
typedef BOOL (WINAPI * __CreateRestrictedToken) (HANDLE, DWORD, DWORD, PSID_AND_ATTRIBUTES, DWORD, PLUID_AND_ATTRIBUTES, DWORD, PSID_AND_ATTRIBUTES, PHANDLE);
+/* Windows API define missing from some versions of MingW headers */
+#ifndef DISABLE_MAX_PRIVILEGE
#define DISABLE_MAX_PRIVILEGE 0x1
+#endif
/*
* Create a restricted token and execute the specified process with it.
diff --git a/src/bin/pg_basebackup/pg_basebackup.c b/src/bin/pg_basebackup/pg_basebackup.c
index aeec8136c4..a5d9c2e652 100644
--- a/src/bin/pg_basebackup/pg_basebackup.c
+++ b/src/bin/pg_basebackup/pg_basebackup.c
@@ -756,7 +756,6 @@ static void
BaseBackup(void)
{
PGresult *res;
- uint32 timeline;
char current_path[MAXPGPATH];
char escaped_label[MAXPGPATH];
int i;
@@ -769,25 +768,6 @@ BaseBackup(void)
conn = GetConnection();
/*
- * Run IDENFITY_SYSTEM so we can get the timeline
- */
- res = PQexec(conn, "IDENTIFY_SYSTEM");
- if (PQresultStatus(res) != PGRES_TUPLES_OK)
- {
- fprintf(stderr, _("%s: could not identify system: %s\n"),
- progname, PQerrorMessage(conn));
- disconnect_and_exit(1);
- }
- if (PQntuples(res) != 1)
- {
- fprintf(stderr, _("%s: could not identify system, got %i rows\n"),
- progname, PQntuples(res));
- disconnect_and_exit(1);
- }
- timeline = atoi(PQgetvalue(res, 0, 1));
- PQclear(res);
-
- /*
* Start the actual backup
*/
PQescapeStringConn(conn, escaped_label, label, sizeof(escaped_label), &i);
diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c
index 4e8d0b1d39..98de19fa38 100644
--- a/src/bin/pg_ctl/pg_ctl.c
+++ b/src/bin/pg_ctl/pg_ctl.c
@@ -1461,8 +1461,10 @@ typedef BOOL (WINAPI * __SetInformationJobObject) (HANDLE, JOBOBJECTINFOCLASS, L
typedef BOOL (WINAPI * __AssignProcessToJobObject) (HANDLE, HANDLE);
typedef BOOL (WINAPI * __QueryInformationJobObject) (HANDLE, JOBOBJECTINFOCLASS, LPVOID, DWORD, LPDWORD);
-/* Windows API define missing from MingW headers */
+/* Windows API define missing from some versions of MingW headers */
+#ifndef DISABLE_MAX_PRIVILEGE
#define DISABLE_MAX_PRIVILEGE 0x1
+#endif
/*
* Create a restricted token, a job object sandbox, and execute the specified
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index c2f6180e99..afc7fd7032 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -12004,7 +12004,11 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
"UNLOGGED " : "",
reltypename,
fmtId(tbinfo->dobj.name));
- if (tbinfo->reloftype)
+ /*
+ * In case of a binary upgrade, we dump the table normally and attach
+ * it to the type afterward.
+ */
+ if (tbinfo->reloftype && !binary_upgrade)
appendPQExpBuffer(q, " OF %s", tbinfo->reloftype);
actual_atts = 0;
for (j = 0; j < tbinfo->numatts; j++)
@@ -12032,7 +12036,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
bool has_notnull = (tbinfo->notnull[j]
&& (!tbinfo->inhNotNull[j] || binary_upgrade));
- if (tbinfo->reloftype && !has_default && !has_notnull)
+ if (tbinfo->reloftype && !has_default && !has_notnull && !binary_upgrade)
continue;
/* Format properly if not first attr */
@@ -12060,7 +12064,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
}
/* Attribute type */
- if (tbinfo->reloftype)
+ if (tbinfo->reloftype && !binary_upgrade)
{
appendPQExpBuffer(q, "WITH OPTIONS");
}
@@ -12126,7 +12130,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
if (actual_atts)
appendPQExpBuffer(q, "\n)");
- else if (!tbinfo->reloftype)
+ else if (!(tbinfo->reloftype && !binary_upgrade))
{
/*
* We must have a parenthesized attribute list, even though empty,
@@ -12192,6 +12196,7 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
* an INHERITS clause --- the latter would possibly mess up the column
* order. That also means we have to take care about setting
* attislocal correctly, plus fix up any inherited CHECK constraints.
+ * Analogously, we set up typed tables using ALTER TABLE / OF here.
*/
if (binary_upgrade && tbinfo->relkind == RELKIND_RELATION)
{
@@ -12268,6 +12273,14 @@ dumpTableSchema(Archive *fout, TableInfo *tbinfo)
}
}
+ if (tbinfo->reloftype)
+ {
+ appendPQExpBuffer(q, "\n-- For binary upgrade, set up typed tables this way.\n");
+ appendPQExpBuffer(q, "ALTER TABLE ONLY %s OF %s;\n",
+ fmtId(tbinfo->dobj.name),
+ tbinfo->reloftype);
+ }
+
appendPQExpBuffer(q, "\n-- For binary upgrade, set heap's relfrozenxid\n");
appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
"SET relfrozenxid = '%u'\n"
diff --git a/src/include/access/gin_private.h b/src/include/access/gin_private.h
index 06c6fa2f9c..a79c003a9f 100644
--- a/src/include/access/gin_private.h
+++ b/src/include/access/gin_private.h
@@ -303,8 +303,8 @@ typedef struct GinState
FmgrInfo comparePartialFn[INDEX_MAX_KEYS]; /* optional method */
/* canPartialMatch[i] is true if comparePartialFn[i] is valid */
bool canPartialMatch[INDEX_MAX_KEYS];
- /* Collations to supply to the compareFns and comparePartialFns */
- Oid compareCollation[INDEX_MAX_KEYS];
+ /* Collations to pass to the support functions */
+ Oid supportCollation[INDEX_MAX_KEYS];
} GinState;
/* XLog stuff */
diff --git a/src/include/access/gist_private.h b/src/include/access/gist_private.h
index ecc188f7df..77e3cb5aee 100644
--- a/src/include/access/gist_private.h
+++ b/src/include/access/gist_private.h
@@ -41,6 +41,9 @@ typedef struct GISTSTATE
FmgrInfo equalFn[INDEX_MAX_KEYS];
FmgrInfo distanceFn[INDEX_MAX_KEYS];
+ /* Collations to pass to the support functions */
+ Oid supportCollation[INDEX_MAX_KEYS];
+
TupleDesc tupdesc;
} GISTSTATE;
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 53c684aa4e..2df489f32e 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201104181
+#define CATALOG_VERSION_NO 201104251
#endif
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index 9ee4c381d6..456587b653 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -63,8 +63,7 @@ extern Oid heap_create_with_catalog(const char *relname,
OnCommitAction oncommit,
Datum reloptions,
bool use_user_acl,
- bool allow_system_table_mods,
- bool if_not_exists);
+ bool allow_system_table_mods);
extern void heap_drop_with_catalog(Oid relid);
diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h
index f59beee80d..53600969ad 100644
--- a/src/include/catalog/namespace.h
+++ b/src/include/catalog/namespace.h
@@ -49,6 +49,7 @@ typedef struct OverrideSearchPath
extern Oid RangeVarGetRelid(const RangeVar *relation, bool failOK);
extern Oid RangeVarGetCreationNamespace(const RangeVar *newRelation);
+extern Oid RangeVarGetAndCheckCreationNamespace(const RangeVar *newRelation);
extern Oid RelnameGetRelid(const char *relname);
extern bool RelationIsVisible(Oid relid);
diff --git a/src/include/mb/pg_wchar.h b/src/include/mb/pg_wchar.h
index 8efc6d3046..826c7af53b 100644
--- a/src/include/mb/pg_wchar.h
+++ b/src/include/mb/pg_wchar.h
@@ -19,8 +19,6 @@
#ifndef PG_WCHAR_H
#define PG_WCHAR_H
-#include <sys/types.h>
-
/*
* The pg_wchar type
*/
@@ -392,11 +390,6 @@ extern int pg_mbcharcliplen(const char *mbstr, int len, int imit);
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, Oid collation);
-extern size_t char2wchar(wchar_t *to, size_t tolen, const char *from, size_t fromlen, Oid collation);
-#endif
-
extern int PrepareClientEncoding(int encoding);
extern int SetClientEncoding(int encoding);
extern void InitializeClientEncoding(void);
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index aa8cce5ca8..9d194171a5 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -124,6 +124,7 @@ do { \
extern pid_t PostmasterPid;
extern bool IsPostmasterEnvironment;
extern PGDLLIMPORT bool IsUnderPostmaster;
+extern bool IsBinaryUpgrade;
extern bool ExitOnAnyError;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index b46cccc04b..c67be5f8a2 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1284,7 +1284,6 @@ typedef enum GrantObjectType
ACL_OBJECT_DATABASE, /* database */
ACL_OBJECT_FDW, /* foreign-data wrapper */
ACL_OBJECT_FOREIGN_SERVER, /* foreign server */
- ACL_OBJECT_FOREIGN_TABLE, /* foreign table */
ACL_OBJECT_FUNCTION, /* function */
ACL_OBJECT_LANGUAGE, /* procedural language */
ACL_OBJECT_LARGEOBJECT, /* largeobject */
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 78b03e024e..f6592697e4 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -46,6 +46,20 @@ typedef struct QualCost
Cost per_tuple; /* per-evaluation cost */
} QualCost;
+/*
+ * Costing aggregate function execution requires these statistics about
+ * the aggregates to be executed by a given Agg node. Note that transCost
+ * includes the execution costs of the aggregates' input expressions.
+ */
+typedef struct AggClauseCosts
+{
+ int numAggs; /* total number of aggregate functions */
+ int numOrderedAggs; /* number that use DISTINCT or ORDER BY */
+ QualCost transCost; /* total per-input-row execution costs */
+ Cost finalCost; /* total costs of agg final functions */
+ Size transitionSpace; /* space for pass-by-ref transition data */
+} AggClauseCosts;
+
/*----------
* PlannerGlobal
diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h
index 7ae236d167..4af772d255 100644
--- a/src/include/optimizer/clauses.h
+++ b/src/include/optimizer/clauses.h
@@ -22,13 +22,6 @@
typedef struct
{
- int numAggs; /* total number of aggregate calls */
- int numOrderedAggs; /* number that use DISTINCT or ORDER BY */
- Size transitionSpace; /* for pass-by-ref transition data */
-} AggClauseCounts;
-
-typedef struct
-{
int numWindowFuncs; /* total number of WindowFuncs found */
Index maxWinRef; /* windowFuncs[] is indexed 0 .. maxWinRef */
List **windowFuncs; /* lists of WindowFuncs for each winref */
@@ -56,7 +49,8 @@ extern List *make_ands_implicit(Expr *clause);
extern bool contain_agg_clause(Node *clause);
extern List *pull_agg_clause(Node *clause);
-extern void count_agg_clauses(Node *clause, AggClauseCounts *counts);
+extern void count_agg_clauses(PlannerInfo *root, Node *clause,
+ AggClauseCosts *costs);
extern bool contain_window_function(Node *clause);
extern WindowFuncLists *find_window_functions(Node *clause, Index maxWinRef);
diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h
index 08898c13b9..2763863af2 100644
--- a/src/include/optimizer/cost.h
+++ b/src/include/optimizer/cost.h
@@ -94,12 +94,12 @@ extern void cost_material(Path *path,
Cost input_startup_cost, Cost input_total_cost,
double tuples, int width);
extern void cost_agg(Path *path, PlannerInfo *root,
- AggStrategy aggstrategy, int numAggs,
+ AggStrategy aggstrategy, const AggClauseCosts *aggcosts,
int numGroupCols, double numGroups,
Cost input_startup_cost, Cost input_total_cost,
double input_tuples);
extern void cost_windowagg(Path *path, PlannerInfo *root,
- int numWindowFuncs, int numPartCols, int numOrderCols,
+ List *windowFuncs, int numPartCols, int numOrderCols,
Cost input_startup_cost, Cost input_total_cost,
double input_tuples);
extern void cost_group(Path *path, PlannerInfo *root,
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index d48bf39e41..5261691af6 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -34,7 +34,7 @@ extern void query_planner(PlannerInfo *root, List *tlist,
*/
extern void preprocess_minmax_aggregates(PlannerInfo *root, List *tlist);
extern Plan *optimize_minmax_aggregates(PlannerInfo *root, List *tlist,
- Path *best_path);
+ const AggClauseCosts *aggcosts, Path *best_path);
/*
* prototypes for plan/createplan.c
@@ -54,12 +54,12 @@ extern Sort *make_sort_from_sortclauses(PlannerInfo *root, List *sortcls,
extern Sort *make_sort_from_groupcols(PlannerInfo *root, List *groupcls,
AttrNumber *grpColIdx, Plan *lefttree);
extern Agg *make_agg(PlannerInfo *root, List *tlist, List *qual,
- AggStrategy aggstrategy,
+ AggStrategy aggstrategy, const AggClauseCosts *aggcosts,
int numGroupCols, AttrNumber *grpColIdx, Oid *grpOperators,
- long numGroups, int numAggs,
+ long numGroups,
Plan *lefttree);
extern WindowAgg *make_windowagg(PlannerInfo *root, List *tlist,
- int numWindowFuncs, Index winref,
+ List *windowFuncs, Index winref,
int partNumCols, AttrNumber *partColIdx, Oid *partOperators,
int ordNumCols, AttrNumber *ordColIdx, Oid *ordOperators,
int frameOptions, Node *startOffset, Node *endOffset,
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 3ee1d077a5..04560c74bf 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -656,6 +656,9 @@
/* Define to 1 if you have the `wcstombs' function. */
#undef HAVE_WCSTOMBS
+/* Define to 1 if you have the `wcstombs_l' function. */
+#undef HAVE_WCSTOMBS_L
+
/* Define to 1 if you have the <wctype.h> header file. */
#undef HAVE_WCTYPE_H
diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32
index 177bca1bd5..b85bf411de 100644
--- a/src/include/pg_config.h.win32
+++ b/src/include/pg_config.h.win32
@@ -538,6 +538,9 @@
/* Define to 1 if you have the `wcstombs' function. */
#define HAVE_WCSTOMBS 1
+/* Define to 1 if you have the `wcstombs_l' function. */
+#define HAVE_WCSTOMBS_L 1
+
/* Define to 1 if you have the <wctype.h> header file. */
#define HAVE_WCTYPE_H 1
diff --git a/src/include/port/win32.h b/src/include/port/win32.h
index 8a3c33f995..34f4004129 100644
--- a/src/include/port/win32.h
+++ b/src/include/port/win32.h
@@ -304,6 +304,8 @@ typedef int pid_t;
#define iswspace_l _iswspace_l
#define strcoll_l _strcoll_l
#define wcscoll_l _wcscoll_l
+#define wcstombs_l _wcstombs_l
+#define mbstowcs_l _mbstowcs_l
/* In backend/port/win32/signal.c */
@@ -334,7 +336,7 @@ SOCKET pgwin32_accept(SOCKET s, struct sockaddr * addr, int *addrlen);
int pgwin32_connect(SOCKET s, const struct sockaddr * name, int namelen);
int pgwin32_select(int nfds, fd_set *readfs, fd_set *writefds, fd_set *exceptfds, const struct timeval * timeout);
int pgwin32_recv(SOCKET s, char *buf, int len, int flags);
-int pgwin32_send(SOCKET s, char *buf, int len, int flags);
+int pgwin32_send(SOCKET s, const void *buf, int len, int flags);
const char *pgwin32_socket_strerror(int err);
int pgwin32_waitforsinglesocket(SOCKET s, int what, int timeout);
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 75bdaa5343..6b80039f97 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -150,7 +150,6 @@ typedef ArrayType Acl;
#define ACL_ALL_RIGHTS_DATABASE (ACL_CREATE|ACL_CREATE_TEMP|ACL_CONNECT)
#define ACL_ALL_RIGHTS_FDW (ACL_USAGE)
#define ACL_ALL_RIGHTS_FOREIGN_SERVER (ACL_USAGE)
-#define ACL_ALL_RIGHTS_FOREIGN_TABLE (ACL_SELECT)
#define ACL_ALL_RIGHTS_FUNCTION (ACL_EXECUTE)
#define ACL_ALL_RIGHTS_LANGUAGE (ACL_USAGE)
#define ACL_ALL_RIGHTS_LARGEOBJECT (ACL_SELECT|ACL_UPDATE)
@@ -195,7 +194,6 @@ typedef enum AclObjectKind
ACL_KIND_TSCONFIGURATION, /* pg_ts_config */
ACL_KIND_FDW, /* pg_foreign_data_wrapper */
ACL_KIND_FOREIGN_SERVER, /* pg_foreign_server */
- ACL_KIND_FOREIGN_TABLE, /* pg_foreign_table */
ACL_KIND_EXTENSION, /* pg_extension */
MAX_ACL_KIND /* MUST BE LAST */
} AclObjectKind;
diff --git a/src/include/utils/pg_locale.h b/src/include/utils/pg_locale.h
index 25b9d50915..c59a004fc9 100644
--- a/src/include/utils/pg_locale.h
+++ b/src/include/utils/pg_locale.h
@@ -72,4 +72,12 @@ typedef int pg_locale_t;
extern pg_locale_t pg_newlocale_from_collation(Oid collid);
+/* These functions convert from/to libc's wchar_t, *not* pg_wchar_t */
+#ifdef USE_WIDE_UPPER_LOWER
+extern size_t wchar2char(char *to, const wchar_t *from, size_t tolen,
+ pg_locale_t locale);
+extern size_t char2wchar(wchar_t *to, size_t tolen,
+ const char *from, size_t fromlen, pg_locale_t locale);
+#endif
+
#endif /* _PG_LOCALE_ */
diff --git a/src/interfaces/ecpg/ecpglib/sqlda.c b/src/interfaces/ecpg/ecpglib/sqlda.c
index e06f25e487..c08c61b5df 100644
--- a/src/interfaces/ecpg/ecpglib/sqlda.c
+++ b/src/interfaces/ecpg/ecpglib/sqlda.c
@@ -228,7 +228,8 @@ ecpg_build_compat_sqlda(int line, PGresult *res, int row, enum COMPAT_MODE compa
strcpy(fname, PQfname(res, i));
sqlda->sqlvar[i].sqlname = fname;
fname += strlen(sqlda->sqlvar[i].sqlname) + 1;
- sqlda->sqlvar[i].sqlformat = (char *) (long) PQfformat(res, i);
+ /* this is reserved for future use, so we leave it empty for the time being */
+ /* sqlda->sqlvar[i].sqlformat = (char *) (long) PQfformat(res, i);*/
sqlda->sqlvar[i].sqlxid = PQftype(res, i);
sqlda->sqlvar[i].sqltypelen = PQfsize(res, i);
}
diff --git a/src/makefiles/pgxs.mk b/src/makefiles/pgxs.mk
index 87ade0a420..7fb007fb1c 100644
--- a/src/makefiles/pgxs.mk
+++ b/src/makefiles/pgxs.mk
@@ -281,13 +281,14 @@ endif
installcheck: submake
$(pg_regress_installcheck) $(REGRESS_OPTS) $(REGRESS)
-# in-tree test doesn't work yet (no way to install my shared library)
-#check: all submake
-# $(pg_regress_check) $(REGRESS_OPTS) $(REGRESS)
+ifdef PGXS
check:
- @echo "'make check' is not supported."
- @echo "Do 'make install', then 'make installcheck' instead."
- @exit 1
+ @echo '"$(MAKE) check" is not supported.'
+ @echo 'Do "$(MAKE) install", then "$(MAKE) installcheck" instead.'
+else
+check: all submake
+ $(pg_regress_check) --extra-install=$(subdir) $(REGRESS_OPTS) $(REGRESS)
+endif
endif # REGRESS
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index ac9134272c..b5418074ae 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -3551,7 +3551,7 @@ hv_store_string(HV *hv, const char *key, SV *val)
* does not appear that hashes track UTF-8-ness of keys at all in Perl
* 5.6.
*/
- hlen = -strlen(hkey);
+ hlen = - (int) strlen(hkey);
ret = hv_store(hv, hkey, hlen, val, 0);
if (hkey != key)
@@ -3576,7 +3576,7 @@ hv_fetch_string(HV *hv, const char *key)
GetDatabaseEncoding(), PG_UTF8);
/* See notes in hv_store_string */
- hlen = -strlen(hkey);
+ hlen = - (int) strlen(hkey);
ret = hv_fetch(hv, hkey, hlen, 0);
if (hkey != key)
diff --git a/src/pl/plperl/plperl.h b/src/pl/plperl/plperl.h
index 5eaec73d2b..a375bb5e31 100644
--- a/src/pl/plperl/plperl.h
+++ b/src/pl/plperl/plperl.h
@@ -26,11 +26,47 @@
#endif
#endif
+/*
+ * Supply a value of PERL_UNUSED_DECL that will satisfy gcc - the one
+ * perl itself supplies doesn't seem to.
+ */
+#if defined(__GNUC__)
+#define PERL_UNUSED_DECL __attribute__ ((unused))
+#endif
+
+/*
+ * Sometimes perl carefully scribbles on our *printf macros.
+ * So we undefine them here and redefine them after it's done its dirty deed.
+ */
+
+#ifdef USE_REPL_SNPRINTF
+#undef snprintf
+#undef vsnprintf
+#endif
+
+
/* required for perl API */
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
+/* put back our snprintf and vsnprintf */
+#ifdef USE_REPL_SNPRINTF
+#ifdef snprintf
+#undef snprintf
+#endif
+#ifdef vsnprintf
+#undef vsnprintf
+#endif
+#ifdef __GNUC__
+#define vsnprintf(...) pg_vsnprintf(__VA_ARGS__)
+#define snprintf(...) pg_snprintf(__VA_ARGS__)
+#else
+#define vsnprintf pg_vsnprintf
+#define snprintf pg_snprintf
+#endif /* __GNUC__ */
+#endif /* USE_REPL_SNPRINTF */
+
/* perl version and platform portability */
#define NEED_eval_pv
#define NEED_newRV_noinc
diff --git a/src/port/getopt.c b/src/port/getopt.c
index aacfbc3e54..e901bf7db5 100644
--- a/src/port/getopt.c
+++ b/src/port/getopt.c
@@ -61,6 +61,8 @@ extern char *optarg;
#define BADARG (int)':'
#define EMSG ""
+int getopt(int nargc, char *const * nargv, const char * ostr);
+
/*
* getopt
* Parse argc/argv argument vector.
@@ -72,10 +74,7 @@ extern char *optarg;
* returning -1.)
*/
int
-getopt(nargc, nargv, ostr)
-int nargc;
-char *const * nargv;
-const char *ostr;
+getopt(int nargc, char *const * nargv, const char * ostr)
{
static char *place = EMSG; /* option letter processing */
char *oli; /* option letter list index */
diff --git a/src/port/noblock.c b/src/port/noblock.c
index 9a90f6826e..883697535d 100644
--- a/src/port/noblock.c
+++ b/src/port/noblock.c
@@ -23,7 +23,7 @@ pg_set_noblock(pgsocket sock)
#if !defined(WIN32)
return (fcntl(sock, F_SETFL, O_NONBLOCK) != -1);
#else
- long ioctlsocket_ret = 1;
+ unsigned long ioctlsocket_ret = 1;
/* Returns non-0 on failure, while fcntl() returns -1 on failure */
return (ioctlsocket(sock, FIONBIO, &ioctlsocket_ret) == 0);
@@ -42,7 +42,7 @@ pg_set_block(pgsocket sock)
return false;
return true;
#else
- long ioctlsocket_ret = 0;
+ unsigned long ioctlsocket_ret = 0;
/* Returns non-0 on failure, while fcntl() returns -1 on failure */
return (ioctlsocket(sock, FIONBIO, &ioctlsocket_ret) == 0);
diff --git a/src/test/regress/expected/collate.linux.utf8.out b/src/test/regress/expected/collate.linux.utf8.out
index 9813b6847c..c065ae98eb 100644
--- a/src/test/regress/expected/collate.linux.utf8.out
+++ b/src/test/regress/expected/collate.linux.utf8.out
@@ -1,6 +1,7 @@
/*
* 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.
+ * locales is installed. It must be run in a database with UTF-8 encoding,
+ * because other encodings don't support all the characters used.
*/
SET client_encoding TO UTF8;
CREATE TABLE collate_test1 (
diff --git a/src/test/regress/expected/delete.out b/src/test/regress/expected/delete.out
index 604d067f22..d3d1a357c0 100644
--- a/src/test/regress/expected/delete.out
+++ b/src/test/regress/expected/delete.out
@@ -1,11 +1,12 @@
CREATE TABLE delete_test (
id SERIAL PRIMARY KEY,
- a INT
+ a INT,
+ b text
);
NOTICE: CREATE TABLE will create implicit sequence "delete_test_id_seq" for serial column "delete_test.id"
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "delete_test_pkey" for table "delete_test"
INSERT INTO delete_test (a) VALUES (10);
-INSERT INTO delete_test (a) VALUES (50);
+INSERT INTO delete_test (a, b) VALUES (50, repeat('x', 10000));
INSERT INTO delete_test (a) VALUES (100);
-- allow an alias to be specified for DELETE's target table
DELETE FROM delete_test AS dt WHERE dt.a > 75;
@@ -16,11 +17,19 @@ ERROR: invalid reference to FROM-clause entry for table "delete_test"
LINE 1: DELETE FROM delete_test dt WHERE delete_test.a > 25;
^
HINT: Perhaps you meant to reference the table alias "dt".
-SELECT * FROM delete_test;
- id | a
-----+----
- 1 | 10
- 2 | 50
+SELECT id, a, char_length(b) FROM delete_test;
+ id | a | char_length
+----+----+-------------
+ 1 | 10 |
+ 2 | 50 | 10000
(2 rows)
+-- delete a row with a TOASTed value
+DELETE FROM delete_test WHERE a > 25;
+SELECT id, a, char_length(b) FROM delete_test;
+ id | a | char_length
+----+----+-------------
+ 1 | 10 |
+(1 row)
+
DROP TABLE delete_test;
diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out
index c05bcabb71..0dc7d045c7 100644
--- a/src/test/regress/expected/foreign_data.out
+++ b/src/test/regress/expected/foreign_data.out
@@ -1086,13 +1086,12 @@ DROP SERVER t1 CASCADE;
NOTICE: drop cascades to user mapping for public
DROP SERVER t2;
DROP USER MAPPING FOR regress_test_role SERVER s6;
+-- This test causes some order dependent cascade detail output,
+-- so switch to terse mode for it.
+\set VERBOSITY terse
DROP FOREIGN DATA WRAPPER foo CASCADE;
NOTICE: drop cascades to 5 other objects
-DETAIL: drop cascades to server s4
-drop cascades to user mapping for foreign_data_user
-drop cascades to server s6
-drop cascades to server s9
-drop cascades to user mapping for unprivileged_role
+\set VERBOSITY default
DROP SERVER s8 CASCADE;
NOTICE: drop cascades to 2 other objects
DETAIL: drop cascades to user mapping for foreign_data_user
diff --git a/src/test/regress/expected/insert.out b/src/test/regress/expected/insert.out
index d8510ea334..50bcead449 100644
--- a/src/test/regress/expected/insert.out
+++ b/src/test/regress/expected/insert.out
@@ -62,4 +62,21 @@ select * from inserttest;
2 | 3 | values are fun!
(7 rows)
+--
+-- TOASTed value test
+--
+insert into inserttest values(30, 50, repeat('x', 10000));
+select col1, col2, char_length(col3) from inserttest;
+ col1 | col2 | char_length
+------+------+-------------
+ | 3 | 7
+ | 5 | 7
+ | 5 | 4
+ | 7 | 7
+ 10 | 20 | 2
+ -1 | 2 | 7
+ 2 | 3 | 15
+ 30 | 50 | 10000
+(8 rows)
+
drop table inserttest;
diff --git a/src/test/regress/expected/oidjoins.out b/src/test/regress/expected/oidjoins.out
index 9fa0f845cf..a7426dde73 100644
--- a/src/test/regress/expected/oidjoins.out
+++ b/src/test/regress/expected/oidjoins.out
@@ -121,6 +121,14 @@ WHERE ambuild != 0 AND
------+---------
(0 rows)
+SELECT ctid, ambuildempty
+FROM pg_catalog.pg_am fk
+WHERE ambuildempty != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ambuildempty);
+ ctid | ambuildempty
+------+--------------
+(0 rows)
+
SELECT ctid, ambulkdelete
FROM pg_catalog.pg_am fk
WHERE ambulkdelete != 0 AND
@@ -193,6 +201,14 @@ WHERE amopmethod != 0 AND
------+------------
(0 rows)
+SELECT ctid, amopsortfamily
+FROM pg_catalog.pg_amop fk
+WHERE amopsortfamily != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_opfamily pk WHERE pk.oid = fk.amopsortfamily);
+ ctid | amopsortfamily
+------+----------------
+(0 rows)
+
SELECT ctid, amprocfamily
FROM pg_catalog.pg_amproc fk
WHERE amprocfamily != 0 AND
@@ -225,6 +241,14 @@ WHERE amproc != 0 AND
------+--------
(0 rows)
+SELECT ctid, adrelid
+FROM pg_catalog.pg_attrdef fk
+WHERE adrelid != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.adrelid);
+ ctid | adrelid
+------+---------
+(0 rows)
+
SELECT ctid, attrelid
FROM pg_catalog.pg_attribute fk
WHERE attrelid != 0 AND
@@ -241,6 +265,14 @@ WHERE atttypid != 0 AND
------+----------
(0 rows)
+SELECT ctid, attcollation
+FROM pg_catalog.pg_attribute fk
+WHERE attcollation != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_collation pk WHERE pk.oid = fk.attcollation);
+ ctid | attcollation
+------+--------------
+(0 rows)
+
SELECT ctid, castsource
FROM pg_catalog.pg_cast fk
WHERE castsource != 0 AND
@@ -281,6 +313,14 @@ WHERE reltype != 0 AND
------+---------
(0 rows)
+SELECT ctid, reloftype
+FROM pg_catalog.pg_class fk
+WHERE reloftype != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.reloftype);
+ ctid | reloftype
+------+-----------
+(0 rows)
+
SELECT ctid, relowner
FROM pg_catalog.pg_class fk
WHERE relowner != 0 AND
@@ -321,6 +361,22 @@ WHERE reltoastidxid != 0 AND
------+---------------
(0 rows)
+SELECT ctid, collnamespace
+FROM pg_catalog.pg_collation fk
+WHERE collnamespace != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.collnamespace);
+ ctid | collnamespace
+------+---------------
+(0 rows)
+
+SELECT ctid, collowner
+FROM pg_catalog.pg_collation fk
+WHERE collowner != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.collowner);
+ ctid | collowner
+------+-----------
+(0 rows)
+
SELECT ctid, connamespace
FROM pg_catalog.pg_constraint fk
WHERE connamespace != 0 AND
@@ -329,6 +385,14 @@ WHERE connamespace != 0 AND
------+--------------
(0 rows)
+SELECT ctid, conrelid
+FROM pg_catalog.pg_constraint fk
+WHERE conrelid != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.conrelid);
+ ctid | conrelid
+------+----------
+(0 rows)
+
SELECT ctid, contypid
FROM pg_catalog.pg_constraint fk
WHERE contypid != 0 AND
@@ -337,6 +401,22 @@ WHERE contypid != 0 AND
------+----------
(0 rows)
+SELECT ctid, conindid
+FROM pg_catalog.pg_constraint fk
+WHERE conindid != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.conindid);
+ ctid | conindid
+------+----------
+(0 rows)
+
+SELECT ctid, confrelid
+FROM pg_catalog.pg_constraint fk
+WHERE confrelid != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.confrelid);
+ ctid | confrelid
+------+-----------
+(0 rows)
+
SELECT ctid, connamespace
FROM pg_catalog.pg_conversion fk
WHERE connamespace != 0 AND
@@ -409,6 +489,30 @@ WHERE classoid != 0 AND
------+----------
(0 rows)
+SELECT ctid, enumtypid
+FROM pg_catalog.pg_enum fk
+WHERE enumtypid != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.enumtypid);
+ ctid | enumtypid
+------+-----------
+(0 rows)
+
+SELECT ctid, extowner
+FROM pg_catalog.pg_extension fk
+WHERE extowner != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.extowner);
+ ctid | extowner
+------+----------
+(0 rows)
+
+SELECT ctid, extnamespace
+FROM pg_catalog.pg_extension fk
+WHERE extnamespace != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.extnamespace);
+ ctid | extnamespace
+------+--------------
+(0 rows)
+
SELECT ctid, indexrelid
FROM pg_catalog.pg_index fk
WHERE indexrelid != 0 AND
@@ -425,6 +529,22 @@ WHERE indrelid != 0 AND
------+----------
(0 rows)
+SELECT ctid, inhrelid
+FROM pg_catalog.pg_inherits fk
+WHERE inhrelid != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.inhrelid);
+ ctid | inhrelid
+------+----------
+(0 rows)
+
+SELECT ctid, inhparent
+FROM pg_catalog.pg_inherits fk
+WHERE inhparent != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.inhparent);
+ ctid | inhparent
+------+-----------
+(0 rows)
+
SELECT ctid, lanowner
FROM pg_catalog.pg_language fk
WHERE lanowner != 0 AND
@@ -641,6 +761,14 @@ WHERE prolang != 0 AND
------+---------
(0 rows)
+SELECT ctid, provariadic
+FROM pg_catalog.pg_proc fk
+WHERE provariadic != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.provariadic);
+ ctid | provariadic
+------+-------------
+(0 rows)
+
SELECT ctid, prorettype
FROM pg_catalog.pg_proc fk
WHERE prorettype != 0 AND
@@ -713,6 +841,46 @@ WHERE spcowner != 0 AND
------+----------
(0 rows)
+SELECT ctid, tgrelid
+FROM pg_catalog.pg_trigger fk
+WHERE tgrelid != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.tgrelid);
+ ctid | tgrelid
+------+---------
+(0 rows)
+
+SELECT ctid, tgfoid
+FROM pg_catalog.pg_trigger fk
+WHERE tgfoid != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.tgfoid);
+ ctid | tgfoid
+------+--------
+(0 rows)
+
+SELECT ctid, tgconstrrelid
+FROM pg_catalog.pg_trigger fk
+WHERE tgconstrrelid != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.tgconstrrelid);
+ ctid | tgconstrrelid
+------+---------------
+(0 rows)
+
+SELECT ctid, tgconstrindid
+FROM pg_catalog.pg_trigger fk
+WHERE tgconstrindid != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.tgconstrindid);
+ ctid | tgconstrindid
+------+---------------
+(0 rows)
+
+SELECT ctid, tgconstraint
+FROM pg_catalog.pg_trigger fk
+WHERE tgconstraint != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_constraint pk WHERE pk.oid = fk.tgconstraint);
+ ctid | tgconstraint
+------+--------------
+(0 rows)
+
SELECT ctid, cfgnamespace
FROM pg_catalog.pg_ts_config fk
WHERE cfgnamespace != 0 AND
@@ -953,3 +1121,43 @@ WHERE typbasetype != 0 AND
------+-------------
(0 rows)
+SELECT ctid, typcollation
+FROM pg_catalog.pg_type fk
+WHERE typcollation != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_collation pk WHERE pk.oid = fk.typcollation);
+ ctid | typcollation
+------+--------------
+(0 rows)
+
+SELECT ctid, conpfeqop
+FROM (SELECT ctid, unnest(conpfeqop) AS conpfeqop FROM pg_catalog.pg_constraint) fk
+WHERE conpfeqop != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.conpfeqop);
+ ctid | conpfeqop
+------+-----------
+(0 rows)
+
+SELECT ctid, conppeqop
+FROM (SELECT ctid, unnest(conppeqop) AS conppeqop FROM pg_catalog.pg_constraint) fk
+WHERE conppeqop != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.conppeqop);
+ ctid | conppeqop
+------+-----------
+(0 rows)
+
+SELECT ctid, conffeqop
+FROM (SELECT ctid, unnest(conffeqop) AS conffeqop FROM pg_catalog.pg_constraint) fk
+WHERE conffeqop != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.conffeqop);
+ ctid | conffeqop
+------+-----------
+(0 rows)
+
+SELECT ctid, proallargtypes
+FROM (SELECT ctid, unnest(proallargtypes) AS proallargtypes FROM pg_catalog.pg_proc) fk
+WHERE proallargtypes != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.proallargtypes);
+ ctid | proallargtypes
+------+----------------
+(0 rows)
+
diff --git a/src/test/regress/expected/update.out b/src/test/regress/expected/update.out
index 02aeccc735..71b856f95c 100644
--- a/src/test/regress/expected/update.out
+++ b/src/test/regress/expected/update.out
@@ -87,4 +87,13 @@ ERROR: invalid reference to FROM-clause entry for table "update_test"
LINE 1: UPDATE update_test AS t SET b = update_test.b + 10 WHERE t.a...
^
HINT: Perhaps you meant to reference the table alias "t".
+-- Make sure that we can update to a TOASTed value.
+UPDATE update_test SET c = repeat('x', 10000) WHERE c = 'car';
+SELECT a, b, char_length(c) FROM update_test;
+ a | b | char_length
+-----+----+-------------
+ 100 | 20 |
+ 11 | 41 | 10000
+(2 rows)
+
DROP TABLE update_test;
diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index 5fe3724c82..23ef0a94e5 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -102,6 +102,7 @@ static bool port_specified_by_user = false;
static char *dlpath = PKGLIBDIR;
static char *user = NULL;
static _stringlist *extraroles = NULL;
+static _stringlist *extra_install = NULL;
/* internal variables */
static const char *progname;
@@ -140,9 +141,11 @@ __attribute__((format(printf, 2, 3)));
#ifdef WIN32
typedef BOOL (WINAPI * __CreateRestrictedToken) (HANDLE, DWORD, DWORD, PSID_AND_ATTRIBUTES, DWORD, PLUID_AND_ATTRIBUTES, DWORD, PSID_AND_ATTRIBUTES, PHANDLE);
-/* Windows API define missing from MingW headers */
+/* Windows API define missing from some versions of MingW headers */
+#ifndef DISABLE_MAX_PRIVILEGE
#define DISABLE_MAX_PRIVILEGE 0x1
#endif
+#endif
/*
* allow core files if possible.
@@ -1894,6 +1897,7 @@ help(void)
printf(_(" --top-builddir=DIR (relative) path to top level build directory\n"));
printf(_(" --port=PORT start postmaster on PORT\n"));
printf(_(" --temp-config=PATH append contents of PATH to temporary config\n"));
+ printf(_(" --extra-install=DIR additional directory to install (e.g., contrib\n"));
printf(_("\n"));
printf(_("Options for using an existing installation:\n"));
printf(_(" --host=HOST use postmaster running on HOST\n"));
@@ -1941,6 +1945,7 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
{"use-existing", no_argument, NULL, 20},
{"launcher", required_argument, NULL, 21},
{"load-extension", required_argument, NULL, 22},
+ {"extra-install", required_argument, NULL, 23},
{NULL, 0, NULL, 0}
};
@@ -2040,6 +2045,9 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
case 22:
add_stringlist_item(&loadextension, optarg);
break;
+ case 23:
+ add_stringlist_item(&extra_install, optarg);
+ break;
default:
/* getopt_long already emitted a complaint */
fprintf(stderr, _("\nTry \"%s -h\" for more information.\n"),
@@ -2084,6 +2092,7 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
if (temp_install)
{
FILE *pg_conf;
+ _stringlist *sl;
/*
* Prepare the temp installation
@@ -2126,6 +2135,24 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc
exit_nicely(2);
}
+ for (sl = extra_install; sl != NULL; sl = sl->next)
+ {
+#ifndef WIN32_ONLY_COMPILER
+ snprintf(buf, sizeof(buf),
+ SYSTEMQUOTE "\"%s\" -C \"%s/%s\" DESTDIR=\"%s/install\" install >> \"%s/log/install.log\" 2>&1" SYSTEMQUOTE,
+ makeprog, top_builddir, sl->str, temp_install, outputdir);
+#else
+ fprintf(stderr, _("\n%s: --extra-install option not supported on this platform\n", progname));
+ exit_nicely(2);
+#endif
+
+ if (system(buf))
+ {
+ fprintf(stderr, _("\n%s: installation failed\nExamine %s/log/install.log for the reason.\nCommand was: %s\n"), progname, outputdir, buf);
+ exit_nicely(2);
+ }
+ }
+
/* initdb */
header(_("initializing database system"));
snprintf(buf, sizeof(buf),
diff --git a/src/test/regress/sql/collate.linux.utf8.sql b/src/test/regress/sql/collate.linux.utf8.sql
index dfb10e4d15..e16d96a331 100644
--- a/src/test/regress/sql/collate.linux.utf8.sql
+++ b/src/test/regress/sql/collate.linux.utf8.sql
@@ -1,6 +1,7 @@
/*
* 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.
+ * locales is installed. It must be run in a database with UTF-8 encoding,
+ * because other encodings don't support all the characters used.
*/
SET client_encoding TO UTF8;
diff --git a/src/test/regress/sql/delete.sql b/src/test/regress/sql/delete.sql
index 6b870bbbd5..d8cb99e93c 100644
--- a/src/test/regress/sql/delete.sql
+++ b/src/test/regress/sql/delete.sql
@@ -1,10 +1,11 @@
CREATE TABLE delete_test (
id SERIAL PRIMARY KEY,
- a INT
+ a INT,
+ b text
);
INSERT INTO delete_test (a) VALUES (10);
-INSERT INTO delete_test (a) VALUES (50);
+INSERT INTO delete_test (a, b) VALUES (50, repeat('x', 10000));
INSERT INTO delete_test (a) VALUES (100);
-- allow an alias to be specified for DELETE's target table
@@ -14,6 +15,11 @@ DELETE FROM delete_test AS dt WHERE dt.a > 75;
-- to be referenced
DELETE FROM delete_test dt WHERE delete_test.a > 25;
-SELECT * FROM delete_test;
+SELECT id, a, char_length(b) FROM delete_test;
+
+-- delete a row with a TOASTed value
+DELETE FROM delete_test WHERE a > 25;
+
+SELECT id, a, char_length(b) FROM delete_test;
DROP TABLE delete_test;
diff --git a/src/test/regress/sql/foreign_data.sql b/src/test/regress/sql/foreign_data.sql
index 0d12b98e21..d3239216c1 100644
--- a/src/test/regress/sql/foreign_data.sql
+++ b/src/test/regress/sql/foreign_data.sql
@@ -443,7 +443,11 @@ DROP SERVER s5 CASCADE;
DROP SERVER t1 CASCADE;
DROP SERVER t2;
DROP USER MAPPING FOR regress_test_role SERVER s6;
+-- This test causes some order dependent cascade detail output,
+-- so switch to terse mode for it.
+\set VERBOSITY terse
DROP FOREIGN DATA WRAPPER foo CASCADE;
+\set VERBOSITY default
DROP SERVER s8 CASCADE;
DROP ROLE regress_test_indirect;
DROP ROLE regress_test_role;
diff --git a/src/test/regress/sql/insert.sql b/src/test/regress/sql/insert.sql
index 8a9ccce2ea..a0ae85003f 100644
--- a/src/test/regress/sql/insert.sql
+++ b/src/test/regress/sql/insert.sql
@@ -28,4 +28,11 @@ insert into inserttest values(10, 20, '40'), (-1, 2, DEFAULT),
select * from inserttest;
+--
+-- TOASTed value test
+--
+insert into inserttest values(30, 50, repeat('x', 10000));
+
+select col1, col2, char_length(col3) from inserttest;
+
drop table inserttest;
diff --git a/src/test/regress/sql/oidjoins.sql b/src/test/regress/sql/oidjoins.sql
index 995271b690..20ca7edd3b 100644
--- a/src/test/regress/sql/oidjoins.sql
+++ b/src/test/regress/sql/oidjoins.sql
@@ -61,6 +61,10 @@ SELECT ctid, ambuild
FROM pg_catalog.pg_am fk
WHERE ambuild != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ambuild);
+SELECT ctid, ambuildempty
+FROM pg_catalog.pg_am fk
+WHERE ambuildempty != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.ambuildempty);
SELECT ctid, ambulkdelete
FROM pg_catalog.pg_am fk
WHERE ambulkdelete != 0 AND
@@ -97,6 +101,10 @@ SELECT ctid, amopmethod
FROM pg_catalog.pg_amop fk
WHERE amopmethod != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_am pk WHERE pk.oid = fk.amopmethod);
+SELECT ctid, amopsortfamily
+FROM pg_catalog.pg_amop fk
+WHERE amopsortfamily != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_opfamily pk WHERE pk.oid = fk.amopsortfamily);
SELECT ctid, amprocfamily
FROM pg_catalog.pg_amproc fk
WHERE amprocfamily != 0 AND
@@ -113,6 +121,10 @@ SELECT ctid, amproc
FROM pg_catalog.pg_amproc fk
WHERE amproc != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.amproc);
+SELECT ctid, adrelid
+FROM pg_catalog.pg_attrdef fk
+WHERE adrelid != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.adrelid);
SELECT ctid, attrelid
FROM pg_catalog.pg_attribute fk
WHERE attrelid != 0 AND
@@ -121,6 +133,10 @@ SELECT ctid, atttypid
FROM pg_catalog.pg_attribute fk
WHERE atttypid != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.atttypid);
+SELECT ctid, attcollation
+FROM pg_catalog.pg_attribute fk
+WHERE attcollation != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_collation pk WHERE pk.oid = fk.attcollation);
SELECT ctid, castsource
FROM pg_catalog.pg_cast fk
WHERE castsource != 0 AND
@@ -141,6 +157,10 @@ SELECT ctid, reltype
FROM pg_catalog.pg_class fk
WHERE reltype != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.reltype);
+SELECT ctid, reloftype
+FROM pg_catalog.pg_class fk
+WHERE reloftype != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.reloftype);
SELECT ctid, relowner
FROM pg_catalog.pg_class fk
WHERE relowner != 0 AND
@@ -161,14 +181,34 @@ SELECT ctid, reltoastidxid
FROM pg_catalog.pg_class fk
WHERE reltoastidxid != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.reltoastidxid);
+SELECT ctid, collnamespace
+FROM pg_catalog.pg_collation fk
+WHERE collnamespace != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.collnamespace);
+SELECT ctid, collowner
+FROM pg_catalog.pg_collation fk
+WHERE collowner != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.collowner);
SELECT ctid, connamespace
FROM pg_catalog.pg_constraint fk
WHERE connamespace != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.connamespace);
+SELECT ctid, conrelid
+FROM pg_catalog.pg_constraint fk
+WHERE conrelid != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.conrelid);
SELECT ctid, contypid
FROM pg_catalog.pg_constraint fk
WHERE contypid != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.contypid);
+SELECT ctid, conindid
+FROM pg_catalog.pg_constraint fk
+WHERE conindid != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.conindid);
+SELECT ctid, confrelid
+FROM pg_catalog.pg_constraint fk
+WHERE confrelid != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.confrelid);
SELECT ctid, connamespace
FROM pg_catalog.pg_conversion fk
WHERE connamespace != 0 AND
@@ -205,6 +245,18 @@ SELECT ctid, classoid
FROM pg_catalog.pg_description fk
WHERE classoid != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.classoid);
+SELECT ctid, enumtypid
+FROM pg_catalog.pg_enum fk
+WHERE enumtypid != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.enumtypid);
+SELECT ctid, extowner
+FROM pg_catalog.pg_extension fk
+WHERE extowner != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.extowner);
+SELECT ctid, extnamespace
+FROM pg_catalog.pg_extension fk
+WHERE extnamespace != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_namespace pk WHERE pk.oid = fk.extnamespace);
SELECT ctid, indexrelid
FROM pg_catalog.pg_index fk
WHERE indexrelid != 0 AND
@@ -213,6 +265,14 @@ SELECT ctid, indrelid
FROM pg_catalog.pg_index fk
WHERE indrelid != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.indrelid);
+SELECT ctid, inhrelid
+FROM pg_catalog.pg_inherits fk
+WHERE inhrelid != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.inhrelid);
+SELECT ctid, inhparent
+FROM pg_catalog.pg_inherits fk
+WHERE inhparent != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.inhparent);
SELECT ctid, lanowner
FROM pg_catalog.pg_language fk
WHERE lanowner != 0 AND
@@ -321,6 +381,10 @@ SELECT ctid, prolang
FROM pg_catalog.pg_proc fk
WHERE prolang != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_language pk WHERE pk.oid = fk.prolang);
+SELECT ctid, provariadic
+FROM pg_catalog.pg_proc fk
+WHERE provariadic != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.provariadic);
SELECT ctid, prorettype
FROM pg_catalog.pg_proc fk
WHERE prorettype != 0 AND
@@ -357,6 +421,26 @@ SELECT ctid, spcowner
FROM pg_catalog.pg_tablespace fk
WHERE spcowner != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_authid pk WHERE pk.oid = fk.spcowner);
+SELECT ctid, tgrelid
+FROM pg_catalog.pg_trigger fk
+WHERE tgrelid != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.tgrelid);
+SELECT ctid, tgfoid
+FROM pg_catalog.pg_trigger fk
+WHERE tgfoid != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.tgfoid);
+SELECT ctid, tgconstrrelid
+FROM pg_catalog.pg_trigger fk
+WHERE tgconstrrelid != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.tgconstrrelid);
+SELECT ctid, tgconstrindid
+FROM pg_catalog.pg_trigger fk
+WHERE tgconstrindid != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_class pk WHERE pk.oid = fk.tgconstrindid);
+SELECT ctid, tgconstraint
+FROM pg_catalog.pg_trigger fk
+WHERE tgconstraint != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_constraint pk WHERE pk.oid = fk.tgconstraint);
SELECT ctid, cfgnamespace
FROM pg_catalog.pg_ts_config fk
WHERE cfgnamespace != 0 AND
@@ -477,3 +561,23 @@ SELECT ctid, typbasetype
FROM pg_catalog.pg_type fk
WHERE typbasetype != 0 AND
NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.typbasetype);
+SELECT ctid, typcollation
+FROM pg_catalog.pg_type fk
+WHERE typcollation != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_collation pk WHERE pk.oid = fk.typcollation);
+SELECT ctid, conpfeqop
+FROM (SELECT ctid, unnest(conpfeqop) AS conpfeqop FROM pg_catalog.pg_constraint) fk
+WHERE conpfeqop != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.conpfeqop);
+SELECT ctid, conppeqop
+FROM (SELECT ctid, unnest(conppeqop) AS conppeqop FROM pg_catalog.pg_constraint) fk
+WHERE conppeqop != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.conppeqop);
+SELECT ctid, conffeqop
+FROM (SELECT ctid, unnest(conffeqop) AS conffeqop FROM pg_catalog.pg_constraint) fk
+WHERE conffeqop != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_operator pk WHERE pk.oid = fk.conffeqop);
+SELECT ctid, proallargtypes
+FROM (SELECT ctid, unnest(proallargtypes) AS proallargtypes FROM pg_catalog.pg_proc) fk
+WHERE proallargtypes != 0 AND
+ NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type pk WHERE pk.oid = fk.proallargtypes);
diff --git a/src/test/regress/sql/update.sql b/src/test/regress/sql/update.sql
index b378d584a4..a8a028f710 100644
--- a/src/test/regress/sql/update.sql
+++ b/src/test/regress/sql/update.sql
@@ -54,4 +54,8 @@ UPDATE update_test SET (a,b) = (select a,b FROM update_test where c = 'foo')
-- to the original table name
UPDATE update_test AS t SET b = update_test.b + 10 WHERE t.a = 10;
+-- Make sure that we can update to a TOASTed value.
+UPDATE update_test SET c = repeat('x', 10000) WHERE c = 'car';
+SELECT a, b, char_length(c) FROM update_test;
+
DROP TABLE update_test;
diff --git a/src/timezone/pgtz.c b/src/timezone/pgtz.c
index 382a4e04f5..cb66f6380b 100644
--- a/src/timezone/pgtz.c
+++ b/src/timezone/pgtz.c
@@ -1158,7 +1158,7 @@ identify_system_timezone(void)
memset(zonename, 0, sizeof(zonename));
namesize = sizeof(zonename);
- if ((r = RegQueryValueEx(key, "Std", NULL, NULL, zonename, &namesize)) != ERROR_SUCCESS)
+ if ((r = RegQueryValueEx(key, "Std", NULL, NULL, (unsigned char *) zonename, &namesize)) != ERROR_SUCCESS)
{
ereport(LOG,
(errmsg_internal("could not query value for key \"std\" to identify system time zone \"%s\": %i",
@@ -1175,7 +1175,7 @@ identify_system_timezone(void)
}
memset(zonename, 0, sizeof(zonename));
namesize = sizeof(zonename);
- if ((r = RegQueryValueEx(key, "Dlt", NULL, NULL, zonename, &namesize)) != ERROR_SUCCESS)
+ if ((r = RegQueryValueEx(key, "Dlt", NULL, NULL, (unsigned char *) zonename, &namesize)) != ERROR_SUCCESS)
{
ereport(LOG,
(errmsg_internal("could not query value for key \"dlt\" to identify system time zone \"%s\": %i",
diff --git a/src/tools/editors/emacs.samples b/src/tools/editors/emacs.samples
index f755843d40..d9cfa2ffd8 100644
--- a/src/tools/editors/emacs.samples
+++ b/src/tools/editors/emacs.samples
@@ -28,6 +28,7 @@
(setq-default tab-width 4)
(c-set-style "bsd") ; set c-basic-offset to 4, plus other stuff
(c-set-offset 'case-label '+) ; tweak case indent to match PG custom
+ (setq fill-column 79) ; matches what pgindent does
(setq indent-tabs-mode t)) ; make sure we keep tabs when indenting
@@ -38,6 +39,7 @@
(c-add-style "pgsql"
'("bsd"
+ (fill-column . 79)
(indent-tabs-mode . t)
(c-basic-offset . 4)
(tab-width . 4)
@@ -65,12 +67,13 @@
(add-hook 'c-mode-hook
(function
(lambda nil
- (if (string-match "pgsql" buffer-file-name)
+ (if (string-match "postgresql" buffer-file-name)
(progn
(c-set-style "bsd")
(setq c-basic-offset 4)
(setq tab-width 4)
(c-set-offset 'case-label '+)
+ (setq fill-column 79)
(setq indent-tabs-mode t)
)
))))
diff --git a/src/tools/findoidjoins/README b/src/tools/findoidjoins/README
index 0685d6cf21..c4a96e3c4b 100644
--- a/src/tools/findoidjoins/README
+++ b/src/tools/findoidjoins/README
@@ -4,39 +4,41 @@ findoidjoins
============
This program scans a database and prints oid fields (also reg* fields)
-and the tables they join to. We don't really recommend running it on
-anything but an empty database, such as template1; else it's likely to
-be very slow.
+and the tables they join to. It is normally used to check the system
+catalog join relationships (shown below for 9.1devel).
-Run on an empty database, it returns the system join relationships (shown
-below for 9.0devel). Note that unexpected matches may indicate bogus entries
-in system tables --- don't accept a peculiar match without question.
-In particular, a field shown as joining to more than one target table is
-probably messed up. In 9.0devel, the *only* fields that should join to more
-than one target are pg_description.objoid, pg_depend.objid,
-pg_depend.refobjid, pg_shdescription.objoid, pg_shdepend.objid, and
-pg_shdepend.refobjid. (Running make_oidjoins_check is an easy way to spot
-fields joining to more than one table, BTW.) NOTE: in an empty database,
-findoidjoins may not report joins for pg_shdescription and pg_shdepend for
-lack of any entries there.
+Historically this has been run against an empty database such as template1,
+but there's a problem with that approach: some of the catalogs are empty
+and so their joining columns won't show up in the output. Current practice
+is to run it against the regression-test database, which populates the
+catalogs in interesting ways.
+
+Note that unexpected matches may indicate bogus entries in system tables;
+don't accept a peculiar match without question. In particular, a field
+shown as joining to more than one target table is probably messed up.
+In 9.1devel, the *only* fields that should join to more than one target
+table are pg_description.objoid, pg_depend.objid, pg_depend.refobjid,
+pg_shdescription.objoid, pg_shdepend.objid, and pg_shdepend.refobjid.
+(Running make_oidjoins_check is an easy way to spot fields joining to more
+than one table, BTW.)
The shell script make_oidjoins_check converts findoidjoins' output
into an SQL script that checks for dangling links (entries in an
OID or REG* column that don't match any row in the expected table).
-Note that fields joining to more than one table are NOT processed.
+Note that fields joining to more than one table are NOT processed,
+just reported as linking to more than one table.
The result of make_oidjoins_check should be installed as the "oidjoins"
regression test. The oidjoins test should be updated after any
revision in the patterns of cross-links between system tables.
-(Ideally we'd just regenerate the script as part of the regression
-tests themselves, but that seems too slow...)
+(Typically we update it at the end of each development cycle.)
-NOTE: in 9.0devel, make_oidjoins_check produces two bogus join checks:
+NOTE: as of 9.1devel, make_oidjoins_check produces two bogus join checks:
Join pg_catalog.pg_class.relfilenode => pg_catalog.pg_class.oid
Join pg_catalog.pg_database.datlastsysoid => pg_catalog.pg_database.oid
These are artifacts and should not be added to the oidjoins regress test.
You might also get output for pg_shdepend.refobjid and pg_shdescription.objoid,
-neither of which should be added.
+neither of which should be added to the regress test.
---------------------------------------------------------------------------
@@ -55,6 +57,7 @@ Join pg_catalog.pg_am.amendscan => pg_catalog.pg_proc.oid
Join pg_catalog.pg_am.ammarkpos => pg_catalog.pg_proc.oid
Join pg_catalog.pg_am.amrestrpos => pg_catalog.pg_proc.oid
Join pg_catalog.pg_am.ambuild => pg_catalog.pg_proc.oid
+Join pg_catalog.pg_am.ambuildempty => pg_catalog.pg_proc.oid
Join pg_catalog.pg_am.ambulkdelete => pg_catalog.pg_proc.oid
Join pg_catalog.pg_am.amvacuumcleanup => pg_catalog.pg_proc.oid
Join pg_catalog.pg_am.amcostestimate => pg_catalog.pg_proc.oid
@@ -64,24 +67,33 @@ Join pg_catalog.pg_amop.amoplefttype => pg_catalog.pg_type.oid
Join pg_catalog.pg_amop.amoprighttype => pg_catalog.pg_type.oid
Join pg_catalog.pg_amop.amopopr => pg_catalog.pg_operator.oid
Join pg_catalog.pg_amop.amopmethod => pg_catalog.pg_am.oid
+Join pg_catalog.pg_amop.amopsortfamily => pg_catalog.pg_opfamily.oid
Join pg_catalog.pg_amproc.amprocfamily => pg_catalog.pg_opfamily.oid
Join pg_catalog.pg_amproc.amproclefttype => pg_catalog.pg_type.oid
Join pg_catalog.pg_amproc.amprocrighttype => pg_catalog.pg_type.oid
Join pg_catalog.pg_amproc.amproc => pg_catalog.pg_proc.oid
+Join pg_catalog.pg_attrdef.adrelid => pg_catalog.pg_class.oid
Join pg_catalog.pg_attribute.attrelid => pg_catalog.pg_class.oid
Join pg_catalog.pg_attribute.atttypid => pg_catalog.pg_type.oid
+Join pg_catalog.pg_attribute.attcollation => pg_catalog.pg_collation.oid
Join pg_catalog.pg_cast.castsource => pg_catalog.pg_type.oid
Join pg_catalog.pg_cast.casttarget => pg_catalog.pg_type.oid
Join pg_catalog.pg_cast.castfunc => pg_catalog.pg_proc.oid
Join pg_catalog.pg_class.relnamespace => pg_catalog.pg_namespace.oid
Join pg_catalog.pg_class.reltype => pg_catalog.pg_type.oid
+Join pg_catalog.pg_class.reloftype => pg_catalog.pg_type.oid
Join pg_catalog.pg_class.relowner => pg_catalog.pg_authid.oid
Join pg_catalog.pg_class.relam => pg_catalog.pg_am.oid
Join pg_catalog.pg_class.reltablespace => pg_catalog.pg_tablespace.oid
Join pg_catalog.pg_class.reltoastrelid => pg_catalog.pg_class.oid
Join pg_catalog.pg_class.reltoastidxid => pg_catalog.pg_class.oid
+Join pg_catalog.pg_collation.collnamespace => pg_catalog.pg_namespace.oid
+Join pg_catalog.pg_collation.collowner => pg_catalog.pg_authid.oid
Join pg_catalog.pg_constraint.connamespace => pg_catalog.pg_namespace.oid
+Join pg_catalog.pg_constraint.conrelid => pg_catalog.pg_class.oid
Join pg_catalog.pg_constraint.contypid => pg_catalog.pg_type.oid
+Join pg_catalog.pg_constraint.conindid => pg_catalog.pg_class.oid
+Join pg_catalog.pg_constraint.confrelid => pg_catalog.pg_class.oid
Join pg_catalog.pg_conversion.connamespace => pg_catalog.pg_namespace.oid
Join pg_catalog.pg_conversion.conowner => pg_catalog.pg_authid.oid
Join pg_catalog.pg_conversion.conproc => pg_catalog.pg_proc.oid
@@ -91,8 +103,13 @@ Join pg_catalog.pg_db_role_setting.setdatabase => pg_catalog.pg_database.oid
Join pg_catalog.pg_depend.classid => pg_catalog.pg_class.oid
Join pg_catalog.pg_depend.refclassid => pg_catalog.pg_class.oid
Join pg_catalog.pg_description.classoid => pg_catalog.pg_class.oid
+Join pg_catalog.pg_enum.enumtypid => pg_catalog.pg_type.oid
+Join pg_catalog.pg_extension.extowner => pg_catalog.pg_authid.oid
+Join pg_catalog.pg_extension.extnamespace => pg_catalog.pg_namespace.oid
Join pg_catalog.pg_index.indexrelid => pg_catalog.pg_class.oid
Join pg_catalog.pg_index.indrelid => pg_catalog.pg_class.oid
+Join pg_catalog.pg_inherits.inhrelid => pg_catalog.pg_class.oid
+Join pg_catalog.pg_inherits.inhparent => pg_catalog.pg_class.oid
Join pg_catalog.pg_language.lanowner => pg_catalog.pg_authid.oid
Join pg_catalog.pg_language.lanplcallfoid => pg_catalog.pg_proc.oid
Join pg_catalog.pg_language.laninline => pg_catalog.pg_proc.oid
@@ -120,6 +137,7 @@ Join pg_catalog.pg_opfamily.opfowner => pg_catalog.pg_authid.oid
Join pg_catalog.pg_proc.pronamespace => pg_catalog.pg_namespace.oid
Join pg_catalog.pg_proc.proowner => pg_catalog.pg_authid.oid
Join pg_catalog.pg_proc.prolang => pg_catalog.pg_language.oid
+Join pg_catalog.pg_proc.provariadic => pg_catalog.pg_type.oid
Join pg_catalog.pg_proc.prorettype => pg_catalog.pg_type.oid
Join pg_catalog.pg_rewrite.ev_class => pg_catalog.pg_class.oid
Join pg_catalog.pg_shdepend.refclassid => pg_catalog.pg_class.oid
@@ -129,6 +147,11 @@ Join pg_catalog.pg_statistic.staop1 => pg_catalog.pg_operator.oid
Join pg_catalog.pg_statistic.staop2 => pg_catalog.pg_operator.oid
Join pg_catalog.pg_statistic.staop3 => pg_catalog.pg_operator.oid
Join pg_catalog.pg_tablespace.spcowner => pg_catalog.pg_authid.oid
+Join pg_catalog.pg_trigger.tgrelid => pg_catalog.pg_class.oid
+Join pg_catalog.pg_trigger.tgfoid => pg_catalog.pg_proc.oid
+Join pg_catalog.pg_trigger.tgconstrrelid => pg_catalog.pg_class.oid
+Join pg_catalog.pg_trigger.tgconstrindid => pg_catalog.pg_class.oid
+Join pg_catalog.pg_trigger.tgconstraint => pg_catalog.pg_constraint.oid
Join pg_catalog.pg_ts_config.cfgnamespace => pg_catalog.pg_namespace.oid
Join pg_catalog.pg_ts_config.cfgowner => pg_catalog.pg_authid.oid
Join pg_catalog.pg_ts_config.cfgparser => pg_catalog.pg_ts_parser.oid
@@ -159,8 +182,13 @@ Join pg_catalog.pg_type.typmodin => pg_catalog.pg_proc.oid
Join pg_catalog.pg_type.typmodout => pg_catalog.pg_proc.oid
Join pg_catalog.pg_type.typanalyze => pg_catalog.pg_proc.oid
Join pg_catalog.pg_type.typbasetype => pg_catalog.pg_type.oid
+Join pg_catalog.pg_type.typcollation => pg_catalog.pg_collation.oid
+Join pg_catalog.pg_constraint.conpfeqop []=> pg_catalog.pg_operator.oid
+Join pg_catalog.pg_constraint.conppeqop []=> pg_catalog.pg_operator.oid
+Join pg_catalog.pg_constraint.conffeqop []=> pg_catalog.pg_operator.oid
+Join pg_catalog.pg_proc.proallargtypes []=> pg_catalog.pg_type.oid
---------------------------------------------------------------------------
-Bruce Momjian ([email protected])
+Bruce Momjian ([email protected])
Updated for 7.3 by Joe Conway ([email protected])
diff --git a/src/tools/findoidjoins/findoidjoins.c b/src/tools/findoidjoins/findoidjoins.c
index 3af97c7a09..031a77fa70 100644
--- a/src/tools/findoidjoins/findoidjoins.c
+++ b/src/tools/findoidjoins/findoidjoins.c
@@ -46,9 +46,7 @@ main(int argc, char **argv)
/* Get a list of relations that have OIDs */
- resetPQExpBuffer(&sql);
-
- appendPQExpBuffer(&sql, "%s",
+ printfPQExpBuffer(&sql, "%s",
"SET search_path = public;"
"SELECT c.relname, (SELECT nspname FROM "
"pg_catalog.pg_namespace n WHERE n.oid = c.relnamespace) AS nspname "
@@ -68,9 +66,7 @@ main(int argc, char **argv)
/* Get a list of columns of OID type (or any OID-alias type) */
- resetPQExpBuffer(&sql);
-
- appendPQExpBuffer(&sql, "%s",
+ printfPQExpBuffer(&sql, "%s",
"SELECT c.relname, "
"(SELECT nspname FROM pg_catalog.pg_namespace n WHERE n.oid = c.relnamespace) AS nspname, "
"a.attname "
@@ -113,15 +109,15 @@ main(int argc, char **argv)
pk_relname = PQgetvalue(pkrel_res, pk, 0);
pk_nspname = PQgetvalue(pkrel_res, pk, 1);
- resetPQExpBuffer(&sql);
-
- appendPQExpBuffer(&sql,
+ printfPQExpBuffer(&sql,
"SELECT 1 "
"FROM \"%s\".\"%s\" t1, "
"\"%s\".\"%s\" t2 "
"WHERE t1.\"%s\"::pg_catalog.oid = t2.oid "
"LIMIT 1",
- fk_nspname, fk_relname, pk_nspname, pk_relname, fk_attname);
+ fk_nspname, fk_relname,
+ pk_nspname, pk_relname,
+ fk_attname);
res = PQexec(conn, sql.data);
if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
@@ -139,8 +135,85 @@ main(int argc, char **argv)
}
}
- PQclear(pkrel_res);
PQclear(fkrel_res);
+
+ /* Now, do the same for referencing columns that are arrays */
+
+ /* Get a list of columns of OID-array type (or any OID-alias type) */
+
+ printfPQExpBuffer(&sql, "%s",
+ "SELECT c.relname, "
+ "(SELECT nspname FROM pg_catalog.pg_namespace n WHERE n.oid = c.relnamespace) AS nspname, "
+ "a.attname "
+ "FROM pg_catalog.pg_class c, pg_catalog.pg_attribute a "
+ "WHERE a.attnum > 0 AND c.relkind = 'r' "
+ "AND a.attrelid = c.oid "
+ "AND a.atttypid IN ('pg_catalog.oid[]'::regtype, "
+ " 'pg_catalog.regclass[]'::regtype, "
+ " 'pg_catalog.regoper[]'::regtype, "
+ " 'pg_catalog.regoperator[]'::regtype, "
+ " 'pg_catalog.regproc[]'::regtype, "
+ " 'pg_catalog.regprocedure[]'::regtype, "
+ " 'pg_catalog.regtype[]'::regtype, "
+ " 'pg_catalog.regconfig[]'::regtype, "
+ " 'pg_catalog.regdictionary[]'::regtype) "
+ "ORDER BY nspname, c.relname, a.attnum"
+ );
+
+ res = PQexec(conn, sql.data);
+ if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
+ {
+ fprintf(stderr, "sql error: %s\n", PQerrorMessage(conn));
+ exit(EXIT_FAILURE);
+ }
+ fkrel_res = res;
+
+ /*
+ * For each column and each relation-having-OIDs, look to see if the
+ * column contains any values matching entries in the relation.
+ */
+
+ for (fk = 0; fk < PQntuples(fkrel_res); fk++)
+ {
+ fk_relname = PQgetvalue(fkrel_res, fk, 0);
+ fk_nspname = PQgetvalue(fkrel_res, fk, 1);
+ fk_attname = PQgetvalue(fkrel_res, fk, 2);
+
+ for (pk = 0; pk < PQntuples(pkrel_res); pk++)
+ {
+ pk_relname = PQgetvalue(pkrel_res, pk, 0);
+ pk_nspname = PQgetvalue(pkrel_res, pk, 1);
+
+ printfPQExpBuffer(&sql,
+ "SELECT 1 "
+ "FROM \"%s\".\"%s\" t1, "
+ "\"%s\".\"%s\" t2 "
+ "WHERE t2.oid = ANY(t1.\"%s\")"
+ "LIMIT 1",
+ fk_nspname, fk_relname,
+ pk_nspname, pk_relname,
+ fk_attname);
+
+ res = PQexec(conn, sql.data);
+ if (!res || PQresultStatus(res) != PGRES_TUPLES_OK)
+ {
+ fprintf(stderr, "sql error: %s\n", PQerrorMessage(conn));
+ exit(EXIT_FAILURE);
+ }
+
+ if (PQntuples(res) != 0)
+ printf("Join %s.%s.%s []=> %s.%s.oid\n",
+ fk_nspname, fk_relname, fk_attname,
+ pk_nspname, pk_relname);
+
+ PQclear(res);
+ }
+ }
+
+ PQclear(fkrel_res);
+
+ PQclear(pkrel_res);
+
PQfinish(conn);
termPQExpBuffer(&sql);
diff --git a/src/tools/findoidjoins/make_oidjoins_check b/src/tools/findoidjoins/make_oidjoins_check
index 59ac76aaba..09d283462c 100755
--- a/src/tools/findoidjoins/make_oidjoins_check
+++ b/src/tools/findoidjoins/make_oidjoins_check
@@ -2,7 +2,7 @@
# src/tools/findoidjoins/make_oidjoins_check
-# You first run findoidjoins on the template1 database, and send that
+# You first run findoidjoins on the regression database, then send that
# output into this script to generate a list of SQL statements.
# NOTE: any field that findoidjoins thinks joins to more than one table
@@ -12,17 +12,16 @@
# Caution: you may need to use GNU awk.
AWK=${AWK:-awk}
-TMP="${TMPDIR:-/tmp}/make_oidjoins_check.$$"
-trap "rm -rf $TMP" 0 1 2 3 15
-
# Create a temporary directory with the proper permissions so no one can
# intercept our temporary files and cause a security breach.
+TMP="${TMPDIR:-/tmp}/make_oidjoins_check.$$"
OMASK="`umask`"
umask 077
if ! mkdir $TMP
then echo "Can't create temporary directory $TMP." 1>&2
exit 1
fi
+trap "rm -rf $TMP" 0 1 2 3 15
umask "$OMASK"
unset OMASK
@@ -40,7 +39,7 @@ if [ -s $DUPSFILE ] ; then
cat $DUPSFILE 1>&2
fi
-# Get the non-multiply-referenced fields.
+# Get the fields without multiple references.
cat $INPUTFILE | while read LINE
do
set -- $LINE
@@ -49,7 +48,7 @@ done >$NONDUPSFILE
# Generate the output.
cat $NONDUPSFILE |
-$AWK -F'[ \.]' '\
+$AWK -F'[ .]' '\
BEGIN \
{
printf "\
@@ -57,14 +56,25 @@ $AWK -F'[ \.]' '\
-- This is created by pgsql/src/tools/findoidjoins/make_oidjoins_check\n\
--\n";
}
+ $5 == "=>" \
{
printf "\
-SELECT ctid, %s \n\
-FROM %s.%s fk \n\
-WHERE %s != 0 AND \n\
+SELECT ctid, %s\n\
+FROM %s.%s fk\n\
+WHERE %s != 0 AND\n\
NOT EXISTS(SELECT 1 FROM %s.%s pk WHERE pk.oid = fk.%s);\n",
$4, $2, $3, $4,
$6, $7, $4;
+ }
+ $5 == "[]=>" \
+ {
+ printf "\
+SELECT ctid, %s\n\
+FROM (SELECT ctid, unnest(%s) AS %s FROM %s.%s) fk\n\
+WHERE %s != 0 AND\n\
+ NOT EXISTS(SELECT 1 FROM %s.%s pk WHERE pk.oid = fk.%s);\n",
+ $4, $4, $4, $2, $3, $4,
+ $6, $7, $4;
}'
exit 0
diff --git a/src/tools/msvc/pgflex.bat b/src/tools/msvc/pgflex.bat
index da7c33d7af..58870f0512 100755
--- a/src/tools/msvc/pgflex.bat
+++ b/src/tools/msvc/pgflex.bat
@@ -25,9 +25,19 @@ if "%1" == "contrib\seg\segscan.l" call :generate %1 contrib\seg\segscan.c
echo Unknown flex input: %1
exit 1
+REM For non-reentrant scanners we need to fix up the yywrap macro definition
+REM to keep the MS compiler happy.
+REM For reentrant scanners (like the core scanner) we do not
+REM need to (and must not) change the yywrap definition.
:generate
flex %3 -o%2 %1
-exit %errorlevel%
+if errorlevel 1 exit %errorlevel%
+perl -n -e "exit 1 if /^\%%option\s+reentrant/;" %1
+if errorlevel 1 exit 0
+perl -pi.bak -e "s/yywrap\(n\)/yywrap()/;" %2
+if errorlevel 1 exit %errorlevel%
+del %2.bak
+exit 0
:noflex
echo WARNING! flex install not found, attempting to build without