diff options
Diffstat (limited to 'src/backend/commands')
-rw-r--r-- | src/backend/commands/sequence.c | 133 | ||||
-rw-r--r-- | src/backend/commands/tablecmds.c | 100 | ||||
-rw-r--r-- | src/backend/commands/view.c | 3 |
3 files changed, 177 insertions, 59 deletions
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index 6154a4ed3da..865c2f60fe5 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.138 2006/07/31 20:09:00 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.139 2006/08/21 00:57:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,6 +17,7 @@ #include "access/heapam.h" #include "access/transam.h" #include "access/xact.h" +#include "catalog/dependency.h" #include "catalog/namespace.h" #include "catalog/pg_type.h" #include "commands/defrem.h" @@ -26,6 +27,7 @@ #include "nodes/makefuncs.h" #include "utils/acl.h" #include "utils/builtins.h" +#include "utils/lsyscache.h" #include "utils/resowner.h" #include "utils/syscache.h" @@ -82,8 +84,11 @@ static int64 nextval_internal(Oid relid); static Relation open_share_lock(SeqTable seq); static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel); static Form_pg_sequence read_info(SeqTable elm, Relation rel, Buffer *buf); -static void init_params(List *options, Form_pg_sequence new, bool isInit); +static void init_params(List *options, bool isInit, + Form_pg_sequence new, List **owned_by); static void do_setval(Oid relid, int64 next, bool iscalled); +static void process_owned_by(Relation seqrel, List *owned_by); + /* * DefineSequence @@ -93,6 +98,7 @@ void DefineSequence(CreateSeqStmt *seq) { FormData_pg_sequence new; + List *owned_by; CreateStmt *stmt = makeNode(CreateStmt); Oid seqoid; Relation rel; @@ -107,7 +113,7 @@ DefineSequence(CreateSeqStmt *seq) NameData name; /* Check and set all option values */ - init_params(seq->options, &new, true); + init_params(seq->options, true, &new, &owned_by); /* * Create relation (and fill *null & *value) @@ -123,7 +129,6 @@ DefineSequence(CreateSeqStmt *seq) coldef->raw_default = NULL; coldef->cooked_default = NULL; coldef->constraints = NIL; - coldef->support = NULL; null[i - 1] = ' '; @@ -287,6 +292,10 @@ DefineSequence(CreateSeqStmt *seq) UnlockReleaseBuffer(buf); + /* process OWNED BY if given */ + if (owned_by) + process_owned_by(rel, owned_by); + heap_close(rel, NoLock); } @@ -305,6 +314,7 @@ AlterSequence(AlterSeqStmt *stmt) Page page; Form_pg_sequence seq; FormData_pg_sequence new; + List *owned_by; /* open and AccessShareLock sequence */ relid = RangeVarGetRelid(stmt->sequence, false); @@ -323,7 +333,7 @@ AlterSequence(AlterSeqStmt *stmt) memcpy(&new, seq, sizeof(FormData_pg_sequence)); /* Check and set new values */ - init_params(stmt->options, &new, false); + init_params(stmt->options, false, &new, &owned_by); /* Now okay to update the on-disk tuple */ memcpy(seq, &new, sizeof(FormData_pg_sequence)); @@ -366,6 +376,10 @@ AlterSequence(AlterSeqStmt *stmt) UnlockReleaseBuffer(buf); + /* process OWNED BY if given */ + if (owned_by) + process_owned_by(seqrel, owned_by); + relation_close(seqrel, NoLock); } @@ -933,13 +947,15 @@ read_info(SeqTable elm, Relation rel, Buffer *buf) /* * init_params: process the options list of CREATE or ALTER SEQUENCE, - * and store the values into appropriate fields of *new. + * and store the values into appropriate fields of *new. Also set + * *owned_by to any OWNED BY option, or to NIL if there is none. * * If isInit is true, fill any unspecified options with default values; * otherwise, do not change existing options that aren't explicitly overridden. */ static void -init_params(List *options, Form_pg_sequence new, bool isInit) +init_params(List *options, bool isInit, + Form_pg_sequence new, List **owned_by) { DefElem *last_value = NULL; DefElem *increment_by = NULL; @@ -949,6 +965,8 @@ init_params(List *options, Form_pg_sequence new, bool isInit) DefElem *is_cycled = NULL; ListCell *option; + *owned_by = NIL; + foreach(option, options) { DefElem *defel = (DefElem *) lfirst(option); @@ -1006,6 +1024,14 @@ init_params(List *options, Form_pg_sequence new, bool isInit) errmsg("conflicting or redundant options"))); is_cycled = defel; } + else if (strcmp(defel->defname, "owned_by") == 0) + { + if (*owned_by) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("conflicting or redundant options"))); + *owned_by = defGetQualifiedName(defel); + } else elog(ERROR, "option \"%s\" not recognized", defel->defname); @@ -1130,6 +1156,99 @@ init_params(List *options, Form_pg_sequence new, bool isInit) new->cache_value = 1; } +/* + * Process an OWNED BY option for CREATE/ALTER SEQUENCE + * + * Ownership permissions on the sequence are already checked, + * but if we are establishing a new owned-by dependency, we must + * enforce that the referenced table has the same owner and namespace + * as the sequence. + */ +static void +process_owned_by(Relation seqrel, List *owned_by) +{ + int nnames; + Relation tablerel; + AttrNumber attnum; + + nnames = list_length(owned_by); + Assert(nnames > 0); + if (nnames == 1) + { + /* Must be OWNED BY NONE */ + if (strcmp(strVal(linitial(owned_by)), "none") != 0) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("invalid OWNED BY option"), + errhint("Specify OWNED BY table.column or OWNED BY NONE."))); + tablerel = NULL; + attnum = 0; + } + else + { + List *relname; + char *attrname; + RangeVar *rel; + + /* Separate relname and attr name */ + relname = list_truncate(list_copy(owned_by), nnames - 1); + attrname = strVal(lfirst(list_tail(owned_by))); + + /* Open and lock rel to ensure it won't go away meanwhile */ + rel = makeRangeVarFromNameList(relname); + tablerel = relation_openrv(rel, AccessShareLock); + + /* Must be a regular table */ + if (tablerel->rd_rel->relkind != RELKIND_RELATION) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("referenced relation \"%s\" is not a table", + RelationGetRelationName(tablerel)))); + + /* We insist on same owner and schema */ + if (seqrel->rd_rel->relowner != tablerel->rd_rel->relowner) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("sequence must have same owner as table it is owned by"))); + if (RelationGetNamespace(seqrel) != RelationGetNamespace(tablerel)) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("sequence must be in same schema as table it is owned by"))); + + /* Now, fetch the attribute number from the system cache */ + attnum = get_attnum(RelationGetRelid(tablerel), attrname); + if (attnum == InvalidAttrNumber) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" of relation \"%s\" does not exist", + attrname, RelationGetRelationName(tablerel)))); + } + + /* + * OK, we are ready to update pg_depend. First remove any existing + * AUTO dependencies for the sequence, then optionally add a new one. + */ + markSequenceUnowned(RelationGetRelid(seqrel)); + + if (tablerel) + { + ObjectAddress refobject, + depobject; + + refobject.classId = RelationRelationId; + refobject.objectId = RelationGetRelid(tablerel); + refobject.objectSubId = attnum; + depobject.classId = RelationRelationId; + depobject.objectId = RelationGetRelid(seqrel); + depobject.objectSubId = 0; + recordDependencyOn(&depobject, &refobject, DEPENDENCY_AUTO); + } + + /* Done, but hold lock until commit */ + if (tablerel) + relation_close(tablerel, NoLock); +} + void seq_redo(XLogRecPtr lsn, XLogRecord *record) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index cd4c4eb2304..a1f76033377 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.199 2006/08/03 20:57:06 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.200 2006/08/21 00:57:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -209,8 +209,6 @@ static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, static void ATExecAddColumn(AlteredTableInfo *tab, Relation rel, ColumnDef *colDef); static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid); -static void add_column_support_dependency(Oid relid, int32 attnum, - RangeVar *support); static void ATExecDropNotNull(Relation rel, const char *colName); static void ATExecSetNotNull(AlteredTableInfo *tab, Relation rel, const char *colName); @@ -476,10 +474,6 @@ DefineRelation(CreateStmt *stmt, char relkind) * 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; @@ -502,10 +496,6 @@ DefineRelation(CreateStmt *stmt, char relkind) rawEnt->raw_default = colDef->raw_default; rawDefaults = lappend(rawDefaults, rawEnt); } - - /* Create dependency for supporting relation for this column */ - if (colDef->support != NULL) - add_column_support_dependency(relationId, attnum, colDef->support); } /* @@ -944,7 +934,6 @@ MergeAttributes(List *schema, List *supers, bool istemp, def->raw_default = NULL; def->cooked_default = NULL; def->constraints = NIL; - def->support = NULL; inhSchema = lappend(inhSchema, def); newattno[parent_attno - 1] = ++child_attno; } @@ -1159,9 +1148,10 @@ varattnos_map(TupleDesc old, TupleDesc new) return attmap; } -/* Generate a map for change_varattnos_of_a_node from a tupledesc and a list of - * ColumnDefs */ - +/* + * Generate a map for change_varattnos_of_a_node from a tupledesc and a list of + * ColumnDefs + */ AttrNumber * varattnos_map_schema(TupleDesc old, List *schema) { @@ -3017,8 +3007,6 @@ ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, /* Child should see column as singly inherited */ colDefChild->inhcount = 1; colDefChild->is_local = false; - /* and don't make a support dependency on the child */ - colDefChild->support = NULL; ATOneLevelRecursion(wqueue, rel, childCmd); } @@ -3259,8 +3247,6 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, * Add needed dependency entries for the new column. */ add_column_datatype_dependency(myrelid, i, attribute->atttypid); - if (colDef->support != NULL) - add_column_support_dependency(myrelid, i, colDef->support); } /* @@ -3282,24 +3268,6 @@ add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid) } /* - * Install a dependency for a column's supporting relation (serial sequence). - */ -static void -add_column_support_dependency(Oid relid, int32 attnum, RangeVar *support) -{ - ObjectAddress colobject, - suppobject; - - colobject.classId = RelationRelationId; - colobject.objectId = relid; - colobject.objectSubId = attnum; - suppobject.classId = RelationRelationId; - suppobject.objectId = RangeVarGetRelid(support, false); - suppobject.objectSubId = 0; - recordDependencyOn(&suppobject, &colobject, DEPENDENCY_INTERNAL); -} - -/* * ALTER TABLE ALTER COLUMN DROP NOT NULL */ static void @@ -5444,9 +5412,9 @@ ATPostAlterTypeParse(char *cmd, List **wqueue) /* * ALTER TABLE OWNER * - * recursing is true if we are recursing from a table to its indexes or - * toast table. We don't allow the ownership of those things to be - * changed separately from the parent table. Also, we can skip permission + * recursing is true if we are recursing from a table to its indexes, + * sequences, or toast table. We don't allow the ownership of those things to + * be changed separately from the parent table. Also, we can skip permission * checks (this is necessary not just an optimization, else we'd fail to * handle toast tables properly). */ @@ -5479,7 +5447,6 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing) { case RELKIND_RELATION: case RELKIND_VIEW: - case RELKIND_SEQUENCE: /* ok to change owner */ break; case RELKIND_INDEX: @@ -5502,6 +5469,24 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing) newOwnerId = tuple_class->relowner; } break; + case RELKIND_SEQUENCE: + if (!recursing && + tuple_class->relowner != newOwnerId) + { + /* if it's an owned sequence, disallow changing it by itself */ + Oid tableId; + int32 colId; + + if (sequenceIsOwned(relationOid, &tableId, &colId)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot change owner of sequence \"%s\"", + NameStr(tuple_class->relname)), + errdetail("Sequence \"%s\" is linked to table \"%s\".", + NameStr(tuple_class->relname), + get_rel_name(tableId)))); + } + break; case RELKIND_TOASTVALUE: if (recursing) break; @@ -5644,7 +5629,7 @@ change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId) HeapTuple tup; /* - * SERIAL sequences are those having an internal dependency on one of the + * SERIAL sequences are those having an auto dependency on one of the * table's columns (we don't care *which* column, exactly). */ depRel = heap_open(DependRelationId, AccessShareLock); @@ -5667,11 +5652,11 @@ change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId) Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup); Relation seqRel; - /* skip dependencies other than internal dependencies on columns */ + /* skip dependencies other than auto dependencies on columns */ if (depForm->refobjsubid == 0 || depForm->classid != RelationRelationId || depForm->objsubid != 0 || - depForm->deptype != DEPENDENCY_INTERNAL) + depForm->deptype != DEPENDENCY_AUTO) continue; /* Use relation_open just in case it's an index */ @@ -5686,7 +5671,7 @@ change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId) } /* We don't need to close the sequence while we alter it. */ - ATExecChangeOwner(depForm->objid, newOwnerId, false); + ATExecChangeOwner(depForm->objid, newOwnerId, true); /* Now we can close it. Keep the lock till end of transaction. */ relation_close(seqRel, NoLock); @@ -6549,6 +6534,9 @@ AlterTableNamespace(RangeVar *relation, const char *newschema) rel = heap_openrv(relation, AccessExclusiveLock); + relid = RelationGetRelid(rel); + oldNspOid = RelationGetNamespace(rel); + /* heap_openrv allows TOAST, but we don't want to */ if (rel->rd_rel->relkind == RELKIND_TOASTVALUE) ereport(ERROR, @@ -6556,8 +6544,20 @@ AlterTableNamespace(RangeVar *relation, const char *newschema) errmsg("\"%s\" is a TOAST relation", RelationGetRelationName(rel)))); - relid = RelationGetRelid(rel); - oldNspOid = RelationGetNamespace(rel); + /* if it's an owned sequence, disallow moving it by itself */ + if (rel->rd_rel->relkind == RELKIND_SEQUENCE) + { + Oid tableId; + int32 colId; + + if (sequenceIsOwned(relid, &tableId, &colId)) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot move an owned sequence into another schema"), + errdetail("Sequence \"%s\" is linked to table \"%s\".", + RelationGetRelationName(rel), + get_rel_name(tableId)))); + } /* get schema OID and check its permissions */ nspOid = LookupCreationNamespace(newschema); @@ -6699,7 +6699,7 @@ AlterSeqNamespaces(Relation classRel, Relation rel, HeapTuple tup; /* - * SERIAL sequences are those having an internal dependency on one of the + * SERIAL sequences are those having an auto dependency on one of the * table's columns (we don't care *which* column, exactly). */ depRel = heap_open(DependRelationId, AccessShareLock); @@ -6722,11 +6722,11 @@ AlterSeqNamespaces(Relation classRel, Relation rel, Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup); Relation seqRel; - /* skip dependencies other than internal dependencies on columns */ + /* skip dependencies other than auto dependencies on columns */ if (depForm->refobjsubid == 0 || depForm->classid != RelationRelationId || depForm->objsubid != 0 || - depForm->deptype != DEPENDENCY_INTERNAL) + depForm->deptype != DEPENDENCY_AUTO) continue; /* Use relation_open just in case it's an index */ diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 63850cbeae7..df7f479f316 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.96 2006/07/13 16:49:14 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/view.c,v 1.97 2006/08/21 00:57:24 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -126,7 +126,6 @@ DefineVirtualRelation(const RangeVar *relation, List *tlist, bool replace) def->raw_default = NULL; def->cooked_default = NULL; def->constraints = NIL; - def->support = NULL; attrList = lappend(attrList, def); } |