diff options
author | Bernd Helmle | 2011-06-04 13:56:43 +0000 |
---|---|---|
committer | Bernd Helmle | 2011-06-04 13:56:43 +0000 |
commit | 820e91132277e492d4a09737ef3530b8a53649dd (patch) | |
tree | b50efc1dd556ed4280bf5f33d4249cfa9947a3c1 | |
parent | 337798e4985da6192bc4b6335fc188588b05197b (diff) | |
parent | 048417511aef8d5fb2d541b17b73afc730935cd5 (diff) |
Merge branch 'master' of ../bernd_pg into notnull_constraint
30 files changed, 598 insertions, 320 deletions
@@ -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(&var_buf, 0, sizeof(var_buf)); - memcpy(&var_buf, sqldata, (sizeof(var_buf) <= sqllen ? sizeof(var_buf) - 1 : sqllen)); + memcpy(&var_buf, sqldata, (sizeof(var_buf) <= sqllen ? sizeof(var_buf) - 1 : sqllen)); break; case ECPGt_int: /* integer */ @@ -4390,7 +4390,7 @@ main(void) case ECPGt_char: memset(&var_buf, 0, sizeof(var_buf)); - memcpy(&var_buf, sqldata, (sizeof(var_buf) <= sqllen ? sizeof(var_buf)-1 : sqllen)); + memcpy(&var_buf, sqldata, (sizeof(var_buf) <= 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 < 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 < 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 < 0) printf("lo_write() failed\n"); rc = lo_close(conn, fd); - if (rc < 0) + if (rc < 0) printf("lo_close() failed: %s", PQerrorMessage(conn)); /* read test */ fd = lo_open(conn, loid, INV_READ); - if (fd < 0) + if (fd < 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 < 0) printf("lo_read() failed\n"); rc = lo_close(conn, fd); - if (rc < 0) + if (rc < 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 < 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 -- |