summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBernd Helmle2011-06-04 13:56:43 +0000
committerBernd Helmle2011-06-04 13:56:43 +0000
commit820e91132277e492d4a09737ef3530b8a53649dd (patch)
treeb50efc1dd556ed4280bf5f33d4249cfa9947a3c1
parent337798e4985da6192bc4b6335fc188588b05197b (diff)
parent048417511aef8d5fb2d541b17b73afc730935cd5 (diff)
Merge branch 'master' of ../bernd_pg into notnull_constraint
-rwxr-xr-xconfigure6
-rw-r--r--configure.in4
-rw-r--r--doc/src/sgml/array.sgml2
-rw-r--r--doc/src/sgml/ecpg.sgml20
-rw-r--r--doc/src/sgml/information_schema.sgml2
-rw-r--r--doc/src/sgml/ref/comment.sgml8
-rw-r--r--src/backend/catalog/heap.c13
-rw-r--r--src/backend/catalog/index.c11
-rw-r--r--src/backend/commands/sequence.c16
-rw-r--r--src/backend/commands/tablecmds.c2
-rw-r--r--src/backend/executor/execMain.c64
-rw-r--r--src/backend/libpq/auth.c88
-rw-r--r--src/backend/optimizer/plan/subselect.c1
-rw-r--r--src/backend/parser/parse_oper.c36
-rw-r--r--src/backend/storage/lmgr/predicate.c18
-rw-r--r--src/backend/utils/adt/ruleutils.c3
-rw-r--r--src/backend/utils/cache/lsyscache.c61
-rw-r--r--src/backend/utils/cache/typcache.c345
-rw-r--r--src/include/catalog/pg_operator.h3
-rw-r--r--src/include/port.h4
-rw-r--r--src/include/storage/predicate_internals.h5
-rw-r--r--src/include/utils/typcache.h11
-rw-r--r--src/interfaces/libpq/.gitignore1
-rw-r--r--src/interfaces/libpq/Makefile6
-rw-r--r--src/interfaces/libpq/fe-connect.c86
-rw-r--r--src/port/getpeereid.c80
-rw-r--r--src/test/regress/expected/alter_table.out3
-rw-r--r--src/test/regress/expected/rowtypes.out10
-rw-r--r--src/test/regress/sql/alter_table.sql2
-rw-r--r--src/test/regress/sql/rowtypes.sql7
30 files changed, 598 insertions, 320 deletions
diff --git a/configure b/configure
index 5c6022903d..a8c5923118 100755
--- a/configure
+++ b/configure
@@ -18852,8 +18852,7 @@ 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 wcstombs_l
+for ac_func in cbrt dlopen fcvt fdatasync getifaddrs 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
@@ -20424,7 +20423,8 @@ LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'`
-for ac_func in crypt erand48 getopt getrusage inet_aton random rint srandom strdup strerror strlcat strlcpy strtol strtoul
+
+for ac_func in crypt erand48 getopt getpeereid getrusage inet_aton random rint srandom strdup strerror strlcat strlcpy strtol strtoul
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 ed2d17d219..6a7278aefe 100644
--- a/configure.in
+++ b/configure.in
@@ -1191,7 +1191,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 wcstombs_l])
+AC_CHECK_FUNCS([cbrt dlopen fcvt fdatasync getifaddrs 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
@@ -1310,7 +1310,7 @@ fi
pgac_save_LIBS="$LIBS"
LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'`
-AC_REPLACE_FUNCS([crypt erand48 getopt getrusage inet_aton random rint srandom strdup strerror strlcat strlcpy strtol strtoul])
+AC_REPLACE_FUNCS([crypt erand48 getopt getpeereid getrusage inet_aton random rint srandom strdup strerror strlcat strlcpy strtol strtoul])
case $host_os in
diff --git a/doc/src/sgml/array.sgml b/doc/src/sgml/array.sgml
index bb318c5fc5..3508ba3e3c 100644
--- a/doc/src/sgml/array.sgml
+++ b/doc/src/sgml/array.sgml
@@ -369,7 +369,7 @@ UPDATE sal_emp SET pay_by_quarter = ARRAY[25000,25000,27000,27000]
<programlisting>
UPDATE sal_emp SET pay_by_quarter[4] = 15000
WHERE name = 'Bill';
-</programListing>
+</programlisting>
or updated in a slice:
diff --git a/doc/src/sgml/ecpg.sgml b/doc/src/sgml/ecpg.sgml
index 9c6ca4c519..a8ffde5150 100644
--- a/doc/src/sgml/ecpg.sgml
+++ b/doc/src/sgml/ecpg.sgml
@@ -4154,7 +4154,7 @@ switch (v.sqltype)
{
case ECPGt_char:
memset(&amp;var_buf, 0, sizeof(var_buf));
- memcpy(&amp;var_buf, sqldata, (sizeof(var_buf) <= sqllen ? sizeof(var_buf) - 1 : sqllen));
+ memcpy(&amp;var_buf, sqldata, (sizeof(var_buf) &lt;= sqllen ? sizeof(var_buf) - 1 : sqllen));
break;
case ECPGt_int: /* integer */
@@ -4390,7 +4390,7 @@ main(void)
case ECPGt_char:
memset(&amp;var_buf, 0, sizeof(var_buf));
- memcpy(&amp;var_buf, sqldata, (sizeof(var_buf) <= sqllen ? sizeof(var_buf)-1 : sqllen));
+ memcpy(&amp;var_buf, sqldata, (sizeof(var_buf) &lt;= sqllen ? sizeof(var_buf)-1 : sqllen));
break;
case ECPGt_int: /* integer */
@@ -5871,39 +5871,39 @@ main(void)
/* create */
loid = lo_create(conn, 0);
- if (loid < 0)
+ if (loid &lt; 0)
printf("lo_create() failed: %s", PQerrorMessage(conn));
printf("loid = %d\n", loid);
/* write test */
fd = lo_open(conn, loid, INV_READ|INV_WRITE);
- if (fd < 0)
+ if (fd &lt; 0)
printf("lo_open() failed: %s", PQerrorMessage(conn));
printf("fd = %d\n", fd);
rc = lo_write(conn, fd, buf, buflen);
- if (rc < 0)
+ if (rc &lt; 0)
printf("lo_write() failed\n");
rc = lo_close(conn, fd);
- if (rc < 0)
+ if (rc &lt; 0)
printf("lo_close() failed: %s", PQerrorMessage(conn));
/* read test */
fd = lo_open(conn, loid, INV_READ);
- if (fd < 0)
+ if (fd &lt; 0)
printf("lo_open() failed: %s", PQerrorMessage(conn));
printf("fd = %d\n", fd);
rc = lo_read(conn, fd, buf2, buflen);
- if (rc < 0)
+ if (rc &lt; 0)
printf("lo_read() failed\n");
rc = lo_close(conn, fd);
- if (rc < 0)
+ if (rc &lt; 0)
printf("lo_close() failed: %s", PQerrorMessage(conn));
/* check */
@@ -5912,7 +5912,7 @@ main(void)
/* cleanup */
rc = lo_unlink(conn, loid);
- if (rc < 0)
+ if (rc &lt; 0)
printf("lo_unlink() failed: %s", PQerrorMessage(conn));
EXEC SQL COMMIT;
diff --git a/doc/src/sgml/information_schema.sgml b/doc/src/sgml/information_schema.sgml
index 2febb4ced8..a60014267e 100644
--- a/doc/src/sgml/information_schema.sgml
+++ b/doc/src/sgml/information_schema.sgml
@@ -825,7 +825,7 @@
</table>
</sect1>
- <sect1 id="infoschema-collation-character-set-applicability">
+ <sect1 id="infoschema-collation-character-set-applicab"> <!-- max 44 characters -->
<title><literal>collation_character_set_applicability</literal></title>
<para>
diff --git a/doc/src/sgml/ref/comment.sgml b/doc/src/sgml/ref/comment.sgml
index 1cdc49ff5f..ab12614f84 100644
--- a/doc/src/sgml/ref/comment.sgml
+++ b/doc/src/sgml/ref/comment.sgml
@@ -23,11 +23,10 @@ PostgreSQL documentation
<synopsis>
COMMENT ON
{
- TABLE <replaceable class="PARAMETER">object_name</replaceable> |
- COLUMN <replaceable class="PARAMETER">table_name</replaceable>.<replaceable class="PARAMETER">column_name</replaceable> |
AGGREGATE <replaceable class="PARAMETER">agg_name</replaceable> (<replaceable class="PARAMETER">agg_type</replaceable> [, ...] ) |
CAST (<replaceable>source_type</replaceable> AS <replaceable>target_type</replaceable>) |
COLLATION <replaceable class="PARAMETER">object_name</replaceable> |
+ COLUMN <replaceable class="PARAMETER">table_name</replaceable>.<replaceable class="PARAMETER">column_name</replaceable> |
CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> ON <replaceable class="PARAMETER">table_name</replaceable> |
CONVERSION <replaceable class="PARAMETER">object_name</replaceable> |
DATABASE <replaceable class="PARAMETER">object_name</replaceable> |
@@ -47,6 +46,7 @@ COMMENT ON
SCHEMA <replaceable class="PARAMETER">object_name</replaceable> |
SEQUENCE <replaceable class="PARAMETER">object_name</replaceable> |
SERVER <replaceable class="PARAMETER">object_name</replaceable> |
+ TABLE <replaceable class="PARAMETER">object_name</replaceable> |
TABLESPACE <replaceable class="PARAMETER">object_name</replaceable> |
TEXT SEARCH CONFIGURATION <replaceable class="PARAMETER">object_name</replaceable> |
TEXT SEARCH DICTIONARY <replaceable class="PARAMETER">object_name</replaceable> |
@@ -265,8 +265,11 @@ COMMENT ON CAST (text AS int4) IS 'Allow casts from text to int4';
COMMENT ON COLLATION "fr_CA" IS 'Canadian French';
COMMENT ON COLUMN my_table.my_column IS 'Employee ID number';
COMMENT ON CONVERSION my_conv IS 'Conversion to UTF8';
+COMMENT ON CONSTRAINT bar_col_cons ON bar IS 'Constrains column col';
COMMENT ON DATABASE my_database IS 'Development Database';
COMMENT ON DOMAIN my_domain IS 'Email Address Domain';
+COMMENT ON EXTENSION hstore IS 'implements the hstore data type';
+COMMENT ON FOREIGN DATA WRAPPER mywrapper IS 'my foreign data wrapper';
COMMENT ON FOREIGN TABLE my_foreign_table IS 'Employee Information in other database';
COMMENT ON FUNCTION my_function (timestamp) IS 'Returns Roman Numeral';
COMMENT ON INDEX my_index IS 'Enforces uniqueness on employee ID';
@@ -280,6 +283,7 @@ COMMENT ON ROLE my_role IS 'Administration group for finance tables';
COMMENT ON RULE my_rule ON my_table IS 'Logs updates of employee records';
COMMENT ON SCHEMA my_schema IS 'Departmental data';
COMMENT ON SEQUENCE my_sequence IS 'Used to generate primary keys';
+COMMENT ON SERVER myserver IS 'my foreign server';
COMMENT ON TABLE my_schema.my_table IS 'Employee Information';
COMMENT ON TABLESPACE my_tablespace IS 'Tablespace for indexes';
COMMENT ON TEXT SEARCH CONFIGURATION my_config IS 'Special word filtering';
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 3183ff8fd5..1e4967e293 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -485,12 +485,19 @@ CheckAttributeType(const char *attname,
errmsg("column \"%s\" has pseudo-type %s",
attname, format_type_be(atttypid))));
}
+ else if (att_typtype == TYPTYPE_DOMAIN)
+ {
+ /*
+ * If it's a domain, recurse to check its base type.
+ */
+ CheckAttributeType(attname, getBaseType(atttypid), attcollation,
+ containing_rowtypes,
+ allow_system_table_mods);
+ }
else if (att_typtype == TYPTYPE_COMPOSITE)
{
/*
- * For a composite type, recurse into its attributes. You might think
- * this isn't necessary, but since we allow system catalogs to break
- * the rule, we have to guard against the case.
+ * For a composite type, recurse into its attributes.
*/
Relation relation;
TupleDesc tupdesc;
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index a0898e0048..53b4c3c59b 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1741,9 +1741,14 @@ index_build(Relation heapRelation,
Assert(PointerIsValid(stats));
/*
- * If this is an unlogged index, we need to write out an init fork for it.
- */
- if (heapRelation->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED)
+ * If this is an unlogged index, we may need to write out an init fork for
+ * it -- but we must first check whether one already exists. If, for
+ * example, an unlogged relation is truncated in the transaction that
+ * created it, or truncated twice in a subsequent transaction, the
+ * relfilenode won't change, and nothing needs to be done here.
+ */
+ if (heapRelation->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED
+ && !smgrexists(indexRelation->rd_smgr, INIT_FORKNUM))
{
RegProcedure ambuildempty = indexRelation->rd_am->ambuildempty;
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 6a91a102dc..383690270b 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -1076,6 +1076,22 @@ read_info(SeqTable elm, Relation rel, Buffer *buf)
Assert(ItemIdIsNormal(lp));
tuple.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ /*
+ * Previous releases of Postgres neglected to prevent SELECT FOR UPDATE
+ * on a sequence, which would leave a non-frozen XID in the sequence
+ * tuple's xmax, which eventually leads to clog access failures or worse.
+ * If we see this has happened, clean up after it. We treat this like a
+ * hint bit update, ie, don't bother to WAL-log it, since we can certainly
+ * do this again if the update gets lost.
+ */
+ if (HeapTupleHeaderGetXmax(tuple.t_data) != InvalidTransactionId)
+ {
+ HeapTupleHeaderSetXmax(tuple.t_data, InvalidTransactionId);
+ tuple.t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
+ tuple.t_data->t_infomask |= HEAP_XMAX_INVALID;
+ SetBufferCommitInfoNeedsSave(*buf);
+ }
+
seq = (Form_pg_sequence) GETSTRUCT(&tuple);
/* this is a handy place to update our copy of the increment */
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 398056fff3..17a0384465 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8440,7 +8440,7 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lock
default:
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("\"%s\" is not a table, view, sequence, or foreign tabl, or foreign tablee",
+ errmsg("\"%s\" is not a table, view, sequence, or foreign table",
NameStr(tuple_class->relname))));
}
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 620efda838..df1d3cab4d 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -74,6 +74,7 @@ ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook = NULL;
/* decls for local routines only used within this module */
static void InitPlan(QueryDesc *queryDesc, int eflags);
+static void CheckValidRowMarkRel(Relation rel, RowMarkType markType);
static void ExecPostprocessPlan(EState *estate);
static void ExecEndPlan(PlanState *planstate, EState *estate);
static void ExecutePlan(EState *estate, PlanState *planstate,
@@ -837,12 +838,9 @@ InitPlan(QueryDesc *queryDesc, int eflags)
break;
}
- /* if foreign table, tuples can't be locked */
- if (relation && relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("SELECT FOR UPDATE/SHARE cannot be used with foreign table \"%s\"",
- RelationGetRelationName(relation))));
+ /* Check that relation is a legal target for marking */
+ if (relation)
+ CheckValidRowMarkRel(relation, rc->markType);
erm = (ExecRowMark *) palloc(sizeof(ExecRowMark));
erm->relation = relation;
@@ -977,6 +975,9 @@ InitPlan(QueryDesc *queryDesc, int eflags)
* In most cases parser and/or planner should have noticed this already, but
* let's make sure. In the view case we do need a test here, because if the
* view wasn't rewritten by a rule, it had better have an INSTEAD trigger.
+ *
+ * Note: when changing this function, you probably also need to look at
+ * CheckValidRowMarkRel.
*/
void
CheckValidResultRel(Relation resultRel, CmdType operation)
@@ -1048,6 +1049,57 @@ CheckValidResultRel(Relation resultRel, CmdType operation)
}
/*
+ * Check that a proposed rowmark target relation is a legal target
+ *
+ * In most cases parser and/or planner should have noticed this already, but
+ * they don't cover all cases.
+ */
+static void
+CheckValidRowMarkRel(Relation rel, RowMarkType markType)
+{
+ switch (rel->rd_rel->relkind)
+ {
+ case RELKIND_RELATION:
+ /* OK */
+ break;
+ case RELKIND_SEQUENCE:
+ /* Must disallow this because we don't vacuum sequences */
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot lock rows in sequence \"%s\"",
+ RelationGetRelationName(rel))));
+ break;
+ case RELKIND_TOASTVALUE:
+ /* We could allow this, but there seems no good reason to */
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot lock rows in TOAST relation \"%s\"",
+ RelationGetRelationName(rel))));
+ break;
+ case RELKIND_VIEW:
+ /* Should not get here */
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot lock rows in view \"%s\"",
+ RelationGetRelationName(rel))));
+ break;
+ case RELKIND_FOREIGN_TABLE:
+ /* Perhaps we can support this someday, but not today */
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot lock rows in foreign table \"%s\"",
+ RelationGetRelationName(rel))));
+ break;
+ default:
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot lock rows in relation \"%s\"",
+ RelationGetRelationName(rel))));
+ break;
+ }
+}
+
+/*
* Initialize ResultRelInfo data for one result relation
*
* Caution: before Postgres 9.1, this function included the relkind checking
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index dc7ad2cadf..f4099ac234 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -17,12 +17,6 @@
#include <sys/param.h>
#include <sys/socket.h>
-#ifdef HAVE_UCRED_H
-#include <ucred.h>
-#endif
-#ifdef HAVE_SYS_UCRED_H
-#include <sys/ucred.h>
-#endif
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
@@ -1756,85 +1750,25 @@ static int
auth_peer(hbaPort *port)
{
char ident_user[IDENT_USERNAME_MAX + 1];
- uid_t uid = 0;
- struct passwd *pass;
-
-#if defined(HAVE_GETPEEREID)
- /* Most BSDen, including OS X: use getpeereid() */
+ uid_t uid;
gid_t gid;
+ struct passwd *pass;
errno = 0;
if (getpeereid(port->sock, &uid, &gid) != 0)
{
- /* We didn't get a valid credentials struct. */
- ereport(LOG,
- (errcode_for_socket_access(),
- errmsg("could not get peer credentials: %m")));
- return STATUS_ERROR;
- }
-#elif defined(SO_PEERCRED)
- /* Linux: use getsockopt(SO_PEERCRED) */
- struct ucred peercred;
- ACCEPT_TYPE_ARG3 so_len = sizeof(peercred);
-
- errno = 0;
- if (getsockopt(port->sock, SOL_SOCKET, SO_PEERCRED, &peercred, &so_len) != 0 ||
- so_len != sizeof(peercred))
- {
- /* We didn't get a valid credentials struct. */
- ereport(LOG,
- (errcode_for_socket_access(),
- errmsg("could not get peer credentials: %m")));
- return STATUS_ERROR;
- }
- uid = peercred.uid;
-#elif defined(LOCAL_PEERCRED)
- /* Debian with FreeBSD kernel: use getsockopt(LOCAL_PEERCRED) */
- struct xucred peercred;
- ACCEPT_TYPE_ARG3 so_len = sizeof(peercred);
-
- errno = 0;
- if (getsockopt(port->sock, 0, LOCAL_PEERCRED, &peercred, &so_len) != 0 ||
- so_len != sizeof(peercred) ||
- peercred.cr_version != XUCRED_VERSION)
- {
- /* We didn't get a valid credentials struct. */
- ereport(LOG,
- (errcode_for_socket_access(),
- errmsg("could not get peer credentials: %m")));
- return STATUS_ERROR;
- }
- uid = peercred.cr_uid;
-#elif defined(HAVE_GETPEERUCRED)
- /* Solaris: use getpeerucred() */
- ucred_t *ucred;
-
- ucred = NULL; /* must be initialized to NULL */
- if (getpeerucred(port->sock, &ucred) == -1)
- {
- ereport(LOG,
- (errcode_for_socket_access(),
- errmsg("could not get peer credentials: %m")));
- return STATUS_ERROR;
- }
-
- if ((uid = ucred_geteuid(ucred)) == -1)
- {
- ereport(LOG,
- (errcode_for_socket_access(),
- errmsg("could not get effective UID from peer credentials: %m")));
+ /* Provide special error message if getpeereid is a stub */
+ if (errno == ENOSYS)
+ ereport(LOG,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("peer authentication is not supported on this platform")));
+ else
+ ereport(LOG,
+ (errcode_for_socket_access(),
+ errmsg("could not get peer credentials: %m")));
return STATUS_ERROR;
}
- ucred_free(ucred);
-#else
- ereport(LOG,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("Peer authentication is not supported on local connections on this platform")));
-
- return STATUS_ERROR;
-#endif
-
pass = getpwuid(uid);
if (pass == NULL)
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 45eaa03fda..71ffc17b59 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -891,6 +891,7 @@ hash_ok_operator(OpExpr *expr)
if (opid == ARRAY_EQ_OP)
{
/* array_eq is strict, but must check input type to ensure hashable */
+ /* XXX record_eq will need same treatment when it becomes hashable */
Node *leftarg = linitial(expr->args);
return op_hashjoinable(opid, exprType(leftarg));
diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c
index 15a3bb3a01..4a2f77771b 100644
--- a/src/backend/parser/parse_oper.c
+++ b/src/backend/parser/parse_oper.c
@@ -211,42 +211,6 @@ get_sort_group_operators(Oid argtype,
gt_opr = typentry->gt_opr;
hashable = OidIsValid(typentry->hash_proc);
- /*
- * If the datatype is an array, then we can use array_lt and friends ...
- * but only if there are suitable operators for the element type.
- * Likewise, array types are only hashable if the element type is. Testing
- * all three operator IDs here should be redundant, but let's do it
- * anyway.
- */
- if (lt_opr == ARRAY_LT_OP ||
- eq_opr == ARRAY_EQ_OP ||
- gt_opr == ARRAY_GT_OP)
- {
- Oid elem_type = get_base_element_type(argtype);
-
- if (OidIsValid(elem_type))
- {
- typentry = lookup_type_cache(elem_type, cache_flags);
- if (!OidIsValid(typentry->eq_opr))
- {
- /* element type is neither sortable nor hashable */
- lt_opr = eq_opr = gt_opr = InvalidOid;
- }
- else if (!OidIsValid(typentry->lt_opr) ||
- !OidIsValid(typentry->gt_opr))
- {
- /* element type is hashable but not sortable */
- lt_opr = gt_opr = InvalidOid;
- }
- hashable = OidIsValid(typentry->hash_proc);
- }
- else
- {
- lt_opr = eq_opr = gt_opr = InvalidOid; /* bogus array type? */
- hashable = false;
- }
- }
-
/* Report errors if needed */
if ((needLT && !OidIsValid(lt_opr)) ||
(needGT && !OidIsValid(gt_opr)))
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index 5ac7624991..6facb533e0 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -245,6 +245,11 @@
#define SxactIsReadOnly(sxact) (((sxact)->flags & SXACT_FLAG_READ_ONLY) != 0)
#define SxactHasSummaryConflictIn(sxact) (((sxact)->flags & SXACT_FLAG_SUMMARY_CONFLICT_IN) != 0)
#define SxactHasSummaryConflictOut(sxact) (((sxact)->flags & SXACT_FLAG_SUMMARY_CONFLICT_OUT) != 0)
+/*
+ * The following macro actually means that the specified transaction has a
+ * conflict out *to a transaction which committed ahead of it*. It's hard
+ * to get that into a name of a reasonable length.
+ */
#define SxactHasConflictOut(sxact) (((sxact)->flags & SXACT_FLAG_CONFLICT_OUT) != 0)
#define SxactIsDeferrableWaiting(sxact) (((sxact)->flags & SXACT_FLAG_DEFERRABLE_WAITING) != 0)
#define SxactIsROSafe(sxact) (((sxact)->flags & SXACT_FLAG_RO_SAFE) != 0)
@@ -2708,7 +2713,7 @@ SetNewSxactGlobalXmin(void)
* up in some relatively timely fashion.
*
* If this transaction is committing and is holding any predicate locks,
- * it must be added to a list of completed serializable transaction still
+ * it must be added to a list of completed serializable transactions still
* holding locks.
*/
void
@@ -2753,12 +2758,13 @@ ReleasePredicateLocks(const bool isCommit)
LWLockAcquire(SerializableXactHashLock, LW_EXCLUSIVE);
/*
- * We don't hold a lock here, assuming that TransactionId is atomic!
+ * We don't hold XidGenLock lock here, assuming that TransactionId is
+ * atomic!
*
* If this value is changing, we don't care that much whether we get the
* old or new value -- it is just used to determine how far
- * GlobalSerizableXmin must advance before this transaction can be cleaned
- * fully cleaned up. The worst that could happen is we wait for ome more
+ * GlobalSerizableXmin must advance before this transaction can be fully
+ * cleaned up. The worst that could happen is we wait for one more
* transaction to complete before freeing some RAM; correctness of visible
* behavior is not affected.
*/
@@ -3860,8 +3866,8 @@ OnConflict_CheckForSerializationFailure(const SERIALIZABLEXACT *reader,
* Because T2 must've committed first, there is no anomaly if:
* - the reader committed before T2
* - the writer committed before T2
- * - the reader is a READ ONLY transaction and the reader was not
- * concurrent with T2 (= reader acquired its snapshot after T2 committed)
+ * - the reader is a READ ONLY transaction and the reader was concurrent
+ * with T2 (= reader acquired its snapshot before T2 committed)
*/
if (!failure)
{
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index b457a72034..3371bd2b9b 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -1411,6 +1411,9 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
if (conForm->condeferred)
appendStringInfo(&buf, " INITIALLY DEFERRED");
+ if (!conForm->convalidated)
+ appendStringInfoString(&buf, " NOT VALID");
+
/* Cleanup */
ReleaseSysCache(tup);
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index d3b2a5a557..28d18b0d32 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -33,6 +33,7 @@
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/datum.h"
+#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
@@ -1120,34 +1121,35 @@ op_input_types(Oid opno, Oid *lefttype, Oid *righttype)
* opfamily entries for this operator and associated sortops. The pg_operator
* flag is just a hint to tell the planner whether to bother looking.)
*
- * In some cases (currently only array_eq), mergejoinability depends on the
- * specific input data type the operator is invoked for, so that must be
- * passed as well. We currently assume that only one input's type is needed
- * to check this --- by convention, pass the left input's data type.
+ * In some cases (currently only array_eq and record_eq), mergejoinability
+ * depends on the specific input data type the operator is invoked for, so
+ * that must be passed as well. We currently assume that only one input's type
+ * is needed to check this --- by convention, pass the left input's data type.
*/
bool
op_mergejoinable(Oid opno, Oid inputtype)
{
- HeapTuple tp;
bool result = false;
+ HeapTuple tp;
+ TypeCacheEntry *typentry;
+ /*
+ * For array_eq or record_eq, we can sort if the element or field types
+ * are all sortable. We could implement all the checks for that here, but
+ * the typcache already does that and caches the results too, so let's
+ * rely on the typcache.
+ */
if (opno == ARRAY_EQ_OP)
{
- /*
- * For array_eq, can sort if element type has a default btree opclass.
- * We could use GetDefaultOpClass, but that's fairly expensive and not
- * cached, so let's use the typcache instead.
- */
- Oid elem_type = get_base_element_type(inputtype);
-
- if (OidIsValid(elem_type))
- {
- TypeCacheEntry *typentry;
-
- typentry = lookup_type_cache(elem_type, TYPECACHE_BTREE_OPFAMILY);
- if (OidIsValid(typentry->btree_opf))
- result = true;
- }
+ typentry = lookup_type_cache(inputtype, TYPECACHE_CMP_PROC);
+ if (typentry->cmp_proc == F_BTARRAYCMP)
+ result = true;
+ }
+ else if (opno == RECORD_EQ_OP)
+ {
+ typentry = lookup_type_cache(inputtype, TYPECACHE_CMP_PROC);
+ if (typentry->cmp_proc == F_BTRECORDCMP)
+ result = true;
}
else
{
@@ -1178,22 +1180,17 @@ op_mergejoinable(Oid opno, Oid inputtype)
bool
op_hashjoinable(Oid opno, Oid inputtype)
{
- HeapTuple tp;
bool result = false;
+ HeapTuple tp;
+ TypeCacheEntry *typentry;
+ /* As in op_mergejoinable, let the typcache handle the hard cases */
+ /* Eventually we'll need a similar case for record_eq ... */
if (opno == ARRAY_EQ_OP)
{
- /* For array_eq, can hash if element type has a default hash opclass */
- Oid elem_type = get_base_element_type(inputtype);
-
- if (OidIsValid(elem_type))
- {
- TypeCacheEntry *typentry;
-
- typentry = lookup_type_cache(elem_type, TYPECACHE_HASH_OPFAMILY);
- if (OidIsValid(typentry->hash_opf))
- result = true;
- }
+ typentry = lookup_type_cache(inputtype, TYPECACHE_HASH_PROC);
+ if (typentry->hash_proc == F_HASH_ARRAY)
+ result = true;
}
else
{
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 2769a30acd..14d9cf7a44 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -10,11 +10,11 @@
* be used for grouping and sorting the type (GROUP BY, ORDER BY ASC/DESC).
*
* Several seemingly-odd choices have been made to support use of the type
- * cache by the generic array handling routines array_eq(), array_cmp(),
- * and hash_array(). Because those routines are used as index support
- * operations, they cannot leak memory. To allow them to execute efficiently,
- * all information that they would like to re-use across calls is kept in the
- * type cache.
+ * cache by generic array and record handling routines, such as array_eq(),
+ * record_cmp(), and hash_array(). Because those routines are used as index
+ * support operations, they cannot leak memory. To allow them to execute
+ * efficiently, all information that they would like to re-use across calls
+ * is kept in the type cache.
*
* Once created, a type cache entry lives as long as the backend does, so
* there is no need for a call to release a cache entry. (For present uses,
@@ -28,8 +28,9 @@
* doesn't cope with opclasses changing under it, either, so this seems
* a low-priority problem.
*
- * We do support clearing the tuple descriptor part of a rowtype's cache
- * entry, since that may need to change as a consequence of ALTER TABLE.
+ * We do support clearing the tuple descriptor and operator/function parts
+ * of a rowtype's cache entry, since those may need to change as a consequence
+ * of ALTER TABLE.
*
*
* Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
@@ -49,6 +50,7 @@
#include "access/nbtree.h"
#include "catalog/indexing.h"
#include "catalog/pg_enum.h"
+#include "catalog/pg_operator.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "utils/builtins.h"
@@ -65,6 +67,15 @@
/* The main type cache hashtable searched by lookup_type_cache */
static HTAB *TypeCacheHash = NULL;
+/* Private flag bits in the TypeCacheEntry.flags field */
+#define TCFLAGS_CHECKED_ELEM_PROPERTIES 0x0001
+#define TCFLAGS_HAVE_ELEM_EQUALITY 0x0002
+#define TCFLAGS_HAVE_ELEM_COMPARE 0x0004
+#define TCFLAGS_HAVE_ELEM_HASHING 0x0008
+#define TCFLAGS_CHECKED_FIELD_PROPERTIES 0x0010
+#define TCFLAGS_HAVE_FIELD_EQUALITY 0x0020
+#define TCFLAGS_HAVE_FIELD_COMPARE 0x0040
+
/* Private information to support comparisons of enum values */
typedef struct
{
@@ -109,6 +120,14 @@ static TupleDesc *RecordCacheArray = NULL;
static int32 RecordCacheArrayLen = 0; /* allocated length of array */
static int32 NextRecordTypmod = 0; /* number of entries used */
+static void load_typcache_tupdesc(TypeCacheEntry *typentry);
+static bool array_element_has_equality(TypeCacheEntry *typentry);
+static bool array_element_has_compare(TypeCacheEntry *typentry);
+static bool array_element_has_hashing(TypeCacheEntry *typentry);
+static void cache_array_element_properties(TypeCacheEntry *typentry);
+static bool record_fields_have_equality(TypeCacheEntry *typentry);
+static bool record_fields_have_compare(TypeCacheEntry *typentry);
+static void cache_record_field_properties(TypeCacheEntry *typentry);
static void TypeCacheRelCallback(Datum arg, Oid relid);
static void load_enum_cache_data(TypeCacheEntry *tcache);
static EnumItem *find_enumitem(TypeCacheEnumData *enumdata, Oid arg);
@@ -257,17 +276,34 @@ lookup_type_cache(Oid type_id, int flags)
if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_EQ_OPR_FINFO)) &&
typentry->eq_opr == InvalidOid)
{
+ Oid eq_opr = InvalidOid;
+
if (typentry->btree_opf != InvalidOid)
- typentry->eq_opr = get_opfamily_member(typentry->btree_opf,
- typentry->btree_opintype,
- typentry->btree_opintype,
- BTEqualStrategyNumber);
- if (typentry->eq_opr == InvalidOid &&
+ eq_opr = get_opfamily_member(typentry->btree_opf,
+ typentry->btree_opintype,
+ typentry->btree_opintype,
+ BTEqualStrategyNumber);
+ if (eq_opr == InvalidOid &&
typentry->hash_opf != InvalidOid)
- typentry->eq_opr = get_opfamily_member(typentry->hash_opf,
- typentry->hash_opintype,
- typentry->hash_opintype,
- HTEqualStrategyNumber);
+ eq_opr = get_opfamily_member(typentry->hash_opf,
+ typentry->hash_opintype,
+ typentry->hash_opintype,
+ HTEqualStrategyNumber);
+
+ /*
+ * If the proposed equality operator is array_eq or record_eq,
+ * check to see if the element type or column types support equality.
+ * If not, array_eq or record_eq would fail at runtime, so we don't
+ * want to report that the type has equality.
+ */
+ if (eq_opr == ARRAY_EQ_OP &&
+ !array_element_has_equality(typentry))
+ eq_opr = InvalidOid;
+ else if (eq_opr == RECORD_EQ_OP &&
+ !record_fields_have_equality(typentry))
+ eq_opr = InvalidOid;
+
+ typentry->eq_opr = eq_opr;
/*
* Reset info about hash function whenever we pick up new info about
@@ -279,32 +315,70 @@ lookup_type_cache(Oid type_id, int flags)
}
if ((flags & TYPECACHE_LT_OPR) && typentry->lt_opr == InvalidOid)
{
+ Oid lt_opr = InvalidOid;
+
if (typentry->btree_opf != InvalidOid)
- typentry->lt_opr = get_opfamily_member(typentry->btree_opf,
- typentry->btree_opintype,
- typentry->btree_opintype,
- BTLessStrategyNumber);
+ lt_opr = get_opfamily_member(typentry->btree_opf,
+ typentry->btree_opintype,
+ typentry->btree_opintype,
+ BTLessStrategyNumber);
+
+ /* As above, make sure array_cmp or record_cmp will succeed */
+ if (lt_opr == ARRAY_LT_OP &&
+ !array_element_has_compare(typentry))
+ lt_opr = InvalidOid;
+ else if (lt_opr == RECORD_LT_OP &&
+ !record_fields_have_compare(typentry))
+ lt_opr = InvalidOid;
+
+ typentry->lt_opr = lt_opr;
}
if ((flags & TYPECACHE_GT_OPR) && typentry->gt_opr == InvalidOid)
{
+ Oid gt_opr = InvalidOid;
+
if (typentry->btree_opf != InvalidOid)
- typentry->gt_opr = get_opfamily_member(typentry->btree_opf,
- typentry->btree_opintype,
- typentry->btree_opintype,
- BTGreaterStrategyNumber);
+ gt_opr = get_opfamily_member(typentry->btree_opf,
+ typentry->btree_opintype,
+ typentry->btree_opintype,
+ BTGreaterStrategyNumber);
+
+ /* As above, make sure array_cmp or record_cmp will succeed */
+ if (gt_opr == ARRAY_GT_OP &&
+ !array_element_has_compare(typentry))
+ gt_opr = InvalidOid;
+ else if (gt_opr == RECORD_GT_OP &&
+ !record_fields_have_compare(typentry))
+ gt_opr = InvalidOid;
+
+ typentry->gt_opr = gt_opr;
}
if ((flags & (TYPECACHE_CMP_PROC | TYPECACHE_CMP_PROC_FINFO)) &&
typentry->cmp_proc == InvalidOid)
{
+ Oid cmp_proc = InvalidOid;
+
if (typentry->btree_opf != InvalidOid)
- typentry->cmp_proc = get_opfamily_proc(typentry->btree_opf,
- typentry->btree_opintype,
- typentry->btree_opintype,
- BTORDER_PROC);
+ cmp_proc = get_opfamily_proc(typentry->btree_opf,
+ typentry->btree_opintype,
+ typentry->btree_opintype,
+ BTORDER_PROC);
+
+ /* As above, make sure array_cmp or record_cmp will succeed */
+ if (cmp_proc == F_BTARRAYCMP &&
+ !array_element_has_compare(typentry))
+ cmp_proc = InvalidOid;
+ else if (cmp_proc == F_BTRECORDCMP &&
+ !record_fields_have_compare(typentry))
+ cmp_proc = InvalidOid;
+
+ typentry->cmp_proc = cmp_proc;
}
if ((flags & (TYPECACHE_HASH_PROC | TYPECACHE_HASH_PROC_FINFO)) &&
typentry->hash_proc == InvalidOid)
{
+ Oid hash_proc = InvalidOid;
+
/*
* We insist that the eq_opr, if one has been determined, match the
* hash opclass; else report there is no hash function.
@@ -315,10 +389,21 @@ lookup_type_cache(Oid type_id, int flags)
typentry->hash_opintype,
typentry->hash_opintype,
HTEqualStrategyNumber)))
- typentry->hash_proc = get_opfamily_proc(typentry->hash_opf,
- typentry->hash_opintype,
- typentry->hash_opintype,
- HASHPROC);
+ hash_proc = get_opfamily_proc(typentry->hash_opf,
+ typentry->hash_opintype,
+ typentry->hash_opintype,
+ HASHPROC);
+
+ /*
+ * As above, make sure hash_array will succeed. We don't currently
+ * support hashing for composite types, but when we do, we'll need
+ * more logic here to check that case too.
+ */
+ if (hash_proc == F_HASH_ARRAY &&
+ !array_element_has_hashing(typentry))
+ hash_proc = InvalidOid;
+
+ typentry->hash_proc = hash_proc;
}
/*
@@ -361,31 +446,166 @@ lookup_type_cache(Oid type_id, int flags)
typentry->tupDesc == NULL &&
typentry->typtype == TYPTYPE_COMPOSITE)
{
- Relation rel;
+ load_typcache_tupdesc(typentry);
+ }
- if (!OidIsValid(typentry->typrelid)) /* should not happen */
- elog(ERROR, "invalid typrelid for composite type %u",
- typentry->type_id);
- rel = relation_open(typentry->typrelid, AccessShareLock);
- Assert(rel->rd_rel->reltype == typentry->type_id);
+ return typentry;
+}
- /*
- * Link to the tupdesc and increment its refcount (we assert it's a
- * refcounted descriptor). We don't use IncrTupleDescRefCount() for
- * this, because the reference mustn't be entered in the current
- * resource owner; it can outlive the current query.
- */
- typentry->tupDesc = RelationGetDescr(rel);
+/*
+ * load_typcache_tupdesc --- helper routine to set up composite type's tupDesc
+ */
+static void
+load_typcache_tupdesc(TypeCacheEntry *typentry)
+{
+ Relation rel;
+
+ if (!OidIsValid(typentry->typrelid)) /* should not happen */
+ elog(ERROR, "invalid typrelid for composite type %u",
+ typentry->type_id);
+ rel = relation_open(typentry->typrelid, AccessShareLock);
+ Assert(rel->rd_rel->reltype == typentry->type_id);
+
+ /*
+ * Link to the tupdesc and increment its refcount (we assert it's a
+ * refcounted descriptor). We don't use IncrTupleDescRefCount() for
+ * this, because the reference mustn't be entered in the current
+ * resource owner; it can outlive the current query.
+ */
+ typentry->tupDesc = RelationGetDescr(rel);
+
+ Assert(typentry->tupDesc->tdrefcount > 0);
+ typentry->tupDesc->tdrefcount++;
+
+ relation_close(rel, AccessShareLock);
+}
+
+
+/*
+ * array_element_has_equality and friends are helper routines to check
+ * whether we should believe that array_eq and related functions will work
+ * on the given array type or composite type.
+ *
+ * The logic above may call these repeatedly on the same type entry, so we
+ * make use of the typentry->flags field to cache the results once known.
+ * Also, we assume that we'll probably want all these facts about the type
+ * if we want any, so we cache them all using only one lookup of the
+ * component datatype(s).
+ */
+
+static bool
+array_element_has_equality(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_array_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_EQUALITY) != 0;
+}
- Assert(typentry->tupDesc->tdrefcount > 0);
- typentry->tupDesc->tdrefcount++;
+static bool
+array_element_has_compare(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_array_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_COMPARE) != 0;
+}
+
+static bool
+array_element_has_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_array_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
+}
- relation_close(rel, AccessShareLock);
+static void
+cache_array_element_properties(TypeCacheEntry *typentry)
+{
+ Oid elem_type = get_base_element_type(typentry->type_id);
+
+ if (OidIsValid(elem_type))
+ {
+ TypeCacheEntry *elementry;
+
+ elementry = lookup_type_cache(elem_type,
+ TYPECACHE_EQ_OPR |
+ TYPECACHE_CMP_PROC |
+ TYPECACHE_HASH_PROC);
+ if (OidIsValid(elementry->eq_opr))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_EQUALITY;
+ if (OidIsValid(elementry->cmp_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_COMPARE;
+ if (OidIsValid(elementry->hash_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
}
+ typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
+}
- return typentry;
+static bool
+record_fields_have_equality(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_FIELD_PROPERTIES))
+ cache_record_field_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_FIELD_EQUALITY) != 0;
+}
+
+static bool
+record_fields_have_compare(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_FIELD_PROPERTIES))
+ cache_record_field_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_FIELD_COMPARE) != 0;
+}
+
+static void
+cache_record_field_properties(TypeCacheEntry *typentry)
+{
+ /*
+ * For type RECORD, we can't really tell what will work, since we don't
+ * have access here to the specific anonymous type. Just assume that
+ * everything will (we may get a failure at runtime ...)
+ */
+ if (typentry->type_id == RECORDOID)
+ typentry->flags |= (TCFLAGS_HAVE_FIELD_EQUALITY |
+ TCFLAGS_HAVE_FIELD_COMPARE);
+ else if (typentry->typtype == TYPTYPE_COMPOSITE)
+ {
+ TupleDesc tupdesc;
+ int newflags;
+ int i;
+
+ /* Fetch composite type's tupdesc if we don't have it already */
+ if (typentry->tupDesc == NULL)
+ load_typcache_tupdesc(typentry);
+ tupdesc = typentry->tupDesc;
+
+ /* Have each property if all non-dropped fields have the property */
+ newflags = (TCFLAGS_HAVE_FIELD_EQUALITY |
+ TCFLAGS_HAVE_FIELD_COMPARE);
+ for (i = 0; i < tupdesc->natts; i++)
+ {
+ TypeCacheEntry *fieldentry;
+
+ if (tupdesc->attrs[i]->attisdropped)
+ continue;
+
+ fieldentry = lookup_type_cache(tupdesc->attrs[i]->atttypid,
+ TYPECACHE_EQ_OPR |
+ TYPECACHE_CMP_PROC);
+ if (!OidIsValid(fieldentry->eq_opr))
+ newflags &= ~TCFLAGS_HAVE_FIELD_EQUALITY;
+ if (!OidIsValid(fieldentry->cmp_proc))
+ newflags &= ~TCFLAGS_HAVE_FIELD_COMPARE;
+
+ /* We can drop out of the loop once we disprove all bits */
+ if (newflags == 0)
+ break;
+ }
+ typentry->flags |= newflags;
+ }
+ typentry->flags |= TCFLAGS_CHECKED_FIELD_PROPERTIES;
}
+
/*
* lookup_rowtype_tupdesc_internal --- internal routine to lookup a rowtype
*
@@ -585,7 +805,8 @@ assign_record_type_typmod(TupleDesc tupDesc)
* Relcache inval callback function
*
* Delete the cached tuple descriptor (if any) for the given rel's composite
- * type, or for all composite types if relid == InvalidOid.
+ * type, or for all composite types if relid == InvalidOid. Also reset
+ * whatever info we have cached about the composite type's comparability.
*
* This is called when a relcache invalidation event occurs for the given
* relid. We must scan the whole typcache hash since we don't know the
@@ -611,12 +832,15 @@ TypeCacheRelCallback(Datum arg, Oid relid)
hash_seq_init(&status, TypeCacheHash);
while ((typentry = (TypeCacheEntry *) hash_seq_search(&status)) != NULL)
{
- if (typentry->tupDesc == NULL)
- continue; /* not composite, or tupdesc hasn't been
- * requested */
+ if (typentry->typtype != TYPTYPE_COMPOSITE)
+ continue; /* skip non-composites */
- /* Delete if match, or if we're zapping all composite types */
- if (relid == typentry->typrelid || relid == InvalidOid)
+ /* Skip if no match, unless we're zapping all composite types */
+ if (relid != typentry->typrelid && relid != InvalidOid)
+ continue;
+
+ /* Delete tupdesc if we have it */
+ if (typentry->tupDesc != NULL)
{
/*
* Release our refcount, and free the tupdesc if none remain.
@@ -628,6 +852,17 @@ TypeCacheRelCallback(Datum arg, Oid relid)
FreeTupleDesc(typentry->tupDesc);
typentry->tupDesc = NULL;
}
+
+ /* Reset equality/comparison/hashing information */
+ typentry->eq_opr = InvalidOid;
+ typentry->lt_opr = InvalidOid;
+ typentry->gt_opr = InvalidOid;
+ typentry->cmp_proc = InvalidOid;
+ typentry->hash_proc = InvalidOid;
+ typentry->eq_opr_finfo.fn_oid = InvalidOid;
+ typentry->cmp_proc_finfo.fn_oid = InvalidOid;
+ typentry->hash_proc_finfo.fn_oid = InvalidOid;
+ typentry->flags = 0;
}
}
diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h
index c9332777c1..64f1391b00 100644
--- a/src/include/catalog/pg_operator.h
+++ b/src/include/catalog/pg_operator.h
@@ -1647,12 +1647,15 @@ DESCR("text search match");
/* generic record comparison operators */
DATA(insert OID = 2988 ( "=" PGNSP PGUID b t f 2249 2249 16 2988 2989 record_eq eqsel eqjoinsel ));
DESCR("equal");
+#define RECORD_EQ_OP 2988
DATA(insert OID = 2989 ( "<>" PGNSP PGUID b f f 2249 2249 16 2989 2988 record_ne neqsel neqjoinsel ));
DESCR("not equal");
DATA(insert OID = 2990 ( "<" PGNSP PGUID b f f 2249 2249 16 2991 2993 record_lt scalarltsel scalarltjoinsel ));
DESCR("less than");
+#define RECORD_LT_OP 2990
DATA(insert OID = 2991 ( ">" PGNSP PGUID b f f 2249 2249 16 2990 2992 record_gt scalargtsel scalargtjoinsel ));
DESCR("greater than");
+#define RECORD_GT_OP 2991
DATA(insert OID = 2992 ( "<=" PGNSP PGUID b f f 2249 2249 16 2993 2991 record_le scalarltsel scalarltjoinsel ));
DESCR("less than or equal");
DATA(insert OID = 2993 ( ">=" PGNSP PGUID b f f 2249 2249 16 2992 2990 record_ge scalargtsel scalargtjoinsel ));
diff --git a/src/include/port.h b/src/include/port.h
index 6ea681f16a..4c7ed64317 100644
--- a/src/include/port.h
+++ b/src/include/port.h
@@ -395,6 +395,10 @@ extern void srand48(long seed);
extern int getopt(int nargc, char *const * nargv, const char *ostr);
#endif
+#if !defined(HAVE_GETPEEREID) && !defined(WIN32)
+extern int getpeereid(int sock, uid_t *uid, gid_t *gid);
+#endif
+
#ifndef HAVE_ISINF
extern int isinf(double x);
#endif
diff --git a/src/include/storage/predicate_internals.h b/src/include/storage/predicate_internals.h
index bd42004c2c..b144ab319a 100644
--- a/src/include/storage/predicate_internals.h
+++ b/src/include/storage/predicate_internals.h
@@ -92,6 +92,11 @@ typedef struct SERIALIZABLEXACT
#define SXACT_FLAG_ROLLED_BACK 0x00000001
#define SXACT_FLAG_COMMITTED 0x00000002
+/*
+ * The following flag actually means that the flagged transaction has a
+ * conflict out *to a transaction which committed ahead of it*. It's hard
+ * to get that into a name of a reasonable length.
+ */
#define SXACT_FLAG_CONFLICT_OUT 0x00000004
#define SXACT_FLAG_READ_ONLY 0x00000008
#define SXACT_FLAG_DID_WRITE 0x00000010
diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h
index e2f8c9ce3c..eb93c1d3b5 100644
--- a/src/include/utils/typcache.h
+++ b/src/include/utils/typcache.h
@@ -39,7 +39,9 @@ typedef struct TypeCacheEntry
* Information obtained from opfamily entries
*
* These will be InvalidOid if no match could be found, or if the
- * information hasn't yet been requested.
+ * information hasn't yet been requested. Also note that for array and
+ * composite types, typcache.c checks that the contained types are
+ * comparable or hashable before allowing eq_opr etc to become set.
*/
Oid btree_opf; /* the default btree opclass' family */
Oid btree_opintype; /* the default btree opclass' opcintype */
@@ -55,8 +57,8 @@ typedef struct TypeCacheEntry
* Pre-set-up fmgr call info for the equality operator, the btree
* comparison function, and the hash calculation function. These are kept
* in the type cache to avoid problems with memory leaks in repeated calls
- * to array_eq, array_cmp, hash_array. There is not currently a need to
- * maintain call info for the lt_opr or gt_opr.
+ * to functions such as array_eq, array_cmp, hash_array. There is not
+ * currently a need to maintain call info for the lt_opr or gt_opr.
*/
FmgrInfo eq_opr_finfo;
FmgrInfo cmp_proc_finfo;
@@ -69,6 +71,9 @@ typedef struct TypeCacheEntry
*/
TupleDesc tupDesc;
+ /* Private data, for internal use of typcache.c only */
+ int flags; /* flags about what we've computed */
+
/*
* Private information about an enum type. NULL if not enum or
* information hasn't been requested.
diff --git a/src/interfaces/libpq/.gitignore b/src/interfaces/libpq/.gitignore
index e79f3872cb..829278fcb0 100644
--- a/src/interfaces/libpq/.gitignore
+++ b/src/interfaces/libpq/.gitignore
@@ -2,6 +2,7 @@
/chklocale.c
/crypt.c
/getaddrinfo.c
+/getpeereid.c
/inet_aton.c
/inet_net_ntop.c
/noblock.c
diff --git a/src/interfaces/libpq/Makefile b/src/interfaces/libpq/Makefile
index 18795446d5..9d4b313b85 100644
--- a/src/interfaces/libpq/Makefile
+++ b/src/interfaces/libpq/Makefile
@@ -37,7 +37,7 @@ OBJS= fe-auth.o fe-connect.o fe-exec.o fe-misc.o fe-print.o fe-lobj.o \
# libpgport C files we always use
OBJS += chklocale.o inet_net_ntop.o noblock.o pgstrcasecmp.o thread.o
# libpgport C files that are needed if identified by configure
-OBJS += $(filter crypt.o getaddrinfo.o inet_aton.o open.o snprintf.o strerror.o strlcpy.o win32error.o, $(LIBOBJS))
+OBJS += $(filter crypt.o getaddrinfo.o getpeereid.o inet_aton.o open.o snprintf.o strerror.o strlcpy.o win32error.o, $(LIBOBJS))
# backend/libpq
OBJS += ip.o md5.o
# utils/mb
@@ -88,7 +88,7 @@ backend_src = $(top_srcdir)/src/backend
# For some libpgport modules, this only happens if configure decides
# the module is needed (see filter hack in OBJS, above).
-chklocale.c crypt.c getaddrinfo.c inet_aton.c inet_net_ntop.c noblock.c open.c pgsleep.c pgstrcasecmp.c snprintf.c strerror.c strlcpy.c thread.c win32error.c: % : $(top_srcdir)/src/port/%
+chklocale.c crypt.c getaddrinfo.c getpeereid.c inet_aton.c inet_net_ntop.c noblock.c open.c pgsleep.c pgstrcasecmp.c snprintf.c strerror.c strlcpy.c thread.c win32error.c: % : $(top_srcdir)/src/port/%
rm -f $@ && $(LN_S) $< .
ip.c md5.c: % : $(backend_src)/libpq/%
@@ -135,7 +135,7 @@ clean distclean: clean-lib
# Might be left over from a Win32 client-only build
rm -f pg_config_paths.h
rm -f inet_net_ntop.c noblock.c pgstrcasecmp.c thread.c
- rm -f chklocale.c crypt.c getaddrinfo.c inet_aton.c open.c snprintf.c strerror.c strlcpy.c win32error.c
+ rm -f chklocale.c crypt.c getaddrinfo.c getpeereid.c inet_aton.c open.c snprintf.c strerror.c strlcpy.c win32error.c
rm -f pgsleep.c
rm -f md5.c ip.c
rm -f encnames.c wchar.c
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 5a6502fff4..9aa6ca01eb 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -21,12 +21,6 @@
#include <ctype.h>
#include <time.h>
#include <unistd.h>
-#ifdef HAVE_UCRED_H
-#include <ucred.h>
-#endif
-#ifdef HAVE_SYS_UCRED_H
-#include <sys/ucred.h>
-#endif
#include "libpq-fe.h"
#include "libpq-int.h"
@@ -1859,6 +1853,7 @@ keep_going: /* We will come back to here until there is
char *startpacket;
int packetlen;
+#ifdef HAVE_UNIX_SOCKETS
/*
* Implement requirepeer check, if requested and it's a
* Unix-domain socket.
@@ -1866,82 +1861,25 @@ keep_going: /* We will come back to here until there is
if (conn->requirepeer && conn->requirepeer[0] &&
IS_AF_UNIX(conn->raddr.addr.ss_family))
{
-#if defined(HAVE_GETPEEREID) || defined(SO_PEERCRED) || defined(LOCAL_PEERCRED) || defined(HAVE_GETPEERUCRED)
char pwdbuf[BUFSIZ];
struct passwd pass_buf;
struct passwd *pass;
uid_t uid;
-
-#if defined(HAVE_GETPEEREID)
- /* Most BSDen, including OS X: use getpeereid() */
gid_t gid;
errno = 0;
if (getpeereid(conn->sock, &uid, &gid) != 0)
{
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not get peer credentials: %s\n"),
- pqStrerror(errno, sebuf, sizeof(sebuf)));
- goto error_return;
- }
-#elif defined(SO_PEERCRED)
- /* Linux: use getsockopt(SO_PEERCRED) */
- struct ucred peercred;
- ACCEPT_TYPE_ARG3 so_len = sizeof(peercred);
-
- errno = 0;
- if (getsockopt(conn->sock, SOL_SOCKET, SO_PEERCRED,
- &peercred, &so_len) != 0 ||
- so_len != sizeof(peercred))
- {
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not get peer credentials: %s\n"),
- pqStrerror(errno, sebuf, sizeof(sebuf)));
- goto error_return;
- }
- uid = peercred.uid;
-#elif defined(LOCAL_PEERCRED)
- /* Debian with FreeBSD kernel: use LOCAL_PEERCRED */
- struct xucred peercred;
- ACCEPT_TYPE_ARG3 so_len = sizeof(peercred);
-
- errno = 0;
- if (getsockopt(conn->sock, 0, LOCAL_PEERCRED,
- &peercred, &so_len) != 0 ||
- so_len != sizeof(peercred) ||
- peercred.cr_version != XUCRED_VERSION)
- {
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not get peer credentials: %s\n"),
- pqStrerror(errno, sebuf, sizeof(sebuf)));
- goto error_return;
- }
- uid = peercred.cr_uid;
-#elif defined(HAVE_GETPEERUCRED)
- /* Solaris: use getpeerucred() */
- ucred_t *ucred;
-
- ucred = NULL; /* must be initialized to NULL */
- if (getpeerucred(conn->sock, &ucred) == -1)
- {
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not get peer credentials: %s\n"),
- pqStrerror(errno, sebuf, sizeof(sebuf)));
- goto error_return;
- }
-
- if ((uid = ucred_geteuid(ucred)) == -1)
- {
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not get effective UID from peer credentials: %s\n"),
- pqStrerror(errno, sebuf, sizeof(sebuf)));
- ucred_free(ucred);
+ /* Provide special error message if getpeereid is a stub */
+ if (errno == ENOSYS)
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("requirepeer parameter is not supported on this platform\n"));
+ else
+ appendPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not get peer credentials: %s\n"),
+ pqStrerror(errno, sebuf, sizeof(sebuf)));
goto error_return;
}
- ucred_free(ucred);
-#else
-#error missing implementation method for requirepeer
-#endif
pqGetpwuid(uid, &pass_buf, pwdbuf, sizeof(pwdbuf), &pass);
@@ -1960,12 +1898,8 @@ keep_going: /* We will come back to here until there is
conn->requirepeer, pass->pw_name);
goto error_return;
}
-#else /* can't support requirepeer */
- appendPQExpBuffer(&conn->errorMessage,
- libpq_gettext("requirepeer parameter is not supported on this platform\n"));
- goto error_return;
-#endif
}
+#endif /* HAVE_UNIX_SOCKETS */
#ifdef USE_SSL
diff --git a/src/port/getpeereid.c b/src/port/getpeereid.c
new file mode 100644
index 0000000000..e10a140624
--- /dev/null
+++ b/src/port/getpeereid.c
@@ -0,0 +1,80 @@
+/*-------------------------------------------------------------------------
+ *
+ * getpeereid.c
+ * get peer userid for UNIX-domain socket connection
+ *
+ * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ * src/port/getpeereid.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "c.h"
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+#ifdef HAVE_UCRED_H
+#include <ucred.h>
+#endif
+#ifdef HAVE_SYS_UCRED_H
+#include <sys/ucred.h>
+#endif
+
+
+/*
+ * BSD-style getpeereid() for platforms that lack it.
+ */
+int
+getpeereid(int sock, uid_t *uid, gid_t *gid)
+{
+#if defined(SO_PEERCRED)
+ /* Linux: use getsockopt(SO_PEERCRED) */
+ struct ucred peercred;
+ ACCEPT_TYPE_ARG3 so_len = sizeof(peercred);
+
+ if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &peercred, &so_len) != 0 ||
+ so_len != sizeof(peercred))
+ return -1;
+ *uid = peercred.uid;
+ *gid = peercred.gid;
+ return 0;
+#elif defined(LOCAL_PEERCRED)
+ /* Debian with FreeBSD kernel: use getsockopt(LOCAL_PEERCRED) */
+ struct xucred peercred;
+ ACCEPT_TYPE_ARG3 so_len = sizeof(peercred);
+
+ if (getsockopt(sock, 0, LOCAL_PEERCRED, &peercred, &so_len) != 0 ||
+ so_len != sizeof(peercred) ||
+ peercred.cr_version != XUCRED_VERSION)
+ return -1;
+ *uid = peercred.cr_uid;
+ *gid = peercred.cr_gid;
+ return 0;
+#elif defined(HAVE_GETPEERUCRED)
+ /* Solaris: use getpeerucred() */
+ ucred_t *ucred;
+
+ ucred = NULL; /* must be initialized to NULL */
+ if (getpeerucred(sock, &ucred) == -1)
+ return -1;
+
+ *uid = ucred_geteuid(ucred);
+ *gid = ucred_getegid(ucred);
+ ucred_free(ucred);
+
+ if (*uid == (uid_t)(-1) || *gid == (gid_t)(-1))
+ return -1;
+ return 0;
+#else
+ /* No implementation available on this platform */
+ errno = ENOSYS;
+ return -1;
+#endif
+}
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index a200f8fe3d..8f11b238f7 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -1516,6 +1516,9 @@ alter table recur1 add column f2 recur1; -- fails
ERROR: composite type recur1 cannot be made a member of itself
alter table recur1 add column f2 recur1[]; -- fails
ERROR: composite type recur1 cannot be made a member of itself
+create domain array_of_recur1 as recur1[];
+alter table recur1 add column f2 array_of_recur1; -- fails
+ERROR: composite type recur1 cannot be made a member of itself
create temp table recur2 (f1 int, f2 recur1);
alter table recur1 add column f2 recur2; -- fails
ERROR: composite type recur1 cannot be made a member of itself
diff --git a/src/test/regress/expected/rowtypes.out b/src/test/regress/expected/rowtypes.out
index e5cd71421c..9430bd9b48 100644
--- a/src/test/regress/expected/rowtypes.out
+++ b/src/test/regress/expected/rowtypes.out
@@ -286,6 +286,16 @@ select row(1,1.1) = any (array[ row(7,7.7), row(1,1.0), row(0,0.0) ]);
f
(1 row)
+-- Check behavior with a non-comparable rowtype
+create type cantcompare as (p point, r float8);
+create temp table cc (f1 cantcompare);
+insert into cc values('("(1,2)",3)');
+insert into cc values('("(4,5)",6)');
+select * from cc order by f1; -- fail, but should complain about cantcompare
+ERROR: could not identify an ordering operator for type cantcompare
+LINE 1: select * from cc order by f1;
+ ^
+HINT: Use an explicit ordering operator or modify the query.
--
-- Test case derived from bug #5716: check multiple uses of a rowtype result
--
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index 0ed16fb7cf..b5d76ea68e 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -1128,6 +1128,8 @@ alter table tab1 alter column b type varchar; -- fails
create temp table recur1 (f1 int);
alter table recur1 add column f2 recur1; -- fails
alter table recur1 add column f2 recur1[]; -- fails
+create domain array_of_recur1 as recur1[];
+alter table recur1 add column f2 array_of_recur1; -- fails
create temp table recur2 (f1 int, f2 recur1);
alter table recur1 add column f2 recur2; -- fails
alter table recur1 add column f2 int;
diff --git a/src/test/regress/sql/rowtypes.sql b/src/test/regress/sql/rowtypes.sql
index 9041df147f..55e1ff9a9e 100644
--- a/src/test/regress/sql/rowtypes.sql
+++ b/src/test/regress/sql/rowtypes.sql
@@ -118,6 +118,13 @@ select array[ row(1,2), row(3,4), row(5,6) ];
select row(1,1.1) = any (array[ row(7,7.7), row(1,1.1), row(0,0.0) ]);
select row(1,1.1) = any (array[ row(7,7.7), row(1,1.0), row(0,0.0) ]);
+-- Check behavior with a non-comparable rowtype
+create type cantcompare as (p point, r float8);
+create temp table cc (f1 cantcompare);
+insert into cc values('("(1,2)",3)');
+insert into cc values('("(4,5)",6)');
+select * from cc order by f1; -- fail, but should complain about cantcompare
+
--
-- Test case derived from bug #5716: check multiple uses of a rowtype result
--