* heap.c
* code to create and destroy POSTGRES heap relations
*
- * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.345 2008/11/19 10:34:51 heikki Exp $
+ * src/backend/catalog/heap.c
*
*
* INTERFACE ROUTINES
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/indexing.h"
+#include "catalog/namespace.h"
+#include "catalog/objectaccess.h"
#include "catalog/pg_attrdef.h"
+#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
+#include "catalog/pg_foreign_table.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_statistic.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/var.h"
#include "parser/parse_coerce.h"
+#include "parser/parse_collate.h"
#include "parser/parse_expr.h"
#include "parser/parse_relation.h"
#include "storage/bufmgr.h"
#include "storage/freespace.h"
#include "storage/smgr.h"
+#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/tqual.h"
+/* Potentially set by contrib/pg_upgrade_support functions */
+Oid binary_upgrade_next_heap_pg_class_oid = InvalidOid;
+Oid binary_upgrade_next_toast_pg_class_oid = InvalidOid;
+
static void AddNewRelationTuple(Relation pg_class_desc,
Relation new_rel_desc,
- Oid new_rel_oid, Oid new_type_oid,
+ Oid new_rel_oid,
+ Oid new_type_oid,
+ Oid reloftype,
Oid relowner,
char relkind,
+ Datum relacl,
Datum reloptions);
static Oid AddNewRelationType(const char *typeName,
Oid typeNamespace,
Oid new_rel_oid,
char new_rel_kind,
+ Oid ownerid,
+ Oid new_row_type,
Oid new_array_type);
static void RelationRemoveInheritance(Oid relid);
static void StoreRelCheck(Relation rel, char *ccname, Node *expr,
- bool is_local, int inhcount);
+ bool is_local, int inhcount);
static void StoreConstraints(Relation rel, List *cooked_constraints);
static bool MergeWithExistingConstraint(Relation rel, char *ccname, Node *expr,
- bool allow_merge, bool is_local);
+ bool allow_merge, bool is_local);
static void SetRelationNumChecks(Relation rel, int numchecks);
static Node *cookConstraint(ParseState *pstate,
Node *raw_constraint,
* Disadvantage: special cases will be all over the place.
*/
+/*
+ * The initializers below do not include the attoptions or attacl fields,
+ * but that's OK - we're never going to reference anything beyond the
+ * fixed-size portion of the structure anyway.
+ */
+
static FormData_pg_attribute a1 = {
0, {"ctid"}, TIDOID, 0, sizeof(ItemPointerData),
SelfItemPointerAttributeNumber, 0, -1, -1,
Oid relid,
TupleDesc tupDesc,
char relkind,
+ char relpersistence,
bool shared_relation,
+ bool mapped_relation,
bool allow_system_table_mods)
{
bool create_storage;
{
case RELKIND_VIEW:
case RELKIND_COMPOSITE_TYPE:
+ case RELKIND_FOREIGN_TABLE:
create_storage = false;
/*
tupDesc,
relid,
reltablespace,
- shared_relation);
+ shared_relation,
+ mapped_relation,
+ relpersistence);
/*
* Have the storage manager create the relation's disk file, if needed.
if (create_storage)
{
RelationOpenSmgr(rel);
- RelationCreateStorage(rel->rd_node, rel->rd_istemp);
+ RelationCreateStorage(rel->rd_node, relpersistence);
}
return rel;
* --------------------------------
*/
void
-CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind)
+CheckAttributeNamesTypes(TupleDesc tupdesc, char relkind,
+ bool allow_system_table_mods)
{
int i;
int j;
for (i = 0; i < natts; i++)
{
CheckAttributeType(NameStr(tupdesc->attrs[i]->attname),
- tupdesc->attrs[i]->atttypid);
+ tupdesc->attrs[i]->atttypid,
+ tupdesc->attrs[i]->attcollation,
+ allow_system_table_mods);
}
}
* --------------------------------
*/
void
-CheckAttributeType(const char *attname, Oid atttypid)
+CheckAttributeType(const char *attname, Oid atttypid, Oid attcollation,
+ bool allow_system_table_mods)
{
char att_typtype = get_typtype(atttypid);
{
/*
* Refuse any attempt to create a pseudo-type column, except for a
- * special hack for pg_statistic: allow ANYARRAY during initdb
+ * special hack for pg_statistic: allow ANYARRAY when modifying system
+ * catalogs (this allows creating pg_statistic and cloning it during
+ * VACUUM FULL)
*/
- if (atttypid != ANYARRAYOID || IsUnderPostmaster)
+ if (atttypid != ANYARRAYOID || !allow_system_table_mods)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
errmsg("column \"%s\" has pseudo-type %s",
if (attr->attisdropped)
continue;
- CheckAttributeType(NameStr(attr->attname), attr->atttypid);
+ CheckAttributeType(NameStr(attr->attname), attr->atttypid, attr->attcollation,
+ allow_system_table_mods);
}
relation_close(relation, AccessShareLock);
}
+
+ /*
+ * This might not be strictly invalid per SQL standard, but it is
+ * pretty useless, and it cannot be dumped, so we must disallow
+ * it.
+ */
+ if (type_is_collatable(atttypid) && !OidIsValid(attcollation))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ errmsg("no collation was derived for column \"%s\" with collatable type %s",
+ attname, format_type_be(atttypid)),
+ errhint("Use the COLLATE clause to set the collation explicitly.")));
}
/*
* Construct and insert a new tuple in pg_attribute.
*
* Caller has already opened and locked pg_attribute. new_attribute is the
- * attribute to insert.
+ * attribute to insert (but we ignore attacl and attoptions, which are always
+ * initialized to NULL).
*
- * indstate is the index state for CatalogIndexInsert. It can be passed as
+ * indstate is the index state for CatalogIndexInsert. It can be passed as
* NULL, in which case we'll fetch the necessary info. (Don't do this when
* inserting multiple attributes, because it's a tad more expensive.)
*/
values[Anum_pg_attribute_attisdropped - 1] = BoolGetDatum(new_attribute->attisdropped);
values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(new_attribute->attislocal);
values[Anum_pg_attribute_attinhcount - 1] = Int32GetDatum(new_attribute->attinhcount);
+ values[Anum_pg_attribute_attcollation - 1] = ObjectIdGetDatum(new_attribute->attcollation);
+
+ /* start out with empty permissions and empty options */
+ nulls[Anum_pg_attribute_attacl - 1] = true;
+ nulls[Anum_pg_attribute_attoptions - 1] = true;
tup = heap_form_tuple(RelationGetDescr(pg_attribute_rel), values, nulls);
heap_freetuple(tup);
}
+
/* --------------------------------
* AddNewAttributeTuples
*
referenced.objectId = attr->atttypid;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ if (OidIsValid(attr->attcollation))
+ {
+ referenced.classId = CollationRelationId;
+ referenced.objectId = attr->attcollation;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
}
/*
* Caller has already opened and locked pg_class.
* Tuple data is taken from new_rel_desc->rd_rel, except for the
* variable-width fields which are not present in a cached reldesc.
- * We always initialize relacl to NULL (i.e., default permissions),
- * and reloptions is set to the passed-in text array (if any).
+ * relacl and reloptions are passed in Datum form (to avoid having
+ * to reference the data types in heap.h). Pass (Datum) 0 to set them
+ * to NULL.
* --------------------------------
*/
void
InsertPgClassTuple(Relation pg_class_desc,
Relation new_rel_desc,
Oid new_rel_oid,
+ Datum relacl,
Datum reloptions)
{
Form_pg_class rd_rel = new_rel_desc->rd_rel;
values[Anum_pg_class_relname - 1] = NameGetDatum(&rd_rel->relname);
values[Anum_pg_class_relnamespace - 1] = ObjectIdGetDatum(rd_rel->relnamespace);
values[Anum_pg_class_reltype - 1] = ObjectIdGetDatum(rd_rel->reltype);
+ values[Anum_pg_class_reloftype - 1] = ObjectIdGetDatum(rd_rel->reloftype);
values[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(rd_rel->relowner);
values[Anum_pg_class_relam - 1] = ObjectIdGetDatum(rd_rel->relam);
values[Anum_pg_class_relfilenode - 1] = ObjectIdGetDatum(rd_rel->relfilenode);
values[Anum_pg_class_reltoastidxid - 1] = ObjectIdGetDatum(rd_rel->reltoastidxid);
values[Anum_pg_class_relhasindex - 1] = BoolGetDatum(rd_rel->relhasindex);
values[Anum_pg_class_relisshared - 1] = BoolGetDatum(rd_rel->relisshared);
+ values[Anum_pg_class_relpersistence - 1] = CharGetDatum(rd_rel->relpersistence);
values[Anum_pg_class_relkind - 1] = CharGetDatum(rd_rel->relkind);
values[Anum_pg_class_relnatts - 1] = Int16GetDatum(rd_rel->relnatts);
values[Anum_pg_class_relchecks - 1] = Int16GetDatum(rd_rel->relchecks);
values[Anum_pg_class_relhastriggers - 1] = BoolGetDatum(rd_rel->relhastriggers);
values[Anum_pg_class_relhassubclass - 1] = BoolGetDatum(rd_rel->relhassubclass);
values[Anum_pg_class_relfrozenxid - 1] = TransactionIdGetDatum(rd_rel->relfrozenxid);
- /* start out with empty permissions */
- nulls[Anum_pg_class_relacl - 1] = true;
+ if (relacl != (Datum) 0)
+ values[Anum_pg_class_relacl - 1] = relacl;
+ else
+ nulls[Anum_pg_class_relacl - 1] = true;
if (reloptions != (Datum) 0)
values[Anum_pg_class_reloptions - 1] = reloptions;
else
Relation new_rel_desc,
Oid new_rel_oid,
Oid new_type_oid,
+ Oid reloftype,
Oid relowner,
char relkind,
+ Datum relacl,
Datum reloptions)
{
Form_pg_class new_rel_reltup;
new_rel_reltup->relowner = relowner;
new_rel_reltup->reltype = new_type_oid;
+ new_rel_reltup->reloftype = reloftype;
new_rel_reltup->relkind = relkind;
new_rel_desc->rd_att->tdtypeid = new_type_oid;
/* Now build and insert the tuple */
- InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid, reloptions);
+ InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid,
+ relacl, reloptions);
}
Oid typeNamespace,
Oid new_rel_oid,
char new_rel_kind,
+ Oid ownerid,
+ Oid new_row_type,
Oid new_array_type)
{
return
- TypeCreate(InvalidOid, /* no predetermined OID */
+ TypeCreate(new_row_type, /* optional predetermined OID */
typeName, /* type name */
typeNamespace, /* type namespace */
new_rel_oid, /* relation oid */
new_rel_kind, /* relation kind */
+ ownerid, /* owner's ID */
-1, /* internal size (varlena) */
TYPTYPE_COMPOSITE, /* type-type (composite) */
- TYPCATEGORY_COMPOSITE, /* type-category (ditto) */
+ TYPCATEGORY_COMPOSITE, /* type-category (ditto) */
false, /* composite types are never preferred */
DEFAULT_TYPDELIM, /* default array delimiter */
F_RECORD_IN, /* input procedure */
'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
- false); /* Type NOT NULL */
+ false, /* Type NOT NULL */
+ InvalidOid); /* typcollation */
}
/* --------------------------------
* heap_create_with_catalog
*
* creates a new cataloged relation. see comments above.
+ *
+ * Arguments:
+ * relname: name to give to new rel
+ * relnamespace: OID of namespace it goes in
+ * reltablespace: OID of tablespace it goes in
+ * relid: OID to assign to new rel, or InvalidOid to select a new OID
+ * reltypeid: OID to assign to rel's rowtype, or InvalidOid to select one
+ * ownerid: OID of new rel's owner
+ * tupdesc: tuple descriptor (source of column definitions)
+ * cooked_constraints: list of precooked check constraints and defaults
+ * relkind: relkind for new rel
+ * shared_relation: TRUE if it's to be a shared relation
+ * mapped_relation: TRUE if the relation will use the relfilenode map
+ * oidislocal: TRUE if oid column (if any) should be marked attislocal
+ * oidinhcount: attinhcount to assign to oid column (if any)
+ * oncommit: ON COMMIT marking (only relevant if it's a temp table)
+ * reloptions: reloptions in Datum form, or (Datum) 0 if none
+ * use_user_acl: TRUE if should look for user-defined default permissions;
+ * if FALSE, relacl is always set NULL
+ * allow_system_table_mods: TRUE to allow creation in system namespaces
+ *
+ * Returns the OID of the new relation
* --------------------------------
*/
Oid
Oid relnamespace,
Oid reltablespace,
Oid relid,
+ Oid reltypeid,
+ Oid reloftypeid,
Oid ownerid,
TupleDesc tupdesc,
List *cooked_constraints,
char relkind,
+ char relpersistence,
bool shared_relation,
+ bool mapped_relation,
bool oidislocal,
int oidinhcount,
OnCommitAction oncommit,
Datum reloptions,
- bool allow_system_table_mods)
+ bool use_user_acl,
+ bool allow_system_table_mods,
+ bool if_not_exists)
{
Relation pg_class_desc;
Relation new_rel_desc;
+ Acl *relacl;
+ Oid existing_relid;
Oid old_type_oid;
Oid new_type_oid;
Oid new_array_oid = InvalidOid;
*/
Assert(IsNormalProcessingMode() || IsBootstrapProcessingMode());
- CheckAttributeNamesTypes(tupdesc, relkind);
+ CheckAttributeNamesTypes(tupdesc, relkind, allow_system_table_mods);
- if (get_relname_relid(relname, relnamespace))
+ /*
+ * If the relation already exists, it's an error, unless the user specifies
+ * "IF NOT EXISTS". In that case, we just print a notice and do nothing
+ * further.
+ */
+ existing_relid = get_relname_relid(relname, relnamespace);
+ if (existing_relid != InvalidOid)
+ {
+ if (if_not_exists)
+ {
+ ereport(NOTICE,
+ (errcode(ERRCODE_DUPLICATE_TABLE),
+ errmsg("relation \"%s\" already exists, skipping",
+ relname)));
+ heap_close(pg_class_desc, RowExclusiveLock);
+ return InvalidOid;
+ }
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_TABLE),
errmsg("relation \"%s\" already exists", relname)));
+ }
/*
* Since we are going to create a rowtype as well, also check for
* autogenerated array, we can rename it out of the way; otherwise we can
* at least give a good error message.
*/
- old_type_oid = GetSysCacheOid(TYPENAMENSP,
- CStringGetDatum(relname),
- ObjectIdGetDatum(relnamespace),
- 0, 0);
+ old_type_oid = GetSysCacheOid2(TYPENAMENSP,
+ CStringGetDatum(relname),
+ ObjectIdGetDatum(relnamespace));
if (OidIsValid(old_type_oid))
{
if (!moveArrayTypeName(old_type_oid, relname, relnamespace))
}
/*
- * Validate shared/non-shared tablespace (must check this before doing
- * GetNewRelFileNode, to prevent Assert therein)
+ * Shared relations must be in pg_global (last-ditch check)
*/
- if (shared_relation)
- {
- if (reltablespace != GLOBALTABLESPACE_OID)
- /* elog since this is not a user-facing error */
- elog(ERROR,
- "shared relations must be placed in pg_global tablespace");
- }
- else
- {
- if (reltablespace == GLOBALTABLESPACE_OID)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("only shared relations can be placed in pg_global tablespace")));
- }
+ if (shared_relation && reltablespace != GLOBALTABLESPACE_OID)
+ elog(ERROR, "shared relations must be placed in pg_global tablespace");
/*
* Allocate an OID for the relation, unless we were told what to use.
* collide with either pg_class OIDs or existing physical files.
*/
if (!OidIsValid(relid))
- relid = GetNewRelFileNode(reltablespace, shared_relation,
- pg_class_desc);
+ {
+ /*
+ * Use binary-upgrade override for pg_class.oid/relfilenode,
+ * if supplied.
+ */
+ if (OidIsValid(binary_upgrade_next_heap_pg_class_oid) &&
+ (relkind == RELKIND_RELATION || relkind == RELKIND_SEQUENCE ||
+ relkind == RELKIND_VIEW || relkind == RELKIND_COMPOSITE_TYPE ||
+ relkind == RELKIND_FOREIGN_TABLE))
+ {
+ relid = binary_upgrade_next_heap_pg_class_oid;
+ binary_upgrade_next_heap_pg_class_oid = InvalidOid;
+ }
+ else if (OidIsValid(binary_upgrade_next_toast_pg_class_oid) &&
+ relkind == RELKIND_TOASTVALUE)
+ {
+ relid = binary_upgrade_next_toast_pg_class_oid;
+ binary_upgrade_next_toast_pg_class_oid = InvalidOid;
+ }
+ else
+ relid = GetNewRelFileNode(reltablespace, pg_class_desc,
+ relpersistence);
+ }
+
+ /*
+ * Determine the relation's initial permissions.
+ */
+ if (use_user_acl)
+ {
+ switch (relkind)
+ {
+ case RELKIND_RELATION:
+ case RELKIND_VIEW:
+ case RELKIND_FOREIGN_TABLE:
+ relacl = get_user_default_acl(ACL_OBJECT_RELATION, ownerid,
+ relnamespace);
+ break;
+ case RELKIND_SEQUENCE:
+ relacl = get_user_default_acl(ACL_OBJECT_SEQUENCE, ownerid,
+ relnamespace);
+ break;
+ default:
+ relacl = NULL;
+ break;
+ }
+ }
+ else
+ relacl = NULL;
/*
* Create the relcache entry (mostly dummy at this point) and the physical
relid,
tupdesc,
relkind,
+ relpersistence,
shared_relation,
+ mapped_relation,
allow_system_table_mods);
Assert(relid == RelationGetRelid(new_rel_desc));
* Decide whether to create an array type over the relation's rowtype. We
* do not create any array types for system catalogs (ie, those made
* during initdb). We create array types for regular relations, views,
- * and composite types ... but not, eg, for toast tables or sequences.
+ * composite types and foreign tables ... but not, eg, for toast tables or
+ * sequences.
*/
if (IsUnderPostmaster && (relkind == RELKIND_RELATION ||
relkind == RELKIND_VIEW ||
+ relkind == RELKIND_FOREIGN_TABLE ||
relkind == RELKIND_COMPOSITE_TYPE))
- {
- /* OK, so pre-assign a type OID for the array type */
- Relation pg_type = heap_open(TypeRelationId, AccessShareLock);
-
- new_array_oid = GetNewOid(pg_type);
- heap_close(pg_type, AccessShareLock);
- }
+ new_array_oid = AssignTypeArrayOid();
/*
* Since defining a relation also defines a complex type, we add a new
- * system type corresponding to the new relation.
+ * system type corresponding to the new relation. The OID of the type can
+ * be preselected by the caller, but if reltypeid is InvalidOid, we'll
+ * generate a new OID for it.
*
* NOTE: we could get a unique-index failure here, in case someone else is
* creating the same type name in parallel but hadn't committed yet when
relnamespace,
relid,
relkind,
+ ownerid,
+ reltypeid,
new_array_oid);
/*
relnamespace, /* Same namespace as parent */
InvalidOid, /* Not composite, no relationOid */
0, /* relkind, also N/A here */
+ ownerid, /* owner's ID */
-1, /* Internal size (varlena) */
TYPTYPE_BASE, /* Not composite - typelem is */
- TYPCATEGORY_ARRAY, /* type-category (array) */
+ TYPCATEGORY_ARRAY, /* type-category (array) */
false, /* array types are never preferred */
DEFAULT_TYPDELIM, /* default array delimiter */
F_ARRAY_IN, /* array input proc */
'x', /* fully TOASTable */
-1, /* typmod */
0, /* array dimensions for typBaseType */
- false); /* Type NOT NULL */
+ false, /* Type NOT NULL */
+ InvalidOid); /* typcollation */
pfree(relarrayname);
}
new_rel_desc,
relid,
new_type_oid,
+ reloftypeid,
ownerid,
relkind,
+ PointerGetDatum(relacl),
reloptions);
/*
/*
* Make a dependency link to force the relation to be deleted if its
- * namespace is. Also make a dependency link to its owner.
+ * namespace is. Also make a dependency link to its owner, as well as
+ * dependencies for any roles mentioned in the default ACL.
*
* For composite types, these dependencies are tracked for the pg_type
* entry, so we needn't record them here. Likewise, TOAST tables don't
* need a namespace dependency (they live in a pinned namespace) nor an
- * owner dependency (they depend indirectly through the parent table).
+ * owner dependency (they depend indirectly through the parent table), nor
+ * should they have any ACL entries. The same applies for extension
+ * dependencies.
+ *
* Also, skip this in bootstrap mode, since we don't make dependencies
* while bootstrapping.
*/
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
recordDependencyOnOwner(RelationRelationId, relid, ownerid);
+
+ recordDependencyOnCurrentExtension(&myself);
+
+ if (reloftypeid)
+ {
+ referenced.classId = TypeRelationId;
+ referenced.objectId = reloftypeid;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
+
+ if (relacl != NULL)
+ {
+ int nnewmembers;
+ Oid *newmembers;
+
+ nnewmembers = aclmembers(relacl, &newmembers);
+ updateAclDependencies(RelationRelationId, relid, 0,
+ ownerid,
+ 0, NULL,
+ nnewmembers, newmembers);
+ }
}
+ /* Post creation hook for new relation */
+ InvokeObjectAccessHook(OAT_POST_CREATE, RelationRelationId, relid, 0);
+
/*
* Store any supplied constraints and defaults.
*
if (oncommit != ONCOMMIT_NOOP)
register_on_commit_action(relid, oncommit);
+ /*
+ * If this is an unlogged relation, it needs an init fork so that it
+ * can be correctly reinitialized on restart. Since we're going to
+ * do an immediate sync, we ony need to xlog this if archiving or
+ * streaming is enabled. And the immediate sync is required, because
+ * otherwise there's no guarantee that this will hit the disk before
+ * the next checkpoint moves the redo pointer.
+ */
+ if (relpersistence == RELPERSISTENCE_UNLOGGED)
+ {
+ Assert(relkind == RELKIND_RELATION || relkind == RELKIND_TOASTVALUE);
+
+ smgrcreate(new_rel_desc->rd_smgr, INIT_FORKNUM, false);
+ if (XLogIsNeeded())
+ log_smgrcreate(&new_rel_desc->rd_smgr->smgr_rnode.node,
+ INIT_FORKNUM);
+ smgrimmedsync(new_rel_desc->rd_smgr, INIT_FORKNUM);
+ }
+
/*
* ok, the relation has been cataloged, so close our relations and return
* the OID of the newly created relation.
/* Grab an appropriate lock on the pg_class relation */
pg_class_desc = heap_open(RelationRelationId, RowExclusiveLock);
- tup = SearchSysCache(RELOID,
- ObjectIdGetDatum(relid),
- 0, 0, 0);
+ tup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
if (!HeapTupleIsValid(tup))
elog(ERROR, "cache lookup failed for relation %u", relid);
attr_rel = heap_open(AttributeRelationId, RowExclusiveLock);
- tuple = SearchSysCacheCopy(ATTNUM,
- ObjectIdGetDatum(relid),
- Int16GetDatum(attnum),
- 0, 0);
+ tuple = SearchSysCacheCopy2(ATTNUM,
+ ObjectIdGetDatum(relid),
+ Int16GetDatum(attnum));
if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
elog(ERROR, "cache lookup failed for attribute %d of relation %u",
attnum, relid);
/* Fix the pg_attribute row */
attr_rel = heap_open(AttributeRelationId, RowExclusiveLock);
- tuple = SearchSysCacheCopy(ATTNUM,
- ObjectIdGetDatum(myrelid),
- Int16GetDatum(myattnum),
- 0, 0);
+ tuple = SearchSysCacheCopy2(ATTNUM,
+ ObjectIdGetDatum(myrelid),
+ Int16GetDatum(myattnum));
if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
elog(ERROR, "cache lookup failed for attribute %d of relation %u",
myattnum, myrelid);
*/
rel = relation_open(relid, AccessExclusiveLock);
+ /*
+ * There can no longer be anyone *else* touching the relation, but we
+ * might still have open queries or cursors, or pending trigger events,
+ * in our own session.
+ */
+ CheckTableNotInUse(rel, "DROP TABLE");
+
+ /*
+ * Delete pg_foreign_table tuple first.
+ */
+ if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+ {
+ Relation rel;
+ HeapTuple tuple;
+
+ rel = heap_open(ForeignTableRelationId, RowExclusiveLock);
+
+ tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(relid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for foreign table %u", relid);
+
+ simple_heap_delete(rel, &tuple->t_self);
+
+ ReleaseSysCache(tuple);
+ heap_close(rel, RowExclusiveLock);
+ }
+
/*
* Schedule unlinking of the relation's physical files at commit.
*/
if (rel->rd_rel->relkind != RELKIND_VIEW &&
- rel->rd_rel->relkind != RELKIND_COMPOSITE_TYPE)
+ rel->rd_rel->relkind != RELKIND_COMPOSITE_TYPE &&
+ rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
{
RelationDropStorage(rel);
}
* exists.
*/
attrrel = heap_open(AttributeRelationId, RowExclusiveLock);
- atttup = SearchSysCacheCopy(ATTNUM,
- ObjectIdGetDatum(RelationGetRelid(rel)),
- Int16GetDatum(attnum),
- 0, 0);
+ atttup = SearchSysCacheCopy2(ATTNUM,
+ ObjectIdGetDatum(RelationGetRelid(rel)),
+ Int16GetDatum(attnum));
if (!HeapTupleIsValid(atttup))
elog(ERROR, "cache lookup failed for attribute %d of relation %u",
attnum, RelationGetRelid(rel));
* in check constraints; it would fail to examine the contents of
* subselects.
*/
- varList = pull_var_clause(expr, false);
+ varList = pull_var_clause(expr, PVC_REJECT_PLACEHOLDERS);
keycount = list_length(varList);
if (keycount > 0)
CONSTRAINT_CHECK, /* Constraint Type */
false, /* Is Deferrable */
false, /* Is Deferred */
+ true, /* Is Validated */
RelationGetRelid(rel), /* relation */
attNos, /* attrs in the constraint */
keycount, /* # attrs in the constraint */
InvalidOid, /* not a domain constraint */
+ InvalidOid, /* no associated index */
InvalidOid, /* Foreign key fields */
NULL,
NULL,
' ',
' ',
' ',
- InvalidOid, /* no associated index */
- expr, /* Tree form check constraint */
- ccbin, /* Binary form check constraint */
- ccsrc, /* Source form check constraint */
- is_local, /* conislocal */
- inhcount); /* coninhcount */
+ NULL, /* not an exclusion constraint */
+ expr, /* Tree form of check constraint */
+ ccbin, /* Binary form of check constraint */
+ ccsrc, /* Source form of check constraint */
+ is_local, /* conislocal */
+ inhcount); /* coninhcount */
pfree(ccbin);
pfree(ccsrc);
/*
* Check name uniqueness, or generate a name if none was given.
*/
- if (cdef->name != NULL)
+ if (cdef->conname != NULL)
{
ListCell *cell2;
- ccname = cdef->name;
+ ccname = cdef->conname;
/* Check against other new constraints */
/* Needed because we don't do CommandCounterIncrement in loop */
foreach(cell2, checknames)
checknames = lappend(checknames, ccname);
/*
- * Check against pre-existing constraints. If we are allowed
- * to merge with an existing constraint, there's no more to
- * do here. (We omit the duplicate constraint from the result,
- * which is what ATAddCheckConstraint wants.)
+ * Check against pre-existing constraints. If we are allowed to
+ * merge with an existing constraint, there's no more to do here.
+ * (We omit the duplicate constraint from the result, which is
+ * what ATAddCheckConstraint wants.)
*/
if (MergeWithExistingConstraint(rel, ccname, expr,
allow_merge, is_local))
List *vars;
char *colname;
- vars = pull_var_clause(expr, false);
+ vars = pull_var_clause(expr, PVC_REJECT_PLACEHOLDERS);
/* eliminate duplicates */
vars = list_union(NIL, vars);
/* Found it. Conflicts if not identical check constraint */
if (con->contype == CONSTRAINT_CHECK)
{
- Datum val;
- bool isnull;
+ Datum val;
+ bool isnull;
val = fastgetattr(tup,
Anum_pg_constraint_conbin,
ccname, RelationGetRelationName(rel))));
/* OK to update the tuple */
ereport(NOTICE,
- (errmsg("merging constraint \"%s\" with inherited definition",
- ccname)));
+ (errmsg("merging constraint \"%s\" with inherited definition",
+ ccname)));
tup = heap_copytuple(tup);
con = (Form_pg_constraint) GETSTRUCT(tup);
if (is_local)
Form_pg_class relStruct;
relrel = heap_open(RelationRelationId, RowExclusiveLock);
- reltup = SearchSysCacheCopy(RELOID,
- ObjectIdGetDatum(RelationGetRelid(rel)),
- 0, 0, 0);
+ reltup = SearchSysCacheCopy1(RELOID,
+ ObjectIdGetDatum(RelationGetRelid(rel)));
if (!HeapTupleIsValid(reltup))
elog(ERROR, "cache lookup failed for relation %u",
RelationGetRelid(rel));
ereport(ERROR,
(errcode(ERRCODE_GROUPING_ERROR),
errmsg("cannot use aggregate function in default expression")));
+ if (pstate->p_hasWindowFuncs)
+ ereport(ERROR,
+ (errcode(ERRCODE_WINDOWING_ERROR),
+ errmsg("cannot use window function in default expression")));
/*
* Coerce the expression to the correct type and typmod, if given. This
errhint("You will need to rewrite or cast the expression.")));
}
+ /*
+ * Finally, take care of collations in the finished expression.
+ */
+ assign_expr_collations(pstate, expr);
+
return expr;
}
*/
expr = coerce_to_boolean(pstate, expr, "CHECK");
+ /*
+ * Take care of collations.
+ */
+ assign_expr_collations(pstate, expr);
+
/*
* Make sure no outside relations are referred to.
*/
if (list_length(pstate->p_rtable) != 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
- errmsg("only table \"%s\" can be referenced in check constraint",
- relname)));
+ errmsg("only table \"%s\" can be referenced in check constraint",
+ relname)));
/*
* No subplans or aggregates, either...
if (pstate->p_hasAggs)
ereport(ERROR,
(errcode(ERRCODE_GROUPING_ERROR),
- errmsg("cannot use aggregate function in check constraint")));
+ errmsg("cannot use aggregate function in check constraint")));
+ if (pstate->p_hasWindowFuncs)
+ ereport(ERROR,
+ (errcode(ERRCODE_WINDOWING_ERROR),
+ errmsg("cannot use window function in check constraint")));
return expr;
}
/*
* RemoveStatistics --- remove entries in pg_statistic for a rel or column
*
- * If attnum is zero, remove all entries for rel; else remove only the one
+ * If attnum is zero, remove all entries for rel; else remove only the one(s)
* for that column.
*/
void
nkeys = 2;
}
- scan = systable_beginscan(pgstatistic, StatisticRelidAttnumIndexId, true,
+ scan = systable_beginscan(pgstatistic, StatisticRelidAttnumInhIndexId, true,
SnapshotNow, nkeys, key);
+ /* we must loop even when attnum != 0, in case of inherited stats */
while (HeapTupleIsValid(tuple = systable_getnext(scan)))
simple_heap_delete(pgstatistic, &tuple->t_self);
indexInfo = BuildIndexInfo(currentIndex);
/*
- * Now truncate the actual file (and discard buffers). The indexam
- * is responsible for truncating the FSM in index_build(), if
- * applicable.
+ * Now truncate the actual file (and discard buffers).
*/
RelationTruncate(currentIndex, 0);
{
Oid rid = lfirst_oid(cell);
Relation rel;
- Oid toastrelid;
rel = heap_open(rid, AccessExclusiveLock);
relations = lappend(relations, rel);
-
- /* If there is a toast table, add it to the list too */
- toastrelid = rel->rd_rel->reltoastrelid;
- if (OidIsValid(toastrelid))
- {
- rel = heap_open(toastrelid, AccessExclusiveLock);
- relations = lappend(relations, rel);
- }
}
/* Don't allow truncate on tables that are referenced by foreign keys */
{
Relation rel = lfirst(cell);
- /* Truncate the FSM and actual file (and discard buffers) */
- RelationTruncate(rel, 0);
+ /* Truncate the relation */
+ heap_truncate_one_rel(rel);
- /* If this relation has indexes, truncate the indexes too */
- RelationTruncateIndexes(rel);
-
- /*
- * Close the relation, but keep exclusive lock on it until commit.
- */
+ /* Close the relation, but keep exclusive lock on it until commit */
heap_close(rel, NoLock);
}
}
+/*
+ * heap_truncate_one_rel
+ *
+ * This routine deletes all data within the specified relation.
+ *
+ * This is not transaction-safe, because the truncation is done immediately
+ * and cannot be rolled back later. Caller is responsible for having
+ * checked permissions etc, and must have obtained AccessExclusiveLock.
+ */
+void
+heap_truncate_one_rel(Relation rel)
+{
+ Oid toastrelid;
+
+ /* Truncate the actual file (and discard buffers) */
+ RelationTruncate(rel, 0);
+
+ /* If the relation has indexes, truncate the indexes too */
+ RelationTruncateIndexes(rel);
+
+ /* If there is a toast table, truncate that too */
+ toastrelid = rel->rd_rel->reltoastrelid;
+ if (OidIsValid(toastrelid))
+ {
+ Relation toastrel = heap_open(toastrelid, AccessExclusiveLock);
+
+ RelationTruncate(toastrel, 0);
+ RelationTruncateIndexes(toastrel);
+ /* keep the lock... */
+ heap_close(toastrel, NoLock);
+ }
+}
+
/*
* heap_truncate_check_FKs
* Check for foreign keys referencing a list of relations that