* 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.340 2008/09/30 10:52:12 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 "catalog/pg_tablespace.h"
#include "catalog/pg_type.h"
#include "catalog/pg_type_fn.h"
+#include "catalog/storage.h"
#include "commands/tablecmds.h"
#include "commands/typecmds.h"
#include "miscadmin.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.
*
- * We create storage for the main fork here, and also for the FSM for a
- * heap or toast relation. The caller is responsible for creating any
- * additional forks if needed.
+ * We only create the main fork here, other forks will be created on
+ * demand.
*/
if (create_storage)
{
- Assert(rel->rd_smgr == NULL);
RelationOpenSmgr(rel);
- smgrcreate(rel->rd_smgr, MAIN_FORKNUM, rel->rd_istemp, false);
-
- /*
- * For a real heap, create FSM fork as well. Indexams are
- * responsible for creating any extra forks themselves.
- */
- if (relkind == RELKIND_RELATION || relkind == RELKIND_TOASTVALUE)
- smgrcreate(rel->rd_smgr, FSM_FORKNUM, rel->rd_istemp, false);
+ 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.")));
+}
+
+/*
+ * InsertPgAttributeTuple
+ * Construct and insert a new tuple in pg_attribute.
+ *
+ * Caller has already opened and locked pg_attribute. new_attribute is the
+ * 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
+ * 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.)
+ */
+void
+InsertPgAttributeTuple(Relation pg_attribute_rel,
+ Form_pg_attribute new_attribute,
+ CatalogIndexState indstate)
+{
+ Datum values[Natts_pg_attribute];
+ bool nulls[Natts_pg_attribute];
+ HeapTuple tup;
+
+ /* This is a tad tedious, but way cleaner than what we used to do... */
+ memset(values, 0, sizeof(values));
+ memset(nulls, false, sizeof(nulls));
+
+ values[Anum_pg_attribute_attrelid - 1] = ObjectIdGetDatum(new_attribute->attrelid);
+ values[Anum_pg_attribute_attname - 1] = NameGetDatum(&new_attribute->attname);
+ values[Anum_pg_attribute_atttypid - 1] = ObjectIdGetDatum(new_attribute->atttypid);
+ values[Anum_pg_attribute_attstattarget - 1] = Int32GetDatum(new_attribute->attstattarget);
+ values[Anum_pg_attribute_attlen - 1] = Int16GetDatum(new_attribute->attlen);
+ values[Anum_pg_attribute_attnum - 1] = Int16GetDatum(new_attribute->attnum);
+ values[Anum_pg_attribute_attndims - 1] = Int32GetDatum(new_attribute->attndims);
+ values[Anum_pg_attribute_attcacheoff - 1] = Int32GetDatum(new_attribute->attcacheoff);
+ values[Anum_pg_attribute_atttypmod - 1] = Int32GetDatum(new_attribute->atttypmod);
+ values[Anum_pg_attribute_attbyval - 1] = BoolGetDatum(new_attribute->attbyval);
+ values[Anum_pg_attribute_attstorage - 1] = CharGetDatum(new_attribute->attstorage);
+ values[Anum_pg_attribute_attalign - 1] = CharGetDatum(new_attribute->attalign);
+ values[Anum_pg_attribute_attnotnull - 1] = BoolGetDatum(new_attribute->attnotnull);
+ values[Anum_pg_attribute_atthasdef - 1] = BoolGetDatum(new_attribute->atthasdef);
+ 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);
+
+ /* finally insert the new tuple, update the indexes, and clean up */
+ simple_heap_insert(pg_attribute_rel, tup);
+
+ if (indstate != NULL)
+ CatalogIndexInsert(indstate, tup);
+ else
+ CatalogUpdateIndexes(pg_attribute_rel, tup);
+
+ heap_freetuple(tup);
}
/* --------------------------------
bool oidislocal,
int oidinhcount)
{
- const Form_pg_attribute *dpp;
+ Form_pg_attribute attr;
int i;
- HeapTuple tup;
Relation rel;
CatalogIndexState indstate;
int natts = tupdesc->natts;
* First we add the user attributes. This is also a convenient place to
* add dependencies on their datatypes.
*/
- dpp = tupdesc->attrs;
for (i = 0; i < natts; i++)
{
+ attr = tupdesc->attrs[i];
/* Fill in the correct relation OID */
- (*dpp)->attrelid = new_rel_oid;
+ attr->attrelid = new_rel_oid;
/* Make sure these are OK, too */
- (*dpp)->attstattarget = -1;
- (*dpp)->attcacheoff = -1;
+ attr->attstattarget = -1;
+ attr->attcacheoff = -1;
- tup = heap_addheader(Natts_pg_attribute,
- false,
- ATTRIBUTE_TUPLE_SIZE,
- (void *) *dpp);
-
- simple_heap_insert(rel, tup);
-
- CatalogIndexInsert(indstate, tup);
-
- heap_freetuple(tup);
+ InsertPgAttributeTuple(rel, attr, indstate);
+ /* Add dependency info */
myself.classId = RelationRelationId;
myself.objectId = new_rel_oid;
myself.objectSubId = i + 1;
referenced.classId = TypeRelationId;
- referenced.objectId = (*dpp)->atttypid;
+ referenced.objectId = attr->atttypid;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
- dpp++;
+ if (OidIsValid(attr->attcollation))
+ {
+ referenced.classId = CollationRelationId;
+ referenced.objectId = attr->attcollation;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
}
/*
*/
if (relkind != RELKIND_VIEW && relkind != RELKIND_COMPOSITE_TYPE)
{
- dpp = SysAtt;
- for (i = 0; i < -1 - FirstLowInvalidHeapAttributeNumber; i++)
+ for (i = 0; i < (int) lengthof(SysAtt); i++)
{
- if (tupdesc->tdhasoid ||
- (*dpp)->attnum != ObjectIdAttributeNumber)
- {
- Form_pg_attribute attStruct;
+ FormData_pg_attribute attStruct;
- tup = heap_addheader(Natts_pg_attribute,
- false,
- ATTRIBUTE_TUPLE_SIZE,
- (void *) *dpp);
- attStruct = (Form_pg_attribute) GETSTRUCT(tup);
-
- /* Fill in the correct relation OID in the copied tuple */
- attStruct->attrelid = new_rel_oid;
-
- /* Fill in correct inheritance info for the OID column */
- if (attStruct->attnum == ObjectIdAttributeNumber)
- {
- attStruct->attislocal = oidislocal;
- attStruct->attinhcount = oidinhcount;
- }
-
- /*
- * Unneeded since they should be OK in the constant data
- * anyway
- */
- /* attStruct->attstattarget = 0; */
- /* attStruct->attcacheoff = -1; */
+ /* skip OID where appropriate */
+ if (!tupdesc->tdhasoid &&
+ SysAtt[i]->attnum == ObjectIdAttributeNumber)
+ continue;
- simple_heap_insert(rel, tup);
+ memcpy(&attStruct, (char *) SysAtt[i], sizeof(FormData_pg_attribute));
- CatalogIndexInsert(indstate, tup);
+ /* Fill in the correct relation OID in the copied tuple */
+ attStruct.attrelid = new_rel_oid;
- heap_freetuple(tup);
+ /* Fill in correct inheritance info for the OID column */
+ if (attStruct.attnum == ObjectIdAttributeNumber)
+ {
+ attStruct.attislocal = oidislocal;
+ attStruct.attinhcount = oidinhcount;
}
- dpp++;
+
+ InsertPgAttributeTuple(rel, &attStruct, indstate);
}
}
* 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;
Datum values[Natts_pg_class];
- char nulls[Natts_pg_class];
+ bool nulls[Natts_pg_class];
HeapTuple tup;
/* This is a tad tedious, but way cleaner than what we used to do... */
memset(values, 0, sizeof(values));
- memset(nulls, ' ', sizeof(nulls));
+ memset(nulls, false, sizeof(nulls));
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_reltriggers - 1] = Int16GetDatum(rd_rel->reltriggers);
- values[Anum_pg_class_relukeys - 1] = Int16GetDatum(rd_rel->relukeys);
- values[Anum_pg_class_relfkeys - 1] = Int16GetDatum(rd_rel->relfkeys);
- values[Anum_pg_class_relrefs - 1] = Int16GetDatum(rd_rel->relrefs);
values[Anum_pg_class_relhasoids - 1] = BoolGetDatum(rd_rel->relhasoids);
values[Anum_pg_class_relhaspkey - 1] = BoolGetDatum(rd_rel->relhaspkey);
values[Anum_pg_class_relhasrules - 1] = BoolGetDatum(rd_rel->relhasrules);
+ 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] = 'n';
+ 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
- nulls[Anum_pg_class_reloptions - 1] = 'n';
+ nulls[Anum_pg_class_reloptions - 1] = true;
- tup = heap_formtuple(RelationGetDescr(pg_class_desc), values, nulls);
+ tup = heap_form_tuple(RelationGetDescr(pg_class_desc), values, nulls);
/*
* The new tuple must have the oid already chosen for the rel. Sure would
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)
{
- ForkNumber forknum;
-
- RelationOpenSmgr(rel);
- for (forknum = 0; forknum <= MAX_FORKNUM; forknum++)
- if (smgrexists(rel->rd_smgr, forknum))
- smgrscheduleunlink(rel->rd_smgr, forknum, rel->rd_istemp);
- RelationCloseSmgr(rel);
+ RelationDropStorage(rel);
}
/*
Relation adrel;
HeapTuple tuple;
Datum values[4];
- static char nulls[4] = {' ', ' ', ' ', ' '};
+ static bool nulls[4] = {false, false, false, false};
Relation attrrel;
HeapTuple atttup;
Form_pg_attribute attStruct;
adrel = heap_open(AttrDefaultRelationId, RowExclusiveLock);
- tuple = heap_formtuple(adrel->rd_att, values, nulls);
+ tuple = heap_form_tuple(adrel->rd_att, values, nulls);
attrdefOid = simple_heap_insert(adrel, tuple);
CatalogUpdateIndexes(adrel, tuple);
* 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) */
- FreeSpaceMapTruncateRel(rel, 0);
- RelationTruncate(rel, 0);
-
- /* If this relation has indexes, truncate the indexes too */
- RelationTruncateIndexes(rel);
+ /* Truncate the relation */
+ heap_truncate_one_rel(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
{
Relation rel = lfirst(cell);
- if (rel->rd_rel->reltriggers != 0)
+ if (rel->rd_rel->relhastriggers)
oids = lappend_oid(oids, RelationGetRelid(rel));
}