tablecmds.c
来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 2,338 行 · 第 1/5 页
C
2,338 行
/*------------------------------------------------------------------------- * * tablecmds.c * Commands for creating and altering table structures and settings * * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.91.2.1 2004/07/17 17:28:47 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/genam.h"#include "access/tuptoaster.h"#include "catalog/catalog.h"#include "catalog/catname.h"#include "catalog/dependency.h"#include "catalog/heap.h"#include "catalog/index.h"#include "catalog/indexing.h"#include "catalog/namespace.h"#include "catalog/pg_constraint.h"#include "catalog/pg_inherits.h"#include "catalog/pg_namespace.h"#include "catalog/pg_opclass.h"#include "catalog/pg_trigger.h"#include "catalog/pg_type.h"#include "commands/cluster.h"#include "commands/tablecmds.h"#include "commands/trigger.h"#include "executor/executor.h"#include "miscadmin.h"#include "nodes/makefuncs.h"#include "optimizer/clauses.h"#include "optimizer/plancat.h"#include "optimizer/prep.h"#include "parser/gramparse.h"#include "parser/parse_coerce.h"#include "parser/parse_expr.h"#include "parser/parse_oper.h"#include "parser/parse_relation.h"#include "parser/parse_type.h"#include "utils/acl.h"#include "utils/builtins.h"#include "utils/fmgroids.h"#include "utils/inval.h"#include "utils/lsyscache.h"#include "utils/relcache.h"#include "utils/syscache.h"/* * ON COMMIT action list */typedef struct OnCommitItem{ Oid relid; /* relid of relation */ OnCommitAction oncommit; /* what to do at end of xact */ /* * If this entry was created during this xact, it should be deleted at * xact abort. Conversely, if this entry was deleted during this * xact, it should be removed at xact commit. We leave deleted * entries in the list until commit so that we can roll back if * needed. */ bool created_in_cur_xact; bool deleted_in_cur_xact;} OnCommitItem;static List *on_commits = NIL;static List *MergeAttributes(List *schema, List *supers, bool istemp, List **supOids, List **supconstr, bool *supHasOids);static bool change_varattnos_of_a_node(Node *node, const AttrNumber *newattno);static void StoreCatalogInheritance(Oid relationId, List *supers);static int findAttrByName(const char *attributeName, List *schema);static void setRelhassubclassInRelation(Oid relationId, bool relhassubclass);static bool needs_toast_table(Relation rel);static void AlterTableAddCheckConstraint(Relation rel, Constraint *constr);static void AlterTableAddForeignKeyConstraint(Relation rel, FkConstraint *fkconstraint);static int transformColumnNameList(Oid relId, List *colList, int16 *attnums, Oid *atttypids);static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid, List **attnamelist, int16 *attnums, Oid *atttypids);static Oid transformFkeyCheckAttrs(Relation pkrel, int numattrs, int16 *attnums);static void validateForeignKeyConstraint(FkConstraint *fkconstraint, Relation rel, Relation pkrel);static void createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint, Oid constrOid);static char *fkMatchTypeToString(char match_type);/* Used by attribute and relation renaming routines: */#define RI_TRIGGER_PK 1 /* is a trigger on the PK relation */#define RI_TRIGGER_FK 2 /* is a trigger on the FK relation */#define RI_TRIGGER_NONE 0 /* is not an RI trigger function */static int ri_trigger_type(Oid tgfoid);static void update_ri_trigger_args(Oid relid, const char *oldname, const char *newname, bool fk_scan, bool update_relname);/* ---------------------------------------------------------------- * DefineRelation * Creates a new relation. * * If successful, returns the OID of the new relation. * ---------------------------------------------------------------- */OidDefineRelation(CreateStmt *stmt, char relkind){ char relname[NAMEDATALEN]; Oid namespaceId; List *schema = stmt->tableElts; Oid relationId; Relation rel; TupleDesc descriptor; List *inheritOids; List *old_constraints; bool parentHasOids; List *rawDefaults; List *listptr; int i; AttrNumber attnum; /* * Truncate relname to appropriate length (probably a waste of time, * as parser should have done this already). */ StrNCpy(relname, stmt->relation->relname, NAMEDATALEN); /* * Check consistency of arguments */ if (stmt->oncommit != ONCOMMIT_NOOP && !stmt->relation->istemp) ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("ON COMMIT can only be used on temporary tables"))); /* * Look up the namespace in which we are supposed to create the * relation. Check we have permission to create there. Skip check if * bootstrapping, since permissions machinery may not be working yet. */ namespaceId = RangeVarGetCreationNamespace(stmt->relation); if (!IsBootstrapProcessingMode()) { AclResult aclresult; aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceId)); } /* * Look up inheritance ancestors and generate relation schema, * including inherited attributes. */ schema = MergeAttributes(schema, stmt->inhRelations, stmt->relation->istemp, &inheritOids, &old_constraints, &parentHasOids); /* * Create a relation descriptor from the relation schema and create * the relation. Note that in this stage only inherited (pre-cooked) * defaults and constraints will be included into the new relation. * (BuildDescForRelation takes care of the inherited defaults, but we * have to copy inherited constraints here.) */ descriptor = BuildDescForRelation(schema); descriptor->tdhasoid = (stmt->hasoids || parentHasOids); if (old_constraints != NIL) { ConstrCheck *check = (ConstrCheck *) palloc(length(old_constraints) * sizeof(ConstrCheck)); int ncheck = 0; int constr_name_ctr = 0; foreach(listptr, old_constraints) { Constraint *cdef = (Constraint *) lfirst(listptr); if (cdef->contype != CONSTR_CHECK) continue; if (cdef->name != NULL) { for (i = 0; i < ncheck; i++) { if (strcmp(check[i].ccname, cdef->name) == 0) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("duplicate check constraint name \"%s\"", cdef->name))); } check[ncheck].ccname = cdef->name; } else { /* * Generate a constraint name. NB: this should match the * form of names that GenerateConstraintName() may produce * for names added later. We are assured that there is no * name conflict, because MergeAttributes() did not pass * back any names of this form. */ check[ncheck].ccname = (char *) palloc(NAMEDATALEN); snprintf(check[ncheck].ccname, NAMEDATALEN, "$%d", ++constr_name_ctr); } Assert(cdef->raw_expr == NULL && cdef->cooked_expr != NULL); check[ncheck].ccbin = pstrdup(cdef->cooked_expr); ncheck++; } if (ncheck > 0) { if (descriptor->constr == NULL) { descriptor->constr = (TupleConstr *) palloc(sizeof(TupleConstr)); descriptor->constr->defval = NULL; descriptor->constr->num_defval = 0; descriptor->constr->has_not_null = false; } descriptor->constr->num_check = ncheck; descriptor->constr->check = check; } } relationId = heap_create_with_catalog(relname, namespaceId, descriptor, relkind, false, stmt->oncommit, allowSystemTableMods); StoreCatalogInheritance(relationId, inheritOids); /* * We must bump the command counter to make the newly-created relation * tuple visible for opening. */ CommandCounterIncrement(); /* * Open the new relation and acquire exclusive lock on it. This isn't * really necessary for locking out other backends (since they can't * see the new rel anyway until we commit), but it keeps the lock * manager from complaining about deadlock risks. */ rel = relation_open(relationId, AccessExclusiveLock); /* * Now add any newly specified column default values and CHECK * constraints to the new relation. These are passed to us in the * form of raw parsetrees; we need to transform them to executable * expression trees before they can be added. The most convenient way * to do that is to apply the parser's transformExpr routine, but * transformExpr doesn't work unless we have a pre-existing relation. * So, the transformation has to be postponed to this final step of * CREATE TABLE. * * Another task that's conveniently done at this step is to add * dependency links between columns and supporting relations (such as * SERIAL sequences). * * First, scan schema to find new column defaults. */ rawDefaults = NIL; attnum = 0; foreach(listptr, schema) { ColumnDef *colDef = lfirst(listptr); attnum++; if (colDef->raw_default != NULL) { RawColumnDefault *rawEnt; Assert(colDef->cooked_default == NULL); rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault)); rawEnt->attnum = attnum; rawEnt->raw_default = colDef->raw_default; rawDefaults = lappend(rawDefaults, rawEnt); } if (colDef->support != NULL) { /* Create dependency for supporting relation for this column */ ObjectAddress colobject, suppobject; colobject.classId = RelOid_pg_class; colobject.objectId = relationId; colobject.objectSubId = attnum; suppobject.classId = RelOid_pg_class; suppobject.objectId = RangeVarGetRelid(colDef->support, false); suppobject.objectSubId = 0; recordDependencyOn(&suppobject, &colobject, DEPENDENCY_INTERNAL); } } /* * Parse and add the defaults/constraints, if any. */ if (rawDefaults || stmt->constraints) AddRelationRawConstraints(rel, rawDefaults, stmt->constraints); /* * Clean up. We keep lock on new relation (although it shouldn't be * visible to anyone else anyway, until commit). */ relation_close(rel, NoLock); return relationId;}/* * RemoveRelation * Deletes a relation. */voidRemoveRelation(const RangeVar *relation, DropBehavior behavior){ Oid relOid; ObjectAddress object; relOid = RangeVarGetRelid(relation, false); object.classId = RelOid_pg_class; object.objectId = relOid; object.objectSubId = 0; performDeletion(&object, behavior);}/* * TruncateRelation * Removes all the rows from a relation. * * Note: This routine only does safety and permissions checks; * rebuild_relation in cluster.c does the actual work. */voidTruncateRelation(const RangeVar *relation){ Relation rel; /* Grab exclusive lock in preparation for truncate */ rel = heap_openrv(relation, AccessExclusiveLock); /* Only allow truncate on regular tables */ if (rel->rd_rel->relkind != RELKIND_RELATION) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a table", RelationGetRelationName(rel)))); /* Permissions checks */ if (!pg_class_ownercheck(RelationGetRelid(rel), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(rel)); if (!allowSystemTableMods && IsSystemRelation(rel)) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("permission denied: \"%s\" is a system catalog", RelationGetRelationName(rel)))); /* * Don't allow truncate on temp tables of other backends ... their * local buffer manager is not going to cope. */ if (isOtherTempNamespace(RelationGetNamespace(rel))) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot truncate temporary tables of other sessions"))); /* * Don't allow truncate on tables which are referenced by foreign keys */ heap_truncate_check_FKs(rel); /* * Do the real work using the same technique as cluster, but without * the data-copying portion */ rebuild_relation(rel, InvalidOid); /* NB: rebuild_relation does heap_close() */ /* * You might think we need to truncate the rel's toast table here too, * but actually we don't; it will have been rebuilt in an empty state. */}/*---------- * MergeAttributes * Returns new schema given initial schema and superclasses. * * Input arguments: * 'schema' is the column/attribute definition for the table. (It's a list * of ColumnDef's.) It is destructively changed. * 'supers' is a list of names (as RangeVar nodes) of parent relations. * 'istemp' is TRUE if we are creating a temp relation. * * Output arguments: * 'supOids' receives a list of the OIDs of the parent relations. * 'supconstr' receives a list of constraints belonging to the parents, * updated as necessary to be valid for the child. * 'supHasOids' is set TRUE if any parent has OIDs, else it is set FALSE. * * Return value: * Completed schema list. * * Notes: * The order in which the attributes are inherited is very important. * Intuitively, the inherited attributes should come first. If a table * inherits from multiple parents, the order of those attributes are * according to the order of the parents specified in CREATE TABLE. * * Here's an example: * * create table person (name text, age int4, location point); * create table emp (salary int4, manager text) inherits(person); * create table student (gpa float8) inherits (person); * create table stud_emp (percent int4) inherits (emp, student); * * The order of the attributes of stud_emp is: * * person {1:name, 2:age, 3:location} * / \ * {6:gpa} student emp {4:salary, 5:manager} * \ / * stud_emp {7:percent} * * If the same attribute name appears multiple times, then it appears * in the result table in the proper location for its first appearance. * * Constraints (including NOT NULL constraints) for the child table * are the union of all relevant constraints, from both the child schema * and parent tables. * * The default value for a child column is defined as: * (1) If the child schema specifies a default, that value is used. * (2) If neither the child nor any parent specifies a default, then
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?