diff --git a/src/backend/commands/Makefile b/src/backend/commands/Makefile index b27e6d77b2abeddfbc7d16faa3fbabe3dc6b115e..190b0fd64f64d75290ce2767864f39c1e606f26f 100644 --- a/src/backend/commands/Makefile +++ b/src/backend/commands/Makefile @@ -1,10 +1,10 @@ #------------------------------------------------------------------------- # # Makefile-- -# Makefile for commands +# Makefile for backend/commands # # IDENTIFICATION -# $Header: /cvsroot/pgsql/src/backend/commands/Makefile,v 1.27 2001/07/13 22:55:59 tgl Exp $ +# $Header: /cvsroot/pgsql/src/backend/commands/Makefile,v 1.28 2002/04/15 05:22:03 tgl Exp $ # #------------------------------------------------------------------------- @@ -12,10 +12,11 @@ subdir = src/backend/commands top_builddir = ../../.. include $(top_builddir)/src/Makefile.global -OBJS = async.o creatinh.o command.o comment.o copy.o indexcmds.o define.o \ - remove.o rename.o vacuum.o vacuumlazy.o analyze.o view.o cluster.o \ - explain.o sequence.o trigger.o user.o proclang.o \ - dbcommands.o variable.o +OBJS = aggregatecmds.o analyze.o async.o cluster.o comment.o copy.o \ + dbcommands.o define.o explain.o functioncmds.o \ + indexcmds.o lockcmds.o operatorcmds.o portalcmds.o proclang.o \ + schemacmds.o sequence.o tablecmds.o trigger.o typecmds.o user.o \ + vacuum.o vacuumlazy.o variable.o view.o all: SUBSYS.o diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c new file mode 100644 index 0000000000000000000000000000000000000000..7ee0c8f0784c5b40928bb4c7f3f2aa9375d6f988 --- /dev/null +++ b/src/backend/commands/aggregatecmds.c @@ -0,0 +1,208 @@ +/*------------------------------------------------------------------------- + * + * aggregatecmds.c + * + * Routines for aggregate-manipulation commands + * + * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/commands/aggregatecmds.c,v 1.1 2002/04/15 05:22:03 tgl Exp $ + * + * DESCRIPTION + * The "DefineFoo" routines take the parse tree and pick out the + * appropriate arguments/flags, passing the results to the + * corresponding "FooDefine" routines (in src/catalog) that do + * the actual catalog-munging. These routines also verify permission + * of the user to execute the command. + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/heapam.h" +#include "catalog/catname.h" +#include "catalog/namespace.h" +#include "catalog/pg_aggregate.h" +#include "commands/comment.h" +#include "commands/defrem.h" +#include "miscadmin.h" +#include "parser/parse_func.h" +#include "parser/parse_type.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/syscache.h" + + +/* + * DefineAggregate + */ +void +DefineAggregate(List *names, List *parameters) +{ + char *aggName; + Oid aggNamespace; + List *transfuncName = NIL; + List *finalfuncName = NIL; + TypeName *baseType = NULL; + TypeName *transType = NULL; + char *initval = NULL; + Oid baseTypeId; + Oid transTypeId; + List *pl; + + /* Convert list of names to a name and namespace */ + aggNamespace = QualifiedNameGetCreationNamespace(names, &aggName); + + foreach(pl, parameters) + { + DefElem *defel = (DefElem *) lfirst(pl); + + /* + * sfunc1, stype1, and initcond1 are accepted as obsolete + * spellings for sfunc, stype, initcond. + */ + if (strcasecmp(defel->defname, "sfunc") == 0) + transfuncName = defGetQualifiedName(defel); + else if (strcasecmp(defel->defname, "sfunc1") == 0) + transfuncName = defGetQualifiedName(defel); + else if (strcasecmp(defel->defname, "finalfunc") == 0) + finalfuncName = defGetQualifiedName(defel); + else if (strcasecmp(defel->defname, "basetype") == 0) + baseType = defGetTypeName(defel); + else if (strcasecmp(defel->defname, "stype") == 0) + transType = defGetTypeName(defel); + else if (strcasecmp(defel->defname, "stype1") == 0) + transType = defGetTypeName(defel); + else if (strcasecmp(defel->defname, "initcond") == 0) + initval = defGetString(defel); + else if (strcasecmp(defel->defname, "initcond1") == 0) + initval = defGetString(defel); + else + elog(WARNING, "DefineAggregate: attribute \"%s\" not recognized", + defel->defname); + } + + /* + * make sure we have our required definitions + */ + if (baseType == NULL) + elog(ERROR, "Define: \"basetype\" unspecified"); + if (transType == NULL) + elog(ERROR, "Define: \"stype\" unspecified"); + if (transfuncName == NIL) + elog(ERROR, "Define: \"sfunc\" unspecified"); + + /* + * Handle the aggregate's base type (input data type). This can be + * specified as 'ANY' for a data-independent transition function, such + * as COUNT(*). + */ + baseTypeId = LookupTypeName(baseType); + if (OidIsValid(baseTypeId)) + { + /* no need to allow aggregates on as-yet-undefined types */ + if (!get_typisdefined(baseTypeId)) + elog(ERROR, "Type \"%s\" is only a shell", + TypeNameToString(baseType)); + } + else + { + char *typnam = TypeNameToString(baseType); + + if (strcasecmp(typnam, "ANY") != 0) + elog(ERROR, "Type \"%s\" does not exist", typnam); + baseTypeId = InvalidOid; + } + + /* handle transtype --- no special cases here */ + transTypeId = typenameTypeId(transType); + + /* + * Most of the argument-checking is done inside of AggregateCreate + */ + AggregateCreate(aggName, /* aggregate name */ + aggNamespace, /* namespace */ + transfuncName, /* step function name */ + finalfuncName, /* final function name */ + baseTypeId, /* type of data being aggregated */ + transTypeId, /* transition data type */ + initval); /* initial condition */ +} + + +void +RemoveAggregate(List *aggName, TypeName *aggType) +{ + Relation relation; + HeapTuple tup; + Oid basetypeID; + Oid procOid; + + /* + * if a basetype is passed in, then attempt to find an aggregate for + * that specific type. + * + * else if the basetype is blank, then attempt to find an aggregate with + * a basetype of zero. This is valid. It means that the aggregate is + * to apply to all basetypes (eg, COUNT). + */ + if (aggType) + basetypeID = typenameTypeId(aggType); + else + basetypeID = InvalidOid; + + procOid = find_aggregate_func("RemoveAggregate", aggName, basetypeID); + + /* Permission check */ + + if (!pg_proc_ownercheck(procOid, GetUserId())) + { + if (basetypeID == InvalidOid) + elog(ERROR, "RemoveAggregate: aggregate %s for all types: permission denied", + NameListToString(aggName)); + else + elog(ERROR, "RemoveAggregate: aggregate %s for type %s: permission denied", + NameListToString(aggName), format_type_be(basetypeID)); + } + + /* Remove the pg_proc tuple */ + + relation = heap_openr(ProcedureRelationName, RowExclusiveLock); + + tup = SearchSysCache(PROCOID, + ObjectIdGetDatum(procOid), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) /* should not happen */ + elog(ERROR, "RemoveAggregate: couldn't find pg_proc tuple for %s", + NameListToString(aggName)); + + /* Delete any comments associated with this function */ + DeleteComments(procOid, RelationGetRelid(relation)); + + simple_heap_delete(relation, &tup->t_self); + + ReleaseSysCache(tup); + + heap_close(relation, RowExclusiveLock); + + /* Remove the pg_aggregate tuple */ + + relation = heap_openr(AggregateRelationName, RowExclusiveLock); + + tup = SearchSysCache(AGGFNOID, + ObjectIdGetDatum(procOid), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) /* should not happen */ + elog(ERROR, "RemoveAggregate: couldn't find pg_aggregate tuple for %s", + NameListToString(aggName)); + + simple_heap_delete(relation, &tup->t_self); + + ReleaseSysCache(tup); + + heap_close(relation, RowExclusiveLock); +} diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index aad5361b6b837d085c4ed7b8df261ece8b8b21ee..8883eddc49f7d0c9342507e2d063f14031dfdae4 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -15,7 +15,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.77 2002/03/31 07:49:30 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.78 2002/04/15 05:22:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -29,8 +29,7 @@ #include "catalog/pg_index.h" #include "catalog/pg_proc.h" #include "commands/cluster.h" -#include "commands/command.h" -#include "commands/rename.h" +#include "commands/tablecmds.h" #include "miscadmin.h" #include "utils/builtins.h" #include "utils/lsyscache.h" diff --git a/src/backend/commands/creatinh.c b/src/backend/commands/creatinh.c deleted file mode 100644 index 6a0cf8114738a4537d01ec0ff7ee8c3cb9221b40..0000000000000000000000000000000000000000 --- a/src/backend/commands/creatinh.c +++ /dev/null @@ -1,932 +0,0 @@ -/*------------------------------------------------------------------------- - * - * creatinh.c - * POSTGRES create/destroy relation with inheritance utility code. - * - * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/Attic/creatinh.c,v 1.96 2002/04/12 20:38:22 tgl Exp $ - * - *------------------------------------------------------------------------- - */ - -#include "postgres.h" - -#include "access/heapam.h" -#include "catalog/catalog.h" -#include "catalog/catname.h" -#include "catalog/heap.h" -#include "catalog/indexing.h" -#include "catalog/namespace.h" -#include "catalog/pg_inherits.h" -#include "catalog/pg_type.h" -#include "commands/creatinh.h" -#include "miscadmin.h" -#include "optimizer/clauses.h" -#include "parser/parse_type.h" -#include "utils/acl.h" -#include "utils/syscache.h" - - -/* ---------------- - * local stuff - * ---------------- - */ - -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 List *MergeDomainAttributes(List *schema); - - -/* ---------------------------------------------------------------- - * DefineRelation - * Creates a new relation. - * - * If successful, returns the OID of the new relation. - * ---------------------------------------------------------------- - */ -Oid -DefineRelation(CreateStmt *stmt, char relkind) -{ - char *relname = palloc(NAMEDATALEN); - Oid namespaceId; - List *schema = stmt->tableElts; - int numberOfAttributes; - 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); - - /* - * Look up the namespace in which we are supposed to create the - * relation. - */ - namespaceId = RangeVarGetCreationNamespace(stmt->relation); - - /* - * Merge domain attributes into the known columns before processing table - * inheritance. Otherwise we risk adding double constraints to a - * domain-type column that's inherited. - */ - schema = MergeDomainAttributes(schema); - - /* - * Look up inheritance ancestors and generate relation schema, - * including inherited attributes. - */ - schema = MergeAttributes(schema, stmt->inhRelations, - stmt->relation->istemp, - &inheritOids, &old_constraints, &parentHasOids); - - numberOfAttributes = length(schema); - if (numberOfAttributes <= 0) - elog(ERROR, "DefineRelation: please inherit from a relation or define an attribute"); - - /* - * 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); - - if (old_constraints != NIL) - { - ConstrCheck *check = (ConstrCheck *) palloc(length(old_constraints) * - sizeof(ConstrCheck)); - int ncheck = 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) - elog(ERROR, "Duplicate CHECK constraint name: '%s'", - cdef->name); - } - check[ncheck].ccname = cdef->name; - } - else - { - check[ncheck].ccname = (char *) palloc(NAMEDATALEN); - snprintf(check[ncheck].ccname, NAMEDATALEN, "$%d", ncheck + 1); - } - 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, - stmt->hasoids || parentHasOids, - 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 = heap_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. - * - * First, scan schema to find new column defaults. - */ - rawDefaults = NIL; - attnum = 0; - - foreach(listptr, schema) - { - ColumnDef *colDef = lfirst(listptr); - RawColumnDefault *rawEnt; - - attnum++; - - if (colDef->raw_default == NULL) - continue; - Assert(colDef->cooked_default == NULL); - - rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault)); - rawEnt->attnum = attnum; - rawEnt->raw_default = colDef->raw_default; - rawDefaults = lappend(rawDefaults, rawEnt); - } - - /* - * 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). - */ - heap_close(rel, NoLock); - - return relationId; -} - -/* - * RemoveRelation - * Deletes a relation. - * - * Exceptions: - * BadArg if name is invalid. - * - * Note: - * If the relation has indices defined on it, then the index relations - * themselves will be destroyed, too. - */ -void -RemoveRelation(const RangeVar *relation) -{ - Oid relOid; - - relOid = RangeVarGetRelid(relation, false); - heap_drop_with_catalog(relOid, allowSystemTableMods); -} - -/* - * TruncateRelation - * Removes all the rows from a relation - * - * Exceptions: - * BadArg if name is invalid - * - * Note: - * Rows are removed, indices are truncated and reconstructed. - */ -void -TruncateRelation(const RangeVar *relation) -{ - Relation rel; - Oid relid; - - /* Grab exclusive lock in preparation for truncate */ - rel = heap_openrv(relation, AccessExclusiveLock); - relid = RelationGetRelid(rel); - - if (rel->rd_rel->relkind == RELKIND_SEQUENCE) - elog(ERROR, "TRUNCATE cannot be used on sequences. '%s' is a sequence", - RelationGetRelationName(rel)); - - if (rel->rd_rel->relkind == RELKIND_VIEW) - elog(ERROR, "TRUNCATE cannot be used on views. '%s' is a view", - RelationGetRelationName(rel)); - - if (!allowSystemTableMods && IsSystemRelation(rel)) - elog(ERROR, "TRUNCATE cannot be used on system tables. '%s' is a system table", - RelationGetRelationName(rel)); - - if (!pg_class_ownercheck(relid, GetUserId())) - elog(ERROR, "you do not own relation \"%s\"", - RelationGetRelationName(rel)); - - /* Keep the lock until transaction commit */ - heap_close(rel, NoLock); - - heap_truncate(relid); -} - - -/* - * MergeDomainAttributes - * Returns a new table schema with the constraints, types, and other - * attributes of domains resolved for fields using a domain as - * their type. - */ -static List * -MergeDomainAttributes(List *schema) -{ - List *entry; - - /* - * Loop through the table elements supplied. These should - * never include inherited domains else they'll be - * double (or more) processed. - */ - foreach(entry, schema) - { - ColumnDef *coldef = lfirst(entry); - HeapTuple tuple; - Form_pg_type typeTup; - - tuple = typenameType(coldef->typename); - typeTup = (Form_pg_type) GETSTRUCT(tuple); - - if (typeTup->typtype == 'd') - { - /* Force the column to have the correct typmod. */ - coldef->typename->typmod = typeTup->typtypmod; - /* XXX more to do here? */ - } - - /* Enforce type NOT NULL || column definition NOT NULL -> NOT NULL */ - /* Currently only used for domains, but could be valid for all */ - coldef->is_not_null |= typeTup->typnotnull; - - ReleaseSysCache(tuple); - } - - return schema; -} - -/*---------- - * 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 an integer 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 - * the column will not have a default. - * (3) If conflicting defaults are inherited from different parents - * (and not overridden by the child), an error is raised. - * (4) Otherwise the inherited default is used. - * Rule (3) is new in Postgres 7.1; in earlier releases you got a - * rather arbitrary choice of which parent default to use. - *---------- - */ -static List * -MergeAttributes(List *schema, List *supers, bool istemp, - List **supOids, List **supconstr, bool *supHasOids) -{ - List *entry; - List *inhSchema = NIL; - List *parentOids = NIL; - List *constraints = NIL; - bool parentHasOids = false; - bool have_bogus_defaults = false; - char *bogus_marker = "Bogus!"; /* marks conflicting - * defaults */ - int child_attno; - - /* - * Check for duplicate names in the explicit list of attributes. - * - * Although we might consider merging such entries in the same way that - * we handle name conflicts for inherited attributes, it seems to make - * more sense to assume such conflicts are errors. - */ - foreach(entry, schema) - { - ColumnDef *coldef = lfirst(entry); - List *rest; - - foreach(rest, lnext(entry)) - { - ColumnDef *restdef = lfirst(rest); - - if (strcmp(coldef->colname, restdef->colname) == 0) - elog(ERROR, "CREATE TABLE: attribute \"%s\" duplicated", - coldef->colname); - } - } - - /* - * Scan the parents left-to-right, and merge their attributes to form - * a list of inherited attributes (inhSchema). Also check to see if - * we need to inherit an OID column. - */ - child_attno = 0; - foreach(entry, supers) - { - RangeVar *parent = (RangeVar *) lfirst(entry); - Relation relation; - TupleDesc tupleDesc; - TupleConstr *constr; - AttrNumber *newattno; - AttrNumber parent_attno; - - relation = heap_openrv(parent, AccessShareLock); - - if (relation->rd_rel->relkind != RELKIND_RELATION) - elog(ERROR, "CREATE TABLE: inherited relation \"%s\" is not a table", - parent->relname); - /* Permanent rels cannot inherit from temporary ones */ - if (!istemp && isTempNamespace(RelationGetNamespace(relation))) - elog(ERROR, "CREATE TABLE: cannot inherit from temp relation \"%s\"", - parent->relname); - - /* - * We should have an UNDER permission flag for this, but for now, - * demand that creator of a child table own the parent. - */ - if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId())) - elog(ERROR, "you do not own table \"%s\"", - parent->relname); - - /* - * Reject duplications in the list of parents. - */ - if (intMember(RelationGetRelid(relation), parentOids)) - elog(ERROR, "CREATE TABLE: inherited relation \"%s\" duplicated", - parent->relname); - - parentOids = lappendi(parentOids, RelationGetRelid(relation)); - setRelhassubclassInRelation(RelationGetRelid(relation), true); - - parentHasOids |= relation->rd_rel->relhasoids; - - tupleDesc = RelationGetDescr(relation); - constr = tupleDesc->constr; - - /* - * newattno[] will contain the child-table attribute numbers for - * the attributes of this parent table. (They are not the same - * for parents after the first one.) - */ - newattno = (AttrNumber *) palloc(tupleDesc->natts * sizeof(AttrNumber)); - - for (parent_attno = 1; parent_attno <= tupleDesc->natts; - parent_attno++) - { - Form_pg_attribute attribute = tupleDesc->attrs[parent_attno - 1]; - char *attributeName = NameStr(attribute->attname); - int exist_attno; - ColumnDef *def; - TypeName *typename; - - /* - * Does it conflict with some previously inherited column? - */ - exist_attno = findAttrByName(attributeName, inhSchema); - if (exist_attno > 0) - { - /* - * Yes, try to merge the two column definitions. They must - * have the same type and typmod. - */ - elog(NOTICE, "CREATE TABLE: merging multiple inherited definitions of attribute \"%s\"", - attributeName); - def = (ColumnDef *) nth(exist_attno - 1, inhSchema); - if (typenameTypeId(def->typename) != attribute->atttypid || - def->typename->typmod != attribute->atttypmod) - elog(ERROR, "CREATE TABLE: inherited attribute \"%s\" type conflict (%s and %s)", - attributeName, - TypeNameToString(def->typename), - typeidTypeName(attribute->atttypid)); - /* Merge of NOT NULL constraints = OR 'em together */ - def->is_not_null |= attribute->attnotnull; - /* Default and other constraints are handled below */ - newattno[parent_attno - 1] = exist_attno; - } - else - { - /* - * No, create a new inherited column - */ - def = makeNode(ColumnDef); - def->colname = pstrdup(attributeName); - typename = makeNode(TypeName); - typename->typeid = attribute->atttypid; - typename->typmod = attribute->atttypmod; - def->typename = typename; - def->is_not_null = attribute->attnotnull; - def->raw_default = NULL; - def->cooked_default = NULL; - def->constraints = NIL; - inhSchema = lappend(inhSchema, def); - newattno[parent_attno - 1] = ++child_attno; - } - - /* - * Copy default if any - */ - if (attribute->atthasdef) - { - char *this_default = NULL; - AttrDefault *attrdef; - int i; - - /* Find default in constraint structure */ - Assert(constr != NULL); - attrdef = constr->defval; - for (i = 0; i < constr->num_defval; i++) - { - if (attrdef[i].adnum == parent_attno) - { - this_default = attrdef[i].adbin; - break; - } - } - Assert(this_default != NULL); - - /* - * If default expr could contain any vars, we'd need to - * fix 'em, but it can't; so default is ready to apply to - * child. - * - * If we already had a default from some prior parent, check - * to see if they are the same. If so, no problem; if - * not, mark the column as having a bogus default. Below, - * we will complain if the bogus default isn't overridden - * by the child schema. - */ - Assert(def->raw_default == NULL); - if (def->cooked_default == NULL) - def->cooked_default = pstrdup(this_default); - else if (strcmp(def->cooked_default, this_default) != 0) - { - def->cooked_default = bogus_marker; - have_bogus_defaults = true; - } - } - } - - /* - * Now copy the constraints of this parent, adjusting attnos using - * the completed newattno[] map - */ - if (constr && constr->num_check > 0) - { - ConstrCheck *check = constr->check; - int i; - - for (i = 0; i < constr->num_check; i++) - { - Constraint *cdef = makeNode(Constraint); - Node *expr; - - cdef->contype = CONSTR_CHECK; - if (check[i].ccname[0] == '$') - cdef->name = NULL; - else - cdef->name = pstrdup(check[i].ccname); - cdef->raw_expr = NULL; - /* adjust varattnos of ccbin here */ - expr = stringToNode(check[i].ccbin); - change_varattnos_of_a_node(expr, newattno); - cdef->cooked_expr = nodeToString(expr); - constraints = lappend(constraints, cdef); - } - } - - pfree(newattno); - - /* - * Close the parent rel, but keep our AccessShareLock on it until - * xact commit. That will prevent someone else from deleting or - * ALTERing the parent before the child is committed. - */ - heap_close(relation, NoLock); - } - - /* - * If we had no inherited attributes, the result schema is just the - * explicitly declared columns. Otherwise, we need to merge the - * declared columns into the inherited schema list. - */ - if (inhSchema != NIL) - { - foreach(entry, schema) - { - ColumnDef *newdef = lfirst(entry); - char *attributeName = newdef->colname; - int exist_attno; - - /* - * Does it conflict with some previously inherited column? - */ - exist_attno = findAttrByName(attributeName, inhSchema); - if (exist_attno > 0) - { - ColumnDef *def; - - /* - * Yes, try to merge the two column definitions. They must - * have the same type and typmod. - */ - elog(NOTICE, "CREATE TABLE: merging attribute \"%s\" with inherited definition", - attributeName); - def = (ColumnDef *) nth(exist_attno - 1, inhSchema); - if (typenameTypeId(def->typename) != typenameTypeId(newdef->typename) || - def->typename->typmod != newdef->typename->typmod) - elog(ERROR, "CREATE TABLE: attribute \"%s\" type conflict (%s and %s)", - attributeName, - TypeNameToString(def->typename), - TypeNameToString(newdef->typename)); - /* Merge of NOT NULL constraints = OR 'em together */ - def->is_not_null |= newdef->is_not_null; - /* If new def has a default, override previous default */ - if (newdef->raw_default != NULL) - { - def->raw_default = newdef->raw_default; - def->cooked_default = newdef->cooked_default; - } - } - else - { - /* - * No, attach new column to result schema - */ - inhSchema = lappend(inhSchema, newdef); - } - } - - schema = inhSchema; - } - - /* - * If we found any conflicting parent default values, check to make - * sure they were overridden by the child. - */ - if (have_bogus_defaults) - { - foreach(entry, schema) - { - ColumnDef *def = lfirst(entry); - - if (def->cooked_default == bogus_marker) - elog(ERROR, "CREATE TABLE: attribute \"%s\" inherits conflicting default values" - "\n\tTo resolve the conflict, specify a default explicitly", - def->colname); - } - } - - *supOids = parentOids; - *supconstr = constraints; - *supHasOids = parentHasOids; - return schema; -} - -/* - * complementary static functions for MergeAttributes(). - * - * Varattnos of pg_relcheck.rcbin must be rewritten when subclasses inherit - * constraints from parent classes, since the inherited attributes could - * be given different column numbers in multiple-inheritance cases. - * - * Note that the passed node tree is modified in place! - */ -static bool -change_varattnos_walker(Node *node, const AttrNumber *newattno) -{ - if (node == NULL) - return false; - if (IsA(node, Var)) - { - Var *var = (Var *) node; - - if (var->varlevelsup == 0 && var->varno == 1 && - var->varattno > 0) - { - /* - * ??? the following may be a problem when the node is - * multiply referenced though stringToNode() doesn't create - * such a node currently. - */ - Assert(newattno[var->varattno - 1] > 0); - var->varattno = newattno[var->varattno - 1]; - } - return false; - } - return expression_tree_walker(node, change_varattnos_walker, - (void *) newattno); -} - -static bool -change_varattnos_of_a_node(Node *node, const AttrNumber *newattno) -{ - return change_varattnos_walker(node, newattno); -} - -/* - * StoreCatalogInheritance - * Updates the system catalogs with proper inheritance information. - * - * supers is an integer list of the OIDs of the new relation's direct - * ancestors. NB: it is destructively changed to include indirect ancestors. - */ -static void -StoreCatalogInheritance(Oid relationId, List *supers) -{ - Relation relation; - TupleDesc desc; - int16 seqNumber; - List *entry; - HeapTuple tuple; - - /* - * sanity checks - */ - AssertArg(OidIsValid(relationId)); - - if (supers == NIL) - return; - - /* - * Catalog INHERITS information using direct ancestors only. - */ - relation = heap_openr(InheritsRelationName, RowExclusiveLock); - desc = RelationGetDescr(relation); - - seqNumber = 1; - foreach(entry, supers) - { - Oid entryOid = lfirsti(entry); - Datum datum[Natts_pg_inherits]; - char nullarr[Natts_pg_inherits]; - - datum[0] = ObjectIdGetDatum(relationId); /* inhrel */ - datum[1] = ObjectIdGetDatum(entryOid); /* inhparent */ - datum[2] = Int16GetDatum(seqNumber); /* inhseqno */ - - nullarr[0] = ' '; - nullarr[1] = ' '; - nullarr[2] = ' '; - - tuple = heap_formtuple(desc, datum, nullarr); - - heap_insert(relation, tuple); - - if (RelationGetForm(relation)->relhasindex) - { - Relation idescs[Num_pg_inherits_indices]; - - CatalogOpenIndices(Num_pg_inherits_indices, Name_pg_inherits_indices, idescs); - CatalogIndexInsert(idescs, Num_pg_inherits_indices, relation, tuple); - CatalogCloseIndices(Num_pg_inherits_indices, idescs); - } - - heap_freetuple(tuple); - - seqNumber += 1; - } - - heap_close(relation, RowExclusiveLock); - - /* ---------------- - * Expand supers list to include indirect ancestors as well. - * - * Algorithm: - * 0. begin with list of direct superclasses. - * 1. append after each relationId, its superclasses, recursively. - * 2. remove all but last of duplicates. - * ---------------- - */ - - /* - * 1. append after each relationId, its superclasses, recursively. - */ - foreach(entry, supers) - { - HeapTuple tuple; - Oid id; - int16 number; - List *next; - List *current; - - id = (Oid) lfirsti(entry); - current = entry; - next = lnext(entry); - - for (number = 1;; number += 1) - { - tuple = SearchSysCache(INHRELID, - ObjectIdGetDatum(id), - Int16GetDatum(number), - 0, 0); - if (!HeapTupleIsValid(tuple)) - break; - - lnext(current) = lconsi(((Form_pg_inherits) - GETSTRUCT(tuple))->inhparent, - NIL); - - ReleaseSysCache(tuple); - - current = lnext(current); - } - lnext(current) = next; - } - - /* - * 2. remove all but last of duplicates. - */ - foreach(entry, supers) - { - Oid thisone; - bool found; - List *rest; - -again: - thisone = lfirsti(entry); - found = false; - foreach(rest, lnext(entry)) - { - if (thisone == lfirsti(rest)) - { - found = true; - break; - } - } - if (found) - { - /* - * found a later duplicate, so remove this entry. - */ - lfirsti(entry) = lfirsti(lnext(entry)); - lnext(entry) = lnext(lnext(entry)); - - goto again; - } - } -} - -/* - * Look for an existing schema entry with the given name. - * - * Returns the index (starting with 1) if attribute already exists in schema, - * 0 if it doesn't. - */ -static int -findAttrByName(const char *attributeName, List *schema) -{ - List *s; - int i = 0; - - foreach(s, schema) - { - ColumnDef *def = lfirst(s); - - ++i; - if (strcmp(attributeName, def->colname) == 0) - return i; - } - return 0; -} - -/* - * Update a relation's pg_class.relhassubclass entry to the given value - */ -static void -setRelhassubclassInRelation(Oid relationId, bool relhassubclass) -{ - Relation relationRelation; - HeapTuple tuple; - Relation idescs[Num_pg_class_indices]; - - /* - * Fetch a modifiable copy of the tuple, modify it, update pg_class. - */ - relationRelation = heap_openr(RelationRelationName, RowExclusiveLock); - tuple = SearchSysCacheCopy(RELOID, - ObjectIdGetDatum(relationId), - 0, 0, 0); - if (!HeapTupleIsValid(tuple)) - elog(ERROR, "setRelhassubclassInRelation: cache lookup failed for relation %u", relationId); - - ((Form_pg_class) GETSTRUCT(tuple))->relhassubclass = relhassubclass; - simple_heap_update(relationRelation, &tuple->t_self, tuple); - - /* keep the catalog indices up to date */ - CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs); - CatalogIndexInsert(idescs, Num_pg_class_indices, relationRelation, tuple); - CatalogCloseIndices(Num_pg_class_indices, idescs); - - heap_freetuple(tuple); - heap_close(relationRelation, RowExclusiveLock); -} diff --git a/src/backend/commands/define.c b/src/backend/commands/define.c index 692fc9f957c4a389dd1536d1d2deb1c6c1c9f10c..889ddd0f44026a578d78c8cfb1d67d890cd0ec54 100644 --- a/src/backend/commands/define.c +++ b/src/backend/commands/define.c @@ -1,16 +1,15 @@ /*------------------------------------------------------------------------- * * define.c + * Support routines for various kinds of object creation. * - * These routines execute some of the CREATE statements. In an earlier - * version of Postgres, these were "define" statements. * * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.75 2002/04/11 19:59:57 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.76 2002/04/15 05:22:03 tgl Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -28,8 +27,6 @@ * "create operator": * operators * - * Most of the parse-tree manipulation routines are defined in - * commands/manip.c. * *------------------------------------------------------------------------- */ @@ -38,42 +35,16 @@ #include <ctype.h> #include <math.h> -#include "access/heapam.h" -#include "catalog/catname.h" -#include "catalog/heap.h" -#include "catalog/namespace.h" -#include "catalog/pg_aggregate.h" -#include "catalog/pg_language.h" -#include "catalog/pg_operator.h" -#include "catalog/pg_proc.h" -#include "catalog/pg_type.h" #include "commands/defrem.h" -#include "fmgr.h" -#include "miscadmin.h" -#include "optimizer/cost.h" -#include "parser/parse_func.h" #include "parser/parse_type.h" -#include "utils/acl.h" -#include "utils/builtins.h" -#include "utils/fmgroids.h" -#include "utils/lsyscache.h" -#include "utils/syscache.h" - - -static Oid findTypeIOFunction(List *procname, bool isOutput); -static char *defGetString(DefElem *def); -static double defGetNumeric(DefElem *def); -static List *defGetQualifiedName(DefElem *def); -static TypeName *defGetTypeName(DefElem *def); -static int defGetTypeLength(DefElem *def); - -#define DEFAULT_TYPDELIM ',' /* * Translate the input language name to lower case. + * + * Output buffer should be NAMEDATALEN long. */ -static void +void case_translate_language_name(const char *input, char *output) { int i; @@ -86,1021 +57,9 @@ case_translate_language_name(const char *input, char *output) /* - * Examine the "returns" clause returnType of the CREATE FUNCTION statement - * and return information about it as *prorettype_p and *returnsSet. - * - * This is more complex than the average typename lookup because we want to - * allow a shell type to be used, or even created if the specified return type - * doesn't exist yet. (Without this, there's no way to define the I/O procs - * for a new type.) But SQL function creation won't cope, so error out if - * the target language is SQL. - */ -static void -compute_return_type(TypeName *returnType, Oid languageOid, - Oid *prorettype_p, bool *returnsSet_p) -{ - Oid rettype; - - rettype = LookupTypeName(returnType); - - if (OidIsValid(rettype)) - { - if (!get_typisdefined(rettype)) - { - if (languageOid == SQLlanguageId) - elog(ERROR, "SQL functions cannot return shell types"); - else - elog(WARNING, "Return type \"%s\" is only a shell", - TypeNameToString(returnType)); - } - } - else - { - char *typnam = TypeNameToString(returnType); - - if (strcmp(typnam, "opaque") == 0) - rettype = InvalidOid; - else - { - Oid namespaceId; - char *typname; - - if (languageOid == SQLlanguageId) - elog(ERROR, "Type \"%s\" does not exist", typnam); - elog(WARNING, "ProcedureCreate: type %s is not yet defined", - typnam); - namespaceId = QualifiedNameGetCreationNamespace(returnType->names, - &typname); - rettype = TypeShellMake(typname, namespaceId); - if (!OidIsValid(rettype)) - elog(ERROR, "could not create type %s", typnam); - } - } - - *prorettype_p = rettype; - *returnsSet_p = returnType->setof; -} - -/* - * Interpret the argument-types list of the CREATE FUNCTION statement. - */ -static int -compute_parameter_types(List *argTypes, Oid languageOid, - Oid *parameterTypes) -{ - int parameterCount = 0; - List *x; - - MemSet(parameterTypes, 0, FUNC_MAX_ARGS * sizeof(Oid)); - foreach(x, argTypes) - { - TypeName *t = (TypeName *) lfirst(x); - Oid toid; - - if (parameterCount >= FUNC_MAX_ARGS) - elog(ERROR, "functions cannot have more than %d arguments", - FUNC_MAX_ARGS); - - toid = LookupTypeName(t); - if (OidIsValid(toid)) - { - if (!get_typisdefined(toid)) - elog(WARNING, "Argument type \"%s\" is only a shell", - TypeNameToString(t)); - } - else - { - char *typnam = TypeNameToString(t); - - if (strcmp(typnam, "opaque") == 0) - { - if (languageOid == SQLlanguageId) - elog(ERROR, "SQL functions cannot have arguments of type \"opaque\""); - toid = InvalidOid; - } - else - elog(ERROR, "Type \"%s\" does not exist", typnam); - } - - if (t->setof) - elog(ERROR, "functions cannot accept set arguments"); - - parameterTypes[parameterCount++] = toid; - } - - return parameterCount; -} - -/*------------- - * Interpret the parameters *parameters and return their contents as - * *byte_pct_p, etc. - * - * These parameters supply optional information about a function. - * All have defaults if not specified. - * - * Note: currently, only three of these parameters actually do anything: - * - * * isImplicit means the function may be used as an implicit type - * coercion. - * - * * isStrict means the function should not be called when any NULL - * inputs are present; instead a NULL result value should be assumed. - * - * * volatility tells the optimizer whether the function's result can - * be assumed to be repeatable over multiple evaluations. - * - * The other four parameters are not used anywhere. They used to be - * used in the "expensive functions" optimizer, but that's been dead code - * for a long time. - *------------ - */ -static void -compute_full_attributes(List *parameters, - int32 *byte_pct_p, int32 *perbyte_cpu_p, - int32 *percall_cpu_p, int32 *outin_ratio_p, - bool *isImplicit_p, bool *isStrict_p, - char *volatility_p) -{ - List *pl; - - /* the defaults */ - *byte_pct_p = BYTE_PCT; - *perbyte_cpu_p = PERBYTE_CPU; - *percall_cpu_p = PERCALL_CPU; - *outin_ratio_p = OUTIN_RATIO; - *isImplicit_p = false; - *isStrict_p = false; - *volatility_p = PROVOLATILE_VOLATILE; - - foreach(pl, parameters) - { - DefElem *param = (DefElem *) lfirst(pl); - - if (strcasecmp(param->defname, "implicitcoercion") == 0) - *isImplicit_p = true; - else if (strcasecmp(param->defname, "isstrict") == 0) - *isStrict_p = true; - else if (strcasecmp(param->defname, "isimmutable") == 0) - *volatility_p = PROVOLATILE_IMMUTABLE; - else if (strcasecmp(param->defname, "isstable") == 0) - *volatility_p = PROVOLATILE_STABLE; - else if (strcasecmp(param->defname, "isvolatile") == 0) - *volatility_p = PROVOLATILE_VOLATILE; - else if (strcasecmp(param->defname, "iscachable") == 0) - { - /* obsolete spelling of isImmutable */ - *volatility_p = PROVOLATILE_IMMUTABLE; - } - else if (strcasecmp(param->defname, "trusted") == 0) - { - /* - * we don't have untrusted functions any more. The 4.2 - * implementation is lousy anyway so I took it out. -ay 10/94 - */ - elog(ERROR, "untrusted function has been decommissioned."); - } - else if (strcasecmp(param->defname, "byte_pct") == 0) - *byte_pct_p = (int) defGetNumeric(param); - else if (strcasecmp(param->defname, "perbyte_cpu") == 0) - *perbyte_cpu_p = (int) defGetNumeric(param); - else if (strcasecmp(param->defname, "percall_cpu") == 0) - *percall_cpu_p = (int) defGetNumeric(param); - else if (strcasecmp(param->defname, "outin_ratio") == 0) - *outin_ratio_p = (int) defGetNumeric(param); - else - elog(WARNING, "Unrecognized function attribute '%s' ignored", - param->defname); - } -} - - -/* - * For a dynamically linked C language object, the form of the clause is - * - * AS <object file name> [, <link symbol name> ] - * - * In all other cases - * - * AS <object reference, or sql code> - * - */ - -static void -interpret_AS_clause(Oid languageOid, const char *languageName, const List *as, - char **prosrc_str_p, char **probin_str_p) -{ - Assert(as != NIL); - - if (languageOid == ClanguageId) - { - /* - * For "C" language, store the file name in probin and, when - * given, the link symbol name in prosrc. - */ - *probin_str_p = strVal(lfirst(as)); - if (lnext(as) == NULL) - *prosrc_str_p = "-"; - else - *prosrc_str_p = strVal(lsecond(as)); - } - else - { - /* Everything else wants the given string in prosrc. */ - *prosrc_str_p = strVal(lfirst(as)); - *probin_str_p = "-"; - - if (lnext(as) != NIL) - elog(ERROR, "CREATE FUNCTION: only one AS item needed for %s language", - languageName); - } -} - - - -/* - * CreateFunction - * Execute a CREATE FUNCTION utility statement. - */ -void -CreateFunction(ProcedureStmt *stmt) -{ - char *probin_str; - char *prosrc_str; - Oid prorettype; - bool returnsSet; - char languageName[NAMEDATALEN]; - Oid languageOid; - char *funcname; - Oid namespaceId; - int parameterCount; - Oid parameterTypes[FUNC_MAX_ARGS]; - int32 byte_pct, - perbyte_cpu, - percall_cpu, - outin_ratio; - bool isImplicit, - isStrict; - char volatility; - HeapTuple languageTuple; - Form_pg_language languageStruct; - - /* Convert list of names to a name and namespace */ - namespaceId = QualifiedNameGetCreationNamespace(stmt->funcname, - &funcname); - - /* Convert language name to canonical case */ - case_translate_language_name(stmt->language, languageName); - - /* Look up the language and validate permissions */ - languageTuple = SearchSysCache(LANGNAME, - PointerGetDatum(languageName), - 0, 0, 0); - if (!HeapTupleIsValid(languageTuple)) - elog(ERROR, "language \"%s\" does not exist", languageName); - - languageOid = languageTuple->t_data->t_oid; - languageStruct = (Form_pg_language) GETSTRUCT(languageTuple); - - if (!((languageStruct->lanpltrusted - && pg_language_aclcheck(languageOid, GetUserId()) == ACLCHECK_OK) - || superuser())) - elog(ERROR, "permission denied"); - - ReleaseSysCache(languageTuple); - - /* - * Convert remaining parameters of CREATE to form wanted by - * ProcedureCreate. - */ - compute_return_type(stmt->returnType, languageOid, - &prorettype, &returnsSet); - - parameterCount = compute_parameter_types(stmt->argTypes, languageOid, - parameterTypes); - - compute_full_attributes(stmt->withClause, - &byte_pct, &perbyte_cpu, &percall_cpu, - &outin_ratio, &isImplicit, &isStrict, - &volatility); - - interpret_AS_clause(languageOid, languageName, stmt->as, - &prosrc_str, &probin_str); - - /* - * And now that we have all the parameters, and know we're permitted - * to do so, go ahead and create the function. - */ - ProcedureCreate(funcname, - namespaceId, - stmt->replace, - returnsSet, - prorettype, - languageOid, - prosrc_str, /* converted to text later */ - probin_str, /* converted to text later */ - false, /* not an aggregate */ - true, /* (obsolete "trusted") */ - isImplicit, - isStrict, - volatility, - byte_pct, - perbyte_cpu, - percall_cpu, - outin_ratio, - parameterCount, - parameterTypes); -} - - -/* - * DefineOperator - * this function extracts all the information from the - * parameter list generated by the parser and then has - * OperatorCreate() do all the actual work. - * - * 'parameters' is a list of DefElem - */ -void -DefineOperator(List *names, List *parameters) -{ - char *oprName; - Oid oprNamespace; - uint16 precedence = 0; /* operator precedence */ - bool canHash = false; /* operator hashes */ - bool isLeftAssociative = true; /* operator is left - * associative */ - char *functionName = NULL; /* function for operator */ - TypeName *typeName1 = NULL; /* first type name */ - TypeName *typeName2 = NULL; /* second type name */ - Oid typeId1 = InvalidOid; /* types converted to OID */ - Oid typeId2 = InvalidOid; - char *commutatorName = NULL; /* optional commutator operator - * name */ - char *negatorName = NULL; /* optional negator operator name */ - char *restrictionName = NULL; /* optional restrict. sel. - * procedure */ - char *joinName = NULL; /* optional join sel. procedure name */ - char *sortName1 = NULL; /* optional first sort operator */ - char *sortName2 = NULL; /* optional second sort operator */ - List *pl; - - /* Convert list of names to a name and namespace */ - oprNamespace = QualifiedNameGetCreationNamespace(names, &oprName); - - /* - * loop over the definition list and extract the information we need. - */ - foreach(pl, parameters) - { - DefElem *defel = (DefElem *) lfirst(pl); - - if (strcasecmp(defel->defname, "leftarg") == 0) - { - typeName1 = defGetTypeName(defel); - if (typeName1->setof) - elog(ERROR, "setof type not implemented for leftarg"); - } - else if (strcasecmp(defel->defname, "rightarg") == 0) - { - typeName2 = defGetTypeName(defel); - if (typeName2->setof) - elog(ERROR, "setof type not implemented for rightarg"); - } - else if (strcasecmp(defel->defname, "procedure") == 0) - functionName = defGetString(defel); - else if (strcasecmp(defel->defname, "precedence") == 0) - { - /* NOT IMPLEMENTED (never worked in v4.2) */ - elog(NOTICE, "CREATE OPERATOR: precedence not implemented"); - } - else if (strcasecmp(defel->defname, "associativity") == 0) - { - /* NOT IMPLEMENTED (never worked in v4.2) */ - elog(NOTICE, "CREATE OPERATOR: associativity not implemented"); - } - else if (strcasecmp(defel->defname, "commutator") == 0) - commutatorName = defGetString(defel); - else if (strcasecmp(defel->defname, "negator") == 0) - negatorName = defGetString(defel); - else if (strcasecmp(defel->defname, "restrict") == 0) - restrictionName = defGetString(defel); - else if (strcasecmp(defel->defname, "join") == 0) - joinName = defGetString(defel); - else if (strcasecmp(defel->defname, "hashes") == 0) - canHash = TRUE; - else if (strcasecmp(defel->defname, "sort1") == 0) - sortName1 = defGetString(defel); - else if (strcasecmp(defel->defname, "sort2") == 0) - sortName2 = defGetString(defel); - else - { - elog(WARNING, "DefineOperator: attribute \"%s\" not recognized", - defel->defname); - } - } - - /* - * make sure we have our required definitions - */ - if (functionName == NULL) - elog(ERROR, "Define: \"procedure\" unspecified"); - - /* Transform type names to type OIDs */ - if (typeName1) - typeId1 = typenameTypeId(typeName1); - if (typeName2) - typeId2 = typenameTypeId(typeName2); - - /* - * now have OperatorCreate do all the work.. - */ - OperatorCreate(oprName, /* operator name */ - typeId1, /* left type id */ - typeId2, /* right type id */ - functionName, /* function for operator */ - precedence, /* operator precedence */ - isLeftAssociative, /* operator is left associative */ - commutatorName, /* optional commutator operator - * name */ - negatorName, /* optional negator operator name */ - restrictionName, /* optional restrict. sel. - * procedure */ - joinName, /* optional join sel. procedure name */ - canHash, /* operator hashes */ - sortName1, /* optional first sort operator */ - sortName2); /* optional second sort operator */ - -} - -/* - * DefineAggregate - */ -void -DefineAggregate(List *names, List *parameters) -{ - char *aggName; - Oid aggNamespace; - List *transfuncName = NIL; - List *finalfuncName = NIL; - TypeName *baseType = NULL; - TypeName *transType = NULL; - char *initval = NULL; - Oid baseTypeId; - Oid transTypeId; - List *pl; - - /* Convert list of names to a name and namespace */ - aggNamespace = QualifiedNameGetCreationNamespace(names, &aggName); - - foreach(pl, parameters) - { - DefElem *defel = (DefElem *) lfirst(pl); - - /* - * sfunc1, stype1, and initcond1 are accepted as obsolete - * spellings for sfunc, stype, initcond. - */ - if (strcasecmp(defel->defname, "sfunc") == 0) - transfuncName = defGetQualifiedName(defel); - else if (strcasecmp(defel->defname, "sfunc1") == 0) - transfuncName = defGetQualifiedName(defel); - else if (strcasecmp(defel->defname, "finalfunc") == 0) - finalfuncName = defGetQualifiedName(defel); - else if (strcasecmp(defel->defname, "basetype") == 0) - baseType = defGetTypeName(defel); - else if (strcasecmp(defel->defname, "stype") == 0) - transType = defGetTypeName(defel); - else if (strcasecmp(defel->defname, "stype1") == 0) - transType = defGetTypeName(defel); - else if (strcasecmp(defel->defname, "initcond") == 0) - initval = defGetString(defel); - else if (strcasecmp(defel->defname, "initcond1") == 0) - initval = defGetString(defel); - else - elog(WARNING, "DefineAggregate: attribute \"%s\" not recognized", - defel->defname); - } - - /* - * make sure we have our required definitions - */ - if (baseType == NULL) - elog(ERROR, "Define: \"basetype\" unspecified"); - if (transType == NULL) - elog(ERROR, "Define: \"stype\" unspecified"); - if (transfuncName == NIL) - elog(ERROR, "Define: \"sfunc\" unspecified"); - - /* - * Handle the aggregate's base type (input data type). This can be - * specified as 'ANY' for a data-independent transition function, such - * as COUNT(*). - */ - baseTypeId = LookupTypeName(baseType); - if (OidIsValid(baseTypeId)) - { - /* no need to allow aggregates on as-yet-undefined types */ - if (!get_typisdefined(baseTypeId)) - elog(ERROR, "Type \"%s\" is only a shell", - TypeNameToString(baseType)); - } - else - { - char *typnam = TypeNameToString(baseType); - - if (strcasecmp(typnam, "ANY") != 0) - elog(ERROR, "Type \"%s\" does not exist", typnam); - baseTypeId = InvalidOid; - } - - /* handle transtype --- no special cases here */ - transTypeId = typenameTypeId(transType); - - /* - * Most of the argument-checking is done inside of AggregateCreate - */ - AggregateCreate(aggName, /* aggregate name */ - aggNamespace, /* namespace */ - transfuncName, /* step function name */ - finalfuncName, /* final function name */ - baseTypeId, /* type of data being aggregated */ - transTypeId, /* transition data type */ - initval); /* initial condition */ -} - -/* - * DefineDomain - * Registers a new domain. - */ -void -DefineDomain(CreateDomainStmt *stmt) -{ - char *domainName; - Oid domainNamespace; - int16 internalLength; - int16 externalLength; - Oid inputProcedure; - Oid outputProcedure; - Oid receiveProcedure; - Oid sendProcedure; - bool byValue; - char delimiter; - char alignment; - char storage; - char typtype; - Datum datum; - bool isnull; - char *defaultValue = NULL; - char *defaultValueBin = NULL; - bool typNotNull = false; - Oid basetypelem; - int32 typNDims = length(stmt->typename->arrayBounds); - HeapTuple typeTup; - List *schema = stmt->constraints; - List *listptr; - - /* Convert list of names to a name and namespace */ - domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname, - &domainName); - - /* - * Domainnames, unlike typenames don't need to account for the '_' - * prefix. So they can be one character longer. - */ - if (strlen(domainName) > (NAMEDATALEN - 1)) - elog(ERROR, "CREATE DOMAIN: domain names must be %d characters or less", - NAMEDATALEN - 1); - - /* - * Look up the base type. - */ - typeTup = typenameType(stmt->typename); - - /* - * What we really don't want is domains of domains. This could cause all sorts - * of neat issues if we allow that. - * - * With testing, we may determine complex types should be allowed - */ - typtype = ((Form_pg_type) GETSTRUCT(typeTup))->typtype; - if (typtype != 'b') - elog(ERROR, "DefineDomain: %s is not a basetype", - TypeNameToString(stmt->typename)); - - /* passed by value */ - byValue = ((Form_pg_type) GETSTRUCT(typeTup))->typbyval; - - /* Required Alignment */ - alignment = ((Form_pg_type) GETSTRUCT(typeTup))->typalign; - - /* TOAST Strategy */ - storage = ((Form_pg_type) GETSTRUCT(typeTup))->typstorage; - - /* Storage Length */ - internalLength = ((Form_pg_type) GETSTRUCT(typeTup))->typlen; - - /* External Length (unused) */ - externalLength = ((Form_pg_type) GETSTRUCT(typeTup))->typprtlen; - - /* Array element Delimiter */ - delimiter = ((Form_pg_type) GETSTRUCT(typeTup))->typdelim; - - /* I/O Functions */ - inputProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typinput; - outputProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typoutput; - receiveProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typreceive; - sendProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typsend; - - /* Inherited default value */ - datum = SysCacheGetAttr(TYPEOID, typeTup, - Anum_pg_type_typdefault, &isnull); - if (!isnull) - defaultValue = DatumGetCString(DirectFunctionCall1(textout, datum)); - - /* Inherited default binary value */ - datum = SysCacheGetAttr(TYPEOID, typeTup, - Anum_pg_type_typdefaultbin, &isnull); - if (!isnull) - defaultValueBin = DatumGetCString(DirectFunctionCall1(textout, datum)); - - /* - * Pull out the typelem name of the parent OID. - * - * This is what enables us to make a domain of an array - */ - basetypelem = ((Form_pg_type) GETSTRUCT(typeTup))->typelem; - - /* - * Run through constraints manually to avoid the additional - * processing conducted by DefineRelation() and friends. - * - * Besides, we don't want any constraints to be cooked. We'll - * do that when the table is created via MergeDomainAttributes(). - */ - foreach(listptr, schema) - { - Constraint *colDef = lfirst(listptr); - bool nullDefined = false; - Node *expr; - ParseState *pstate; - - switch (colDef->contype) - { - /* - * The inherited default value may be overridden by the user - * with the DEFAULT <expr> statement. - * - * We have to search the entire constraint tree returned as we - * don't want to cook or fiddle too much. - */ - case CONSTR_DEFAULT: - /* Create a dummy ParseState for transformExpr */ - pstate = make_parsestate(NULL); - /* - * Cook the colDef->raw_expr into an expression. - * Note: Name is strictly for error message - */ - expr = cookDefault(pstate, colDef->raw_expr, - typeTup->t_data->t_oid, - stmt->typename->typmod, - domainName); - /* - * Expression must be stored as a nodeToString result, - * but we also require a valid textual representation - * (mainly to make life easier for pg_dump). - */ - defaultValue = deparse_expression(expr, - deparse_context_for(domainName, - InvalidOid), - false); - defaultValueBin = nodeToString(expr); - break; - - /* - * Find the NULL constraint. - */ - case CONSTR_NOTNULL: - if (nullDefined) { - elog(ERROR, "CREATE DOMAIN has conflicting NULL / NOT NULL constraint"); - } else { - typNotNull = true; - nullDefined = true; - } - break; - - case CONSTR_NULL: - if (nullDefined) { - elog(ERROR, "CREATE DOMAIN has conflicting NULL / NOT NULL constraint"); - } else { - typNotNull = false; - nullDefined = true; - } - break; - - case CONSTR_UNIQUE: - elog(ERROR, "CREATE DOMAIN / UNIQUE indexes not supported"); - break; - - case CONSTR_PRIMARY: - elog(ERROR, "CREATE DOMAIN / PRIMARY KEY indexes not supported"); - break; - - case CONSTR_CHECK: - elog(ERROR, "DefineDomain: CHECK Constraints not supported"); - break; - - case CONSTR_ATTR_DEFERRABLE: - case CONSTR_ATTR_NOT_DEFERRABLE: - case CONSTR_ATTR_DEFERRED: - case CONSTR_ATTR_IMMEDIATE: - elog(ERROR, "DefineDomain: DEFERRABLE, NON DEFERRABLE, DEFERRED and IMMEDIATE not supported"); - break; - - default: - elog(ERROR, "DefineDomain: unrecognized constraint node type"); - break; - } - } - - /* - * Have TypeCreate do all the real work. - */ - TypeCreate(domainName, /* type name */ - domainNamespace, /* namespace */ - InvalidOid, /* preassigned type oid (not done here) */ - InvalidOid, /* relation oid (n/a here) */ - internalLength, /* internal size */ - externalLength, /* external size */ - 'd', /* type-type (domain type) */ - delimiter, /* array element delimiter */ - inputProcedure, /* input procedure */ - outputProcedure, /* output procedure */ - receiveProcedure, /* receive procedure */ - sendProcedure, /* send procedure */ - basetypelem, /* element type ID */ - typeTup->t_data->t_oid, /* base type ID */ - defaultValue, /* default type value (text) */ - defaultValueBin, /* default type value (binary) */ - byValue, /* passed by value */ - alignment, /* required alignment */ - storage, /* TOAST strategy */ - stmt->typename->typmod, /* typeMod value */ - typNDims, /* Array dimensions for base type */ - typNotNull); /* Type NOT NULL */ - - /* - * Now we can clean up. - */ - ReleaseSysCache(typeTup); -} - -/* - * DefineType - * Registers a new type. + * Extract a string value (otherwise uninterpreted) from a DefElem. */ -void -DefineType(List *names, List *parameters) -{ - char *typeName; - Oid typeNamespace; - int16 internalLength = -1; /* int2 */ - int16 externalLength = -1; /* int2 */ - Oid elemType = InvalidOid; - List *inputName = NIL; - List *outputName = NIL; - List *sendName = NIL; - List *receiveName = NIL; - char *defaultValue = NULL; - bool byValue = false; - char delimiter = DEFAULT_TYPDELIM; - char alignment = 'i'; /* default alignment */ - char storage = 'p'; /* default TOAST storage method */ - Oid inputOid; - Oid outputOid; - Oid sendOid; - Oid receiveOid; - char *shadow_type; - List *pl; - Oid typoid; - - /* Convert list of names to a name and namespace */ - typeNamespace = QualifiedNameGetCreationNamespace(names, &typeName); - - /* - * Type names must be one character shorter than other names, allowing - * room to create the corresponding array type name with prepended - * "_". - */ - if (strlen(typeName) > (NAMEDATALEN - 2)) - elog(ERROR, "DefineType: type names must be %d characters or less", - NAMEDATALEN - 2); - - foreach(pl, parameters) - { - DefElem *defel = (DefElem *) lfirst(pl); - - if (strcasecmp(defel->defname, "internallength") == 0) - internalLength = defGetTypeLength(defel); - else if (strcasecmp(defel->defname, "externallength") == 0) - externalLength = defGetTypeLength(defel); - else if (strcasecmp(defel->defname, "input") == 0) - inputName = defGetQualifiedName(defel); - else if (strcasecmp(defel->defname, "output") == 0) - outputName = defGetQualifiedName(defel); - else if (strcasecmp(defel->defname, "send") == 0) - sendName = defGetQualifiedName(defel); - else if (strcasecmp(defel->defname, "receive") == 0) - receiveName = defGetQualifiedName(defel); - else if (strcasecmp(defel->defname, "delimiter") == 0) - { - char *p = defGetString(defel); - - delimiter = p[0]; - } - else if (strcasecmp(defel->defname, "element") == 0) - elemType = typenameTypeId(defGetTypeName(defel)); - else if (strcasecmp(defel->defname, "default") == 0) - defaultValue = defGetString(defel); - else if (strcasecmp(defel->defname, "passedbyvalue") == 0) - byValue = true; - else if (strcasecmp(defel->defname, "alignment") == 0) - { - char *a = defGetString(defel); - - /* - * Note: if argument was an unquoted identifier, parser will - * have applied xlateSqlType() to it, so be prepared to - * recognize translated type names as well as the nominal - * form. - */ - if (strcasecmp(a, "double") == 0) - alignment = 'd'; - else if (strcasecmp(a, "float8") == 0) - alignment = 'd'; - else if (strcasecmp(a, "int4") == 0) - alignment = 'i'; - else if (strcasecmp(a, "int2") == 0) - alignment = 's'; - else if (strcasecmp(a, "char") == 0) - alignment = 'c'; - else if (strcasecmp(a, "bpchar") == 0) - alignment = 'c'; - else - elog(ERROR, "DefineType: \"%s\" alignment not recognized", - a); - } - else if (strcasecmp(defel->defname, "storage") == 0) - { - char *a = defGetString(defel); - - if (strcasecmp(a, "plain") == 0) - storage = 'p'; - else if (strcasecmp(a, "external") == 0) - storage = 'e'; - else if (strcasecmp(a, "extended") == 0) - storage = 'x'; - else if (strcasecmp(a, "main") == 0) - storage = 'm'; - else - elog(ERROR, "DefineType: \"%s\" storage not recognized", - a); - } - else - { - elog(WARNING, "DefineType: attribute \"%s\" not recognized", - defel->defname); - } - } - - /* - * make sure we have our required definitions - */ - if (inputName == NIL) - elog(ERROR, "Define: \"input\" unspecified"); - if (outputName == NIL) - elog(ERROR, "Define: \"output\" unspecified"); - - /* Convert I/O proc names to OIDs */ - inputOid = findTypeIOFunction(inputName, false); - outputOid = findTypeIOFunction(outputName, true); - if (sendName) - sendOid = findTypeIOFunction(sendName, true); - else - sendOid = outputOid; - if (receiveName) - receiveOid = findTypeIOFunction(receiveName, false); - else - receiveOid = inputOid; - - /* - * now have TypeCreate do all the real work. - */ - typoid = - TypeCreate(typeName, /* type name */ - typeNamespace, /* namespace */ - InvalidOid, /* preassigned type oid (not done here) */ - InvalidOid, /* relation oid (n/a here) */ - internalLength, /* internal size */ - externalLength, /* external size */ - 'b', /* type-type (base type) */ - delimiter, /* array element delimiter */ - inputOid, /* input procedure */ - outputOid, /* output procedure */ - receiveOid, /* receive procedure */ - sendOid, /* send procedure */ - elemType, /* element type ID */ - InvalidOid, /* base type ID (only for domains) */ - defaultValue, /* default type value */ - NULL, /* no binary form available */ - byValue, /* passed by value */ - alignment, /* required alignment */ - storage, /* TOAST strategy */ - -1, /* typMod (Domains only) */ - 0, /* Array Dimensions of typbasetype */ - false); /* Type NOT NULL */ - - /* - * When we create a base type (as opposed to a complex type) we need - * to have an array entry for it in pg_type as well. - */ - shadow_type = makeArrayTypeName(typeName); - - /* alignment must be 'i' or 'd' for arrays */ - alignment = (alignment == 'd') ? 'd' : 'i'; - - TypeCreate(shadow_type, /* type name */ - typeNamespace, /* namespace */ - InvalidOid, /* preassigned type oid (not done here) */ - InvalidOid, /* relation oid (n/a here) */ - -1, /* internal size */ - -1, /* external size */ - 'b', /* type-type (base type) */ - DEFAULT_TYPDELIM, /* array element delimiter */ - F_ARRAY_IN, /* input procedure */ - F_ARRAY_OUT, /* output procedure */ - F_ARRAY_IN, /* receive procedure */ - F_ARRAY_OUT, /* send procedure */ - typoid, /* element type ID */ - InvalidOid, /* base type ID */ - NULL, /* never a default type value */ - NULL, /* binary default isn't sent either */ - false, /* never passed by value */ - alignment, /* see above */ - 'x', /* ARRAY is always toastable */ - -1, /* typMod (Domains only) */ - 0, /* Array dimensions of typbasetype */ - false); /* Type NOT NULL */ - - pfree(shadow_type); -} - -static Oid -findTypeIOFunction(List *procname, bool isOutput) -{ - Oid argList[FUNC_MAX_ARGS]; - int nargs; - Oid procOid; - - /* - * First look for a 1-argument func with all argtypes 0. This is - * valid for all kinds of procedure. - */ - MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid)); - - procOid = LookupFuncName(procname, 1, argList); - - if (!OidIsValid(procOid)) - { - /* - * Alternatively, input procedures may take 3 args (data - * value, element OID, atttypmod); the pg_proc argtype - * signature is 0,OIDOID,INT4OID. Output procedures may - * take 2 args (data value, element OID). - */ - if (isOutput) - { - /* output proc */ - nargs = 2; - argList[1] = OIDOID; - } - else - { - /* input proc */ - nargs = 3; - argList[1] = OIDOID; - argList[2] = INT4OID; - } - procOid = LookupFuncName(procname, nargs, argList); - - if (!OidIsValid(procOid)) - func_error("TypeCreate", procname, 1, argList, NULL); - } - - return procOid; -} - - -static char * +char * defGetString(DefElem *def) { if (def->arg == NULL) @@ -1133,7 +92,10 @@ defGetString(DefElem *def) return NULL; /* keep compiler quiet */ } -static double +/* + * Extract a numeric value (actually double) from a DefElem. + */ +double defGetNumeric(DefElem *def) { if (def->arg == NULL) @@ -1152,7 +114,10 @@ defGetNumeric(DefElem *def) return 0; /* keep compiler quiet */ } -static List * +/* + * Extract a possibly-qualified name (as a List of Strings) from a DefElem. + */ +List * defGetQualifiedName(DefElem *def) { if (def->arg == NULL) @@ -1172,7 +137,10 @@ defGetQualifiedName(DefElem *def) return NIL; /* keep compiler quiet */ } -static TypeName * +/* + * Extract a TypeName from a DefElem. + */ +TypeName * defGetTypeName(DefElem *def) { if (def->arg == NULL) @@ -1198,7 +166,11 @@ defGetTypeName(DefElem *def) return NULL; /* keep compiler quiet */ } -static int +/* + * Extract a type length indicator (either absolute bytes, or + * -1 for "variable") from a DefElem. + */ +int defGetTypeLength(DefElem *def) { if (def->arg == NULL) diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c new file mode 100644 index 0000000000000000000000000000000000000000..0bee7cdced1c55db71b2a781b347829b51c47db7 --- /dev/null +++ b/src/backend/commands/functioncmds.c @@ -0,0 +1,431 @@ +/*------------------------------------------------------------------------- + * + * functioncmds.c + * + * Routines for CREATE and DROP FUNCTION commands + * + * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/commands/functioncmds.c,v 1.1 2002/04/15 05:22:03 tgl Exp $ + * + * DESCRIPTION + * These routines take the parse tree and pick out the + * appropriate arguments/flags, and pass the results to the + * corresponding "FooDefine" routines (in src/catalog) that do + * the actual catalog-munging. These routines also verify permission + * of the user to execute the command. + * + * NOTES + * These things must be defined and committed in the following order: + * "create function": + * input/output, recv/send procedures + * "create type": + * type + * "create operator": + * operators + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/heapam.h" +#include "catalog/catname.h" +#include "catalog/namespace.h" +#include "catalog/pg_language.h" +#include "catalog/pg_proc.h" +#include "catalog/pg_type.h" +#include "commands/comment.h" +#include "commands/defrem.h" +#include "miscadmin.h" +#include "optimizer/cost.h" +#include "parser/parse_func.h" +#include "parser/parse_type.h" +#include "utils/acl.h" +#include "utils/lsyscache.h" +#include "utils/syscache.h" + + +/* + * Examine the "returns" clause returnType of the CREATE FUNCTION statement + * and return information about it as *prorettype_p and *returnsSet. + * + * This is more complex than the average typename lookup because we want to + * allow a shell type to be used, or even created if the specified return type + * doesn't exist yet. (Without this, there's no way to define the I/O procs + * for a new type.) But SQL function creation won't cope, so error out if + * the target language is SQL. + */ +static void +compute_return_type(TypeName *returnType, Oid languageOid, + Oid *prorettype_p, bool *returnsSet_p) +{ + Oid rettype; + + rettype = LookupTypeName(returnType); + + if (OidIsValid(rettype)) + { + if (!get_typisdefined(rettype)) + { + if (languageOid == SQLlanguageId) + elog(ERROR, "SQL functions cannot return shell types"); + else + elog(WARNING, "Return type \"%s\" is only a shell", + TypeNameToString(returnType)); + } + } + else + { + char *typnam = TypeNameToString(returnType); + + if (strcmp(typnam, "opaque") == 0) + rettype = InvalidOid; + else + { + Oid namespaceId; + char *typname; + + if (languageOid == SQLlanguageId) + elog(ERROR, "Type \"%s\" does not exist", typnam); + elog(WARNING, "ProcedureCreate: type %s is not yet defined", + typnam); + namespaceId = QualifiedNameGetCreationNamespace(returnType->names, + &typname); + rettype = TypeShellMake(typname, namespaceId); + if (!OidIsValid(rettype)) + elog(ERROR, "could not create type %s", typnam); + } + } + + *prorettype_p = rettype; + *returnsSet_p = returnType->setof; +} + +/* + * Interpret the argument-types list of the CREATE FUNCTION statement. + */ +static int +compute_parameter_types(List *argTypes, Oid languageOid, + Oid *parameterTypes) +{ + int parameterCount = 0; + List *x; + + MemSet(parameterTypes, 0, FUNC_MAX_ARGS * sizeof(Oid)); + foreach(x, argTypes) + { + TypeName *t = (TypeName *) lfirst(x); + Oid toid; + + if (parameterCount >= FUNC_MAX_ARGS) + elog(ERROR, "functions cannot have more than %d arguments", + FUNC_MAX_ARGS); + + toid = LookupTypeName(t); + if (OidIsValid(toid)) + { + if (!get_typisdefined(toid)) + elog(WARNING, "Argument type \"%s\" is only a shell", + TypeNameToString(t)); + } + else + { + char *typnam = TypeNameToString(t); + + if (strcmp(typnam, "opaque") == 0) + { + if (languageOid == SQLlanguageId) + elog(ERROR, "SQL functions cannot have arguments of type \"opaque\""); + toid = InvalidOid; + } + else + elog(ERROR, "Type \"%s\" does not exist", typnam); + } + + if (t->setof) + elog(ERROR, "functions cannot accept set arguments"); + + parameterTypes[parameterCount++] = toid; + } + + return parameterCount; +} + +/*------------- + * Interpret the parameters *parameters and return their contents as + * *byte_pct_p, etc. + * + * These parameters supply optional information about a function. + * All have defaults if not specified. + * + * Note: currently, only three of these parameters actually do anything: + * + * * isImplicit means the function may be used as an implicit type + * coercion. + * + * * isStrict means the function should not be called when any NULL + * inputs are present; instead a NULL result value should be assumed. + * + * * volatility tells the optimizer whether the function's result can + * be assumed to be repeatable over multiple evaluations. + * + * The other four parameters are not used anywhere. They used to be + * used in the "expensive functions" optimizer, but that's been dead code + * for a long time. + *------------ + */ +static void +compute_full_attributes(List *parameters, + int32 *byte_pct_p, int32 *perbyte_cpu_p, + int32 *percall_cpu_p, int32 *outin_ratio_p, + bool *isImplicit_p, bool *isStrict_p, + char *volatility_p) +{ + List *pl; + + /* the defaults */ + *byte_pct_p = BYTE_PCT; + *perbyte_cpu_p = PERBYTE_CPU; + *percall_cpu_p = PERCALL_CPU; + *outin_ratio_p = OUTIN_RATIO; + *isImplicit_p = false; + *isStrict_p = false; + *volatility_p = PROVOLATILE_VOLATILE; + + foreach(pl, parameters) + { + DefElem *param = (DefElem *) lfirst(pl); + + if (strcasecmp(param->defname, "implicitcoercion") == 0) + *isImplicit_p = true; + else if (strcasecmp(param->defname, "isstrict") == 0) + *isStrict_p = true; + else if (strcasecmp(param->defname, "isimmutable") == 0) + *volatility_p = PROVOLATILE_IMMUTABLE; + else if (strcasecmp(param->defname, "isstable") == 0) + *volatility_p = PROVOLATILE_STABLE; + else if (strcasecmp(param->defname, "isvolatile") == 0) + *volatility_p = PROVOLATILE_VOLATILE; + else if (strcasecmp(param->defname, "iscachable") == 0) + { + /* obsolete spelling of isImmutable */ + *volatility_p = PROVOLATILE_IMMUTABLE; + } + else if (strcasecmp(param->defname, "trusted") == 0) + { + /* + * we don't have untrusted functions any more. The 4.2 + * implementation is lousy anyway so I took it out. -ay 10/94 + */ + elog(ERROR, "untrusted function has been decommissioned."); + } + else if (strcasecmp(param->defname, "byte_pct") == 0) + *byte_pct_p = (int) defGetNumeric(param); + else if (strcasecmp(param->defname, "perbyte_cpu") == 0) + *perbyte_cpu_p = (int) defGetNumeric(param); + else if (strcasecmp(param->defname, "percall_cpu") == 0) + *percall_cpu_p = (int) defGetNumeric(param); + else if (strcasecmp(param->defname, "outin_ratio") == 0) + *outin_ratio_p = (int) defGetNumeric(param); + else + elog(WARNING, "Unrecognized function attribute '%s' ignored", + param->defname); + } +} + + +/* + * For a dynamically linked C language object, the form of the clause is + * + * AS <object file name> [, <link symbol name> ] + * + * In all other cases + * + * AS <object reference, or sql code> + * + */ + +static void +interpret_AS_clause(Oid languageOid, const char *languageName, const List *as, + char **prosrc_str_p, char **probin_str_p) +{ + Assert(as != NIL); + + if (languageOid == ClanguageId) + { + /* + * For "C" language, store the file name in probin and, when + * given, the link symbol name in prosrc. + */ + *probin_str_p = strVal(lfirst(as)); + if (lnext(as) == NULL) + *prosrc_str_p = "-"; + else + *prosrc_str_p = strVal(lsecond(as)); + } + else + { + /* Everything else wants the given string in prosrc. */ + *prosrc_str_p = strVal(lfirst(as)); + *probin_str_p = "-"; + + if (lnext(as) != NIL) + elog(ERROR, "CREATE FUNCTION: only one AS item needed for %s language", + languageName); + } +} + + + +/* + * CreateFunction + * Execute a CREATE FUNCTION utility statement. + */ +void +CreateFunction(ProcedureStmt *stmt) +{ + char *probin_str; + char *prosrc_str; + Oid prorettype; + bool returnsSet; + char languageName[NAMEDATALEN]; + Oid languageOid; + char *funcname; + Oid namespaceId; + int parameterCount; + Oid parameterTypes[FUNC_MAX_ARGS]; + int32 byte_pct, + perbyte_cpu, + percall_cpu, + outin_ratio; + bool isImplicit, + isStrict; + char volatility; + HeapTuple languageTuple; + Form_pg_language languageStruct; + + /* Convert list of names to a name and namespace */ + namespaceId = QualifiedNameGetCreationNamespace(stmt->funcname, + &funcname); + + /* Convert language name to canonical case */ + case_translate_language_name(stmt->language, languageName); + + /* Look up the language and validate permissions */ + languageTuple = SearchSysCache(LANGNAME, + PointerGetDatum(languageName), + 0, 0, 0); + if (!HeapTupleIsValid(languageTuple)) + elog(ERROR, "language \"%s\" does not exist", languageName); + + languageOid = languageTuple->t_data->t_oid; + languageStruct = (Form_pg_language) GETSTRUCT(languageTuple); + + if (!((languageStruct->lanpltrusted + && pg_language_aclcheck(languageOid, GetUserId()) == ACLCHECK_OK) + || superuser())) + elog(ERROR, "permission denied"); + + ReleaseSysCache(languageTuple); + + /* + * Convert remaining parameters of CREATE to form wanted by + * ProcedureCreate. + */ + compute_return_type(stmt->returnType, languageOid, + &prorettype, &returnsSet); + + parameterCount = compute_parameter_types(stmt->argTypes, languageOid, + parameterTypes); + + compute_full_attributes(stmt->withClause, + &byte_pct, &perbyte_cpu, &percall_cpu, + &outin_ratio, &isImplicit, &isStrict, + &volatility); + + interpret_AS_clause(languageOid, languageName, stmt->as, + &prosrc_str, &probin_str); + + /* + * And now that we have all the parameters, and know we're permitted + * to do so, go ahead and create the function. + */ + ProcedureCreate(funcname, + namespaceId, + stmt->replace, + returnsSet, + prorettype, + languageOid, + prosrc_str, /* converted to text later */ + probin_str, /* converted to text later */ + false, /* not an aggregate */ + true, /* (obsolete "trusted") */ + isImplicit, + isStrict, + volatility, + byte_pct, + perbyte_cpu, + percall_cpu, + outin_ratio, + parameterCount, + parameterTypes); +} + + +/* + * RemoveFunction + * Deletes a function. + * + * Exceptions: + * BadArg if name is invalid. + * "ERROR" if function nonexistent. + * ... + */ +void +RemoveFunction(List *functionName, /* function name to be removed */ + List *argTypes) /* list of TypeName nodes */ +{ + Oid funcOid; + Relation relation; + HeapTuple tup; + + funcOid = LookupFuncNameTypeNames(functionName, argTypes, + true, "RemoveFunction"); + + relation = heap_openr(ProcedureRelationName, RowExclusiveLock); + + tup = SearchSysCache(PROCOID, + ObjectIdGetDatum(funcOid), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) /* should not happen */ + elog(ERROR, "RemoveFunction: couldn't find tuple for function %s", + NameListToString(functionName)); + + if (!pg_proc_ownercheck(funcOid, GetUserId())) + elog(ERROR, "RemoveFunction: function '%s': permission denied", + NameListToString(functionName)); + + if (((Form_pg_proc) GETSTRUCT(tup))->proisagg) + elog(ERROR, "RemoveFunction: function '%s' is an aggregate" + "\n\tUse DROP AGGREGATE to remove it", + NameListToString(functionName)); + + if (((Form_pg_proc) GETSTRUCT(tup))->prolang == INTERNALlanguageId) + { + /* "Helpful" WARNING when removing a builtin function ... */ + elog(WARNING, "Removing built-in function \"%s\"", + NameListToString(functionName)); + } + + /* Delete any comments associated with this function */ + DeleteComments(funcOid, RelationGetRelid(relation)); + + simple_heap_delete(relation, &tup->t_self); + + ReleaseSysCache(tup); + + heap_close(relation, RowExclusiveLock); +} diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c new file mode 100644 index 0000000000000000000000000000000000000000..c91669572ef4a989904cf195bca1e87a140f0f8a --- /dev/null +++ b/src/backend/commands/lockcmds.c @@ -0,0 +1,69 @@ +/*------------------------------------------------------------------------- + * + * lockcmds.c + * Lock command support code + * + * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/commands/lockcmds.c,v 1.1 2002/04/15 05:22:03 tgl Exp $ + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/heapam.h" +#include "catalog/namespace.h" +#include "commands/lockcmds.h" +#include "miscadmin.h" +#include "utils/acl.h" + + +/* + * LOCK TABLE + */ +void +LockTableCommand(LockStmt *lockstmt) +{ + List *p; + + /* + * Iterate over the list and open, lock, and close the relations one + * at a time + */ + + foreach(p, lockstmt->relations) + { + RangeVar *relation = lfirst(p); + Oid reloid; + int32 aclresult; + Relation rel; + + /* + * We don't want to open the relation until we've checked privilege. + * So, manually get the relation OID. + */ + reloid = RangeVarGetRelid(relation, false); + + if (lockstmt->mode == AccessShareLock) + aclresult = pg_class_aclcheck(reloid, GetUserId(), + ACL_SELECT); + else + aclresult = pg_class_aclcheck(reloid, GetUserId(), + ACL_UPDATE | ACL_DELETE); + + if (aclresult != ACLCHECK_OK) + elog(ERROR, "LOCK TABLE: permission denied"); + + rel = relation_open(reloid, lockstmt->mode); + + /* Currently, we only allow plain tables to be locked */ + if (rel->rd_rel->relkind != RELKIND_RELATION) + elog(ERROR, "LOCK TABLE: %s is not a table", + relation->relname); + + relation_close(rel, NoLock); /* close rel, keep lock */ + } +} diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c new file mode 100644 index 0000000000000000000000000000000000000000..54f48928b41e631726d9868411dae3aa13d14fd2 --- /dev/null +++ b/src/backend/commands/operatorcmds.c @@ -0,0 +1,247 @@ +/*------------------------------------------------------------------------- + * + * operatorcmds.c + * + * Routines for operator manipulation commands + * + * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/commands/operatorcmds.c,v 1.1 2002/04/15 05:22:03 tgl Exp $ + * + * DESCRIPTION + * The "DefineFoo" routines take the parse tree and pick out the + * appropriate arguments/flags, passing the results to the + * corresponding "FooDefine" routines (in src/catalog) that do + * the actual catalog-munging. These routines also verify permission + * of the user to execute the command. + * + * NOTES + * These things must be defined and committed in the following order: + * "create function": + * input/output, recv/send procedures + * "create type": + * type + * "create operator": + * operators + * + * Most of the parse-tree manipulation routines are defined in + * commands/manip.c. + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/heapam.h" +#include "catalog/catname.h" +#include "catalog/namespace.h" +#include "catalog/pg_operator.h" +#include "commands/comment.h" +#include "commands/defrem.h" +#include "miscadmin.h" +#include "parser/parse_type.h" +#include "utils/acl.h" +#include "utils/syscache.h" + + +/* + * DefineOperator + * this function extracts all the information from the + * parameter list generated by the parser and then has + * OperatorCreate() do all the actual work. + * + * 'parameters' is a list of DefElem + */ +void +DefineOperator(List *names, List *parameters) +{ + char *oprName; + Oid oprNamespace; + uint16 precedence = 0; /* operator precedence */ + bool canHash = false; /* operator hashes */ + bool isLeftAssociative = true; /* operator is left + * associative */ + char *functionName = NULL; /* function for operator */ + TypeName *typeName1 = NULL; /* first type name */ + TypeName *typeName2 = NULL; /* second type name */ + Oid typeId1 = InvalidOid; /* types converted to OID */ + Oid typeId2 = InvalidOid; + char *commutatorName = NULL; /* optional commutator operator + * name */ + char *negatorName = NULL; /* optional negator operator name */ + char *restrictionName = NULL; /* optional restrict. sel. + * procedure */ + char *joinName = NULL; /* optional join sel. procedure name */ + char *sortName1 = NULL; /* optional first sort operator */ + char *sortName2 = NULL; /* optional second sort operator */ + List *pl; + + /* Convert list of names to a name and namespace */ + oprNamespace = QualifiedNameGetCreationNamespace(names, &oprName); + + /* + * loop over the definition list and extract the information we need. + */ + foreach(pl, parameters) + { + DefElem *defel = (DefElem *) lfirst(pl); + + if (strcasecmp(defel->defname, "leftarg") == 0) + { + typeName1 = defGetTypeName(defel); + if (typeName1->setof) + elog(ERROR, "setof type not implemented for leftarg"); + } + else if (strcasecmp(defel->defname, "rightarg") == 0) + { + typeName2 = defGetTypeName(defel); + if (typeName2->setof) + elog(ERROR, "setof type not implemented for rightarg"); + } + else if (strcasecmp(defel->defname, "procedure") == 0) + functionName = defGetString(defel); + else if (strcasecmp(defel->defname, "precedence") == 0) + { + /* NOT IMPLEMENTED (never worked in v4.2) */ + elog(NOTICE, "CREATE OPERATOR: precedence not implemented"); + } + else if (strcasecmp(defel->defname, "associativity") == 0) + { + /* NOT IMPLEMENTED (never worked in v4.2) */ + elog(NOTICE, "CREATE OPERATOR: associativity not implemented"); + } + else if (strcasecmp(defel->defname, "commutator") == 0) + commutatorName = defGetString(defel); + else if (strcasecmp(defel->defname, "negator") == 0) + negatorName = defGetString(defel); + else if (strcasecmp(defel->defname, "restrict") == 0) + restrictionName = defGetString(defel); + else if (strcasecmp(defel->defname, "join") == 0) + joinName = defGetString(defel); + else if (strcasecmp(defel->defname, "hashes") == 0) + canHash = TRUE; + else if (strcasecmp(defel->defname, "sort1") == 0) + sortName1 = defGetString(defel); + else if (strcasecmp(defel->defname, "sort2") == 0) + sortName2 = defGetString(defel); + else + { + elog(WARNING, "DefineOperator: attribute \"%s\" not recognized", + defel->defname); + } + } + + /* + * make sure we have our required definitions + */ + if (functionName == NULL) + elog(ERROR, "Define: \"procedure\" unspecified"); + + /* Transform type names to type OIDs */ + if (typeName1) + typeId1 = typenameTypeId(typeName1); + if (typeName2) + typeId2 = typenameTypeId(typeName2); + + /* + * now have OperatorCreate do all the work.. + */ + OperatorCreate(oprName, /* operator name */ + typeId1, /* left type id */ + typeId2, /* right type id */ + functionName, /* function for operator */ + precedence, /* operator precedence */ + isLeftAssociative, /* operator is left associative */ + commutatorName, /* optional commutator operator + * name */ + negatorName, /* optional negator operator name */ + restrictionName, /* optional restrict. sel. + * procedure */ + joinName, /* optional join sel. procedure name */ + canHash, /* operator hashes */ + sortName1, /* optional first sort operator */ + sortName2); /* optional second sort operator */ + +} + + +/* + * RemoveOperator + * Deletes an operator. + * + * Exceptions: + * BadArg if name is invalid. + * BadArg if type1 is invalid. + * "ERROR" if operator nonexistent. + * ... + */ +void +RemoveOperator(char *operatorName, /* operator name */ + TypeName *typeName1, /* left argument type name */ + TypeName *typeName2) /* right argument type name */ +{ + Relation relation; + HeapTuple tup; + Oid typeId1 = InvalidOid; + Oid typeId2 = InvalidOid; + char oprtype; + + if (typeName1) + typeId1 = typenameTypeId(typeName1); + + if (typeName2) + typeId2 = typenameTypeId(typeName2); + + if (OidIsValid(typeId1) && OidIsValid(typeId2)) + oprtype = 'b'; + else if (OidIsValid(typeId1)) + oprtype = 'r'; + else + oprtype = 'l'; + + relation = heap_openr(OperatorRelationName, RowExclusiveLock); + + tup = SearchSysCacheCopy(OPERNAME, + PointerGetDatum(operatorName), + ObjectIdGetDatum(typeId1), + ObjectIdGetDatum(typeId2), + CharGetDatum(oprtype)); + + if (HeapTupleIsValid(tup)) + { + if (!pg_oper_ownercheck(tup->t_data->t_oid, GetUserId())) + elog(ERROR, "RemoveOperator: operator '%s': permission denied", + operatorName); + + /* Delete any comments associated with this operator */ + DeleteComments(tup->t_data->t_oid, RelationGetRelid(relation)); + + simple_heap_delete(relation, &tup->t_self); + } + else + { + if (OidIsValid(typeId1) && OidIsValid(typeId2)) + { + elog(ERROR, "RemoveOperator: binary operator '%s' taking '%s' and '%s' does not exist", + operatorName, + TypeNameToString(typeName1), + TypeNameToString(typeName2)); + } + else if (OidIsValid(typeId1)) + { + elog(ERROR, "RemoveOperator: right unary operator '%s' taking '%s' does not exist", + operatorName, + TypeNameToString(typeName1)); + } + else + { + elog(ERROR, "RemoveOperator: left unary operator '%s' taking '%s' does not exist", + operatorName, + TypeNameToString(typeName2)); + } + } + heap_freetuple(tup); + heap_close(relation, RowExclusiveLock); +} diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c new file mode 100644 index 0000000000000000000000000000000000000000..6f690c0927ce5357fff37439f83f6aaa081c72b8 --- /dev/null +++ b/src/backend/commands/portalcmds.c @@ -0,0 +1,234 @@ +/*------------------------------------------------------------------------- + * + * portalcmds.c + * portal support code + * + * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.1 2002/04/15 05:22:03 tgl Exp $ + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "commands/portalcmds.h" +#include "executor/executor.h" + + +/* + * PortalCleanup + */ +void +PortalCleanup(Portal portal) +{ + MemoryContext oldcontext; + + /* + * sanity checks + */ + AssertArg(PortalIsValid(portal)); + AssertArg(portal->cleanup == PortalCleanup); + + /* + * set proper portal-executor context before calling ExecMain. + */ + oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); + + /* + * tell the executor to shutdown the query + */ + ExecutorEnd(PortalGetQueryDesc(portal), PortalGetState(portal)); + + /* + * switch back to previous context + */ + MemoryContextSwitchTo(oldcontext); +} + + +/* + * PerformPortalFetch + * + * name: name of portal + * forward: forward or backward fetch? + * count: # of tuples to fetch (0 implies all) + * dest: where to send results + * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE + * in which to store a command completion status string. + * + * completionTag may be NULL if caller doesn't want a status string. + */ +void +PerformPortalFetch(char *name, + bool forward, + int count, + CommandDest dest, + char *completionTag) +{ + Portal portal; + QueryDesc *queryDesc; + EState *estate; + MemoryContext oldcontext; + ScanDirection direction; + CommandId savedId; + bool temp_desc = false; + + /* initialize completion status in case of early exit */ + if (completionTag) + strcpy(completionTag, (dest == None) ? "MOVE 0" : "FETCH 0"); + + /* + * sanity checks + */ + if (name == NULL) + { + elog(WARNING, "PerformPortalFetch: missing portal name"); + return; + } + + /* + * get the portal from the portal name + */ + portal = GetPortalByName(name); + if (!PortalIsValid(portal)) + { + elog(WARNING, "PerformPortalFetch: portal \"%s\" not found", + name); + return; + } + + /* + * switch into the portal context + */ + oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); + + queryDesc = PortalGetQueryDesc(portal); + estate = PortalGetState(portal); + + /* + * If the requested destination is not the same as the query's + * original destination, make a temporary QueryDesc with the proper + * destination. This supports MOVE, for example, which will pass in + * dest = None. + * + * EXCEPTION: if the query's original dest is RemoteInternal (ie, it's a + * binary cursor) and the request is Remote, we do NOT override the + * original dest. This is necessary since a FETCH command will pass + * dest = Remote, not knowing whether the cursor is binary or not. + */ + if (dest != queryDesc->dest && + !(queryDesc->dest == RemoteInternal && dest == Remote)) + { + QueryDesc *qdesc = (QueryDesc *) palloc(sizeof(QueryDesc)); + + memcpy(qdesc, queryDesc, sizeof(QueryDesc)); + qdesc->dest = dest; + queryDesc = qdesc; + temp_desc = true; + } + + /* + * Restore the scanCommandId that was current when the cursor was + * opened. This ensures that we see the same tuples throughout the + * execution of the cursor. + */ + savedId = GetScanCommandId(); + SetScanCommandId(PortalGetCommandId(portal)); + + /* + * Determine which direction to go in, and check to see if we're + * already at the end of the available tuples in that direction. If + * so, set the direction to NoMovement to avoid trying to fetch any + * tuples. (This check exists because not all plan node types + * are robust about being called again if they've already returned + * NULL once.) Then call the executor (we must not skip this, because + * the destination needs to see a setup and shutdown even if no tuples + * are available). Finally, update the atStart/atEnd state depending + * on the number of tuples that were retrieved. + */ + if (forward) + { + if (portal->atEnd) + direction = NoMovementScanDirection; + else + direction = ForwardScanDirection; + + ExecutorRun(queryDesc, estate, direction, (long) count); + + if (estate->es_processed > 0) + portal->atStart = false; /* OK to back up now */ + if (count <= 0 || (int) estate->es_processed < count) + portal->atEnd = true; /* we retrieved 'em all */ + } + else + { + if (portal->atStart) + direction = NoMovementScanDirection; + else + direction = BackwardScanDirection; + + ExecutorRun(queryDesc, estate, direction, (long) count); + + if (estate->es_processed > 0) + portal->atEnd = false; /* OK to go forward now */ + if (count <= 0 || (int) estate->es_processed < count) + portal->atStart = true; /* we retrieved 'em all */ + } + + /* Return command status if wanted */ + if (completionTag) + snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %u", + (dest == None) ? "MOVE" : "FETCH", + estate->es_processed); + + /* + * Restore outer command ID. + */ + SetScanCommandId(savedId); + + /* + * Clean up and switch back to old context. + */ + if (temp_desc) + pfree(queryDesc); + + MemoryContextSwitchTo(oldcontext); +} + +/* + * PerformPortalClose + */ +void +PerformPortalClose(char *name, CommandDest dest) +{ + Portal portal; + + /* + * sanity checks + */ + if (name == NULL) + { + elog(WARNING, "PerformPortalClose: missing portal name"); + return; + } + + /* + * get the portal from the portal name + */ + portal = GetPortalByName(name); + if (!PortalIsValid(portal)) + { + elog(WARNING, "PerformPortalClose: portal \"%s\" not found", + name); + return; + } + + /* + * Note: PortalCleanup is called as a side-effect + */ + PortalDrop(portal); +} diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c index 4ef8d8f72a0bee3b3a69cab019cd467181aa6bd2..2ad25fdbd4f2cf977f9cd78175e99cd70b7a40b9 100644 --- a/src/backend/commands/proclang.c +++ b/src/backend/commands/proclang.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/proclang.c,v 1.30 2002/04/09 20:35:48 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/proclang.c,v 1.31 2002/04/15 05:22:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -22,6 +22,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_proc.h" #include "commands/proclang.h" +#include "commands/defrem.h" #include "fmgr.h" #include "miscadmin.h" #include "parser/parse_func.h" @@ -30,21 +31,6 @@ #include "utils/syscache.h" -/* - * Translate the input language name to lower case. - */ -static void -case_translate_language_name(const char *input, char *output) -{ - int i; - - for (i = 0; i < NAMEDATALEN && input[i]; ++i) - output[i] = tolower((unsigned char) input[i]); - - output[i] = '\0'; -} - - /* --------------------------------------------------------------------- * CREATE PROCEDURAL LANGUAGE * --------------------------------------------------------------------- diff --git a/src/backend/commands/remove.c b/src/backend/commands/remove.c deleted file mode 100644 index c32d2b215c5163f64990d0fdb6f0436b1341114c..0000000000000000000000000000000000000000 --- a/src/backend/commands/remove.c +++ /dev/null @@ -1,476 +0,0 @@ -/*------------------------------------------------------------------------- - * - * remove.c - * POSTGRES remove (domain | function | type | operator ) utilty code. - * - * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/Attic/remove.c,v 1.74 2002/04/11 19:59:58 tgl Exp $ - * - *------------------------------------------------------------------------- - */ -#include "postgres.h" - -#include "access/heapam.h" -#include "catalog/catname.h" -#include "catalog/namespace.h" -#include "catalog/pg_language.h" -#include "catalog/pg_proc.h" -#include "catalog/pg_type.h" -#include "commands/comment.h" -#include "commands/defrem.h" -#include "miscadmin.h" -#include "parser/parse.h" -#include "parser/parse_func.h" -#include "parser/parse_type.h" -#include "utils/acl.h" -#include "utils/builtins.h" -#include "utils/syscache.h" - - -/* - * RemoveOperator - * Deletes an operator. - * - * Exceptions: - * BadArg if name is invalid. - * BadArg if type1 is invalid. - * "ERROR" if operator nonexistent. - * ... - */ -void -RemoveOperator(char *operatorName, /* operator name */ - TypeName *typeName1, /* left argument type name */ - TypeName *typeName2) /* right argument type name */ -{ - Relation relation; - HeapTuple tup; - Oid typeId1 = InvalidOid; - Oid typeId2 = InvalidOid; - char oprtype; - - if (typeName1) - typeId1 = typenameTypeId(typeName1); - - if (typeName2) - typeId2 = typenameTypeId(typeName2); - - if (OidIsValid(typeId1) && OidIsValid(typeId2)) - oprtype = 'b'; - else if (OidIsValid(typeId1)) - oprtype = 'r'; - else - oprtype = 'l'; - - relation = heap_openr(OperatorRelationName, RowExclusiveLock); - - tup = SearchSysCacheCopy(OPERNAME, - PointerGetDatum(operatorName), - ObjectIdGetDatum(typeId1), - ObjectIdGetDatum(typeId2), - CharGetDatum(oprtype)); - - if (HeapTupleIsValid(tup)) - { - if (!pg_oper_ownercheck(tup->t_data->t_oid, GetUserId())) - elog(ERROR, "RemoveOperator: operator '%s': permission denied", - operatorName); - - /* Delete any comments associated with this operator */ - DeleteComments(tup->t_data->t_oid, RelationGetRelid(relation)); - - simple_heap_delete(relation, &tup->t_self); - } - else - { - if (OidIsValid(typeId1) && OidIsValid(typeId2)) - { - elog(ERROR, "RemoveOperator: binary operator '%s' taking '%s' and '%s' does not exist", - operatorName, - TypeNameToString(typeName1), - TypeNameToString(typeName2)); - } - else if (OidIsValid(typeId1)) - { - elog(ERROR, "RemoveOperator: right unary operator '%s' taking '%s' does not exist", - operatorName, - TypeNameToString(typeName1)); - } - else - { - elog(ERROR, "RemoveOperator: left unary operator '%s' taking '%s' does not exist", - operatorName, - TypeNameToString(typeName2)); - } - } - heap_freetuple(tup); - heap_close(relation, RowExclusiveLock); -} - -#ifdef NOTYET -/* - * this stuff is to support removing all reference to a type - * don't use it - pma 2/1/94 - */ -/* - * SingleOpOperatorRemove - * Removes all operators that have operands or a result of type 'typeOid'. - */ -static void -SingleOpOperatorRemove(Oid typeOid) -{ - Relation rel; - ScanKeyData key[3]; - HeapScanDesc scan; - HeapTuple tup; - static attnums[3] = {7, 8, 9}; /* left, right, return */ - int i; - - ScanKeyEntryInitialize(&key[0], - 0, 0, F_OIDEQ, (Datum) typeOid); - rel = heap_openr(OperatorRelationName, RowExclusiveLock); - for (i = 0; i < 3; ++i) - { - key[0].sk_attno = attnums[i]; - scan = heap_beginscan(rel, 0, SnapshotNow, 1, key); - while (HeapTupleIsValid(tup = heap_getnext(scan, 0))) - { - /* Delete any comments associated with this operator */ - DeleteComments(tup->t_data->t_oid, RelationGetRelid(rel)); - - simple_heap_delete(rel, &tup->t_self); - } - - heap_endscan(scan); - } - heap_close(rel, RowExclusiveLock); -} - -/* - * AttributeAndRelationRemove - * Removes all entries in the attribute and relation relations - * that contain entries of type 'typeOid'. - * Currently nothing calls this code, it is untested. - */ -static void -AttributeAndRelationRemove(Oid typeOid) -{ - struct oidlist - { - Oid reloid; - struct oidlist *next; - }; - struct oidlist *oidptr, - *optr; - Relation rel; - ScanKeyData key[1]; - HeapScanDesc scan; - HeapTuple tup; - - /* - * Get the oid's of the relations to be removed by scanning the entire - * attribute relation. We don't need to remove the attributes here, - * because amdestroy will remove all attributes of the relation. XXX - * should check for duplicate relations - */ - - ScanKeyEntryInitialize(&key[0], - 0, 3, F_OIDEQ, (Datum) typeOid); - - oidptr = (struct oidlist *) palloc(sizeof(*oidptr)); - oidptr->next = NULL; - optr = oidptr; - rel = heap_openr(AttributeRelationName, AccessShareLock); - scan = heap_beginscan(rel, 0, SnapshotNow, 1, key); - while (HeapTupleIsValid(tup = heap_getnext(scan, 0))) - { - optr->reloid = ((Form_pg_attribute) GETSTRUCT(tup))->attrelid; - optr->next = (struct oidlist *) palloc(sizeof(*oidptr)); - optr = optr->next; - } - optr->next = NULL; - heap_endscan(scan); - heap_close(rel, AccessShareLock); - - optr = oidptr; - - ScanKeyEntryInitialize(&key[0], 0, - ObjectIdAttributeNumber, - F_OIDEQ, (Datum) 0); - /* get RowExclusiveLock because heap_destroy will need it */ - rel = heap_openr(RelationRelationName, RowExclusiveLock); - while (PointerIsValid((char *) optr->next)) - { - Oid relOid = (optr++)->reloid; - - key[0].sk_argument = ObjectIdGetDatum(relOid); - scan = heap_beginscan(rel, 0, SnapshotNow, 1, key); - tup = heap_getnext(scan, 0); - if (HeapTupleIsValid(tup)) - heap_drop_with_catalog(relOid, allowSystemTableMods); - heap_endscan(scan); - } - heap_close(rel, RowExclusiveLock); -} -#endif /* NOTYET */ - -/* - * TypeRemove - * Removes a datatype. - * - * NOTE: since this tries to remove the associated array type too, it'll - * only work on scalar types. - */ -void -RemoveType(List *names) -{ - TypeName *typename; - Relation relation; - Oid typeoid; - HeapTuple tup; - - /* Make a TypeName so we can use standard type lookup machinery */ - typename = makeNode(TypeName); - typename->names = names; - typename->typmod = -1; - typename->arrayBounds = NIL; - - relation = heap_openr(TypeRelationName, RowExclusiveLock); - - /* Use LookupTypeName here so that shell types can be removed. */ - typeoid = LookupTypeName(typename); - if (!OidIsValid(typeoid)) - elog(ERROR, "Type \"%s\" does not exist", - TypeNameToString(typename)); - - tup = SearchSysCache(TYPEOID, - ObjectIdGetDatum(typeoid), - 0, 0, 0); - if (!HeapTupleIsValid(tup)) - elog(ERROR, "Type \"%s\" does not exist", - TypeNameToString(typename)); - - if (!pg_type_ownercheck(typeoid, GetUserId())) - elog(ERROR, "RemoveType: type '%s': permission denied", - TypeNameToString(typename)); - - /* Delete any comments associated with this type */ - DeleteComments(typeoid, RelationGetRelid(relation)); - - /* Remove the type tuple from pg_type */ - simple_heap_delete(relation, &tup->t_self); - - ReleaseSysCache(tup); - - /* Now, delete the "array of" that type */ - typename->arrayBounds = makeList1(makeInteger(1)); - - typeoid = LookupTypeName(typename); - if (!OidIsValid(typeoid)) - elog(ERROR, "Type \"%s\" does not exist", - TypeNameToString(typename)); - - tup = SearchSysCache(TYPEOID, - ObjectIdGetDatum(typeoid), - 0, 0, 0); - if (!HeapTupleIsValid(tup)) - elog(ERROR, "Type \"%s\" does not exist", - TypeNameToString(typename)); - - DeleteComments(typeoid, RelationGetRelid(relation)); - - simple_heap_delete(relation, &tup->t_self); - - ReleaseSysCache(tup); - - heap_close(relation, RowExclusiveLock); -} - -/* - * RemoveDomain - * Removes a domain. - */ -void -RemoveDomain(List *names, int behavior) -{ - TypeName *typename; - Relation relation; - Oid typeoid; - HeapTuple tup; - char typtype; - - /* CASCADE unsupported */ - if (behavior == CASCADE) - elog(ERROR, "DROP DOMAIN does not support the CASCADE keyword"); - - /* Make a TypeName so we can use standard type lookup machinery */ - typename = makeNode(TypeName); - typename->names = names; - typename->typmod = -1; - typename->arrayBounds = NIL; - - relation = heap_openr(TypeRelationName, RowExclusiveLock); - - typeoid = typenameTypeId(typename); - - tup = SearchSysCache(TYPEOID, - ObjectIdGetDatum(typeoid), - 0, 0, 0); - if (!HeapTupleIsValid(tup)) - elog(ERROR, "RemoveDomain: type '%s' does not exist", - TypeNameToString(typename)); - - if (!pg_type_ownercheck(typeoid, GetUserId())) - elog(ERROR, "RemoveDomain: type '%s': permission denied", - TypeNameToString(typename)); - - /* Check that this is actually a domain */ - typtype = ((Form_pg_type) GETSTRUCT(tup))->typtype; - - if (typtype != 'd') - elog(ERROR, "%s is not a domain", - TypeNameToString(typename)); - - /* Delete any comments associated with this type */ - DeleteComments(typeoid, RelationGetRelid(relation)); - - /* Remove the type tuple from pg_type */ - simple_heap_delete(relation, &tup->t_self); - - ReleaseSysCache(tup); - - /* At present, domains don't have associated array types */ - - heap_close(relation, RowExclusiveLock); -} - -/* - * RemoveFunction - * Deletes a function. - * - * Exceptions: - * BadArg if name is invalid. - * "ERROR" if function nonexistent. - * ... - */ -void -RemoveFunction(List *functionName, /* function name to be removed */ - List *argTypes) /* list of TypeName nodes */ -{ - Oid funcOid; - Relation relation; - HeapTuple tup; - - funcOid = LookupFuncNameTypeNames(functionName, argTypes, - true, "RemoveFunction"); - - relation = heap_openr(ProcedureRelationName, RowExclusiveLock); - - tup = SearchSysCache(PROCOID, - ObjectIdGetDatum(funcOid), - 0, 0, 0); - if (!HeapTupleIsValid(tup)) /* should not happen */ - elog(ERROR, "RemoveFunction: couldn't find tuple for function %s", - NameListToString(functionName)); - - if (!pg_proc_ownercheck(funcOid, GetUserId())) - elog(ERROR, "RemoveFunction: function '%s': permission denied", - NameListToString(functionName)); - - if (((Form_pg_proc) GETSTRUCT(tup))->proisagg) - elog(ERROR, "RemoveFunction: function '%s' is an aggregate" - "\n\tUse DROP AGGREGATE to remove it", - NameListToString(functionName)); - - if (((Form_pg_proc) GETSTRUCT(tup))->prolang == INTERNALlanguageId) - { - /* "Helpful" WARNING when removing a builtin function ... */ - elog(WARNING, "Removing built-in function \"%s\"", - NameListToString(functionName)); - } - - /* Delete any comments associated with this function */ - DeleteComments(funcOid, RelationGetRelid(relation)); - - simple_heap_delete(relation, &tup->t_self); - - ReleaseSysCache(tup); - - heap_close(relation, RowExclusiveLock); -} - -void -RemoveAggregate(List *aggName, TypeName *aggType) -{ - Relation relation; - HeapTuple tup; - Oid basetypeID; - Oid procOid; - - /* - * if a basetype is passed in, then attempt to find an aggregate for - * that specific type. - * - * else if the basetype is blank, then attempt to find an aggregate with - * a basetype of zero. This is valid. It means that the aggregate is - * to apply to all basetypes (eg, COUNT). - */ - if (aggType) - basetypeID = typenameTypeId(aggType); - else - basetypeID = InvalidOid; - - procOid = find_aggregate_func("RemoveAggregate", aggName, basetypeID); - - /* Permission check */ - - if (!pg_proc_ownercheck(procOid, GetUserId())) - { - if (basetypeID == InvalidOid) - elog(ERROR, "RemoveAggregate: aggregate %s for all types: permission denied", - NameListToString(aggName)); - else - elog(ERROR, "RemoveAggregate: aggregate %s for type %s: permission denied", - NameListToString(aggName), format_type_be(basetypeID)); - } - - /* Remove the pg_proc tuple */ - - relation = heap_openr(ProcedureRelationName, RowExclusiveLock); - - tup = SearchSysCache(PROCOID, - ObjectIdGetDatum(procOid), - 0, 0, 0); - if (!HeapTupleIsValid(tup)) /* should not happen */ - elog(ERROR, "RemoveAggregate: couldn't find pg_proc tuple for %s", - NameListToString(aggName)); - - /* Delete any comments associated with this function */ - DeleteComments(procOid, RelationGetRelid(relation)); - - simple_heap_delete(relation, &tup->t_self); - - ReleaseSysCache(tup); - - heap_close(relation, RowExclusiveLock); - - /* Remove the pg_aggregate tuple */ - - relation = heap_openr(AggregateRelationName, RowExclusiveLock); - - tup = SearchSysCache(AGGFNOID, - ObjectIdGetDatum(procOid), - 0, 0, 0); - if (!HeapTupleIsValid(tup)) /* should not happen */ - elog(ERROR, "RemoveAggregate: couldn't find pg_aggregate tuple for %s", - NameListToString(aggName)); - - simple_heap_delete(relation, &tup->t_self); - - ReleaseSysCache(tup); - - heap_close(relation, RowExclusiveLock); -} diff --git a/src/backend/commands/rename.c b/src/backend/commands/rename.c deleted file mode 100644 index 21db59b7f6b0102ba443f23279440b11d302fa9d..0000000000000000000000000000000000000000 --- a/src/backend/commands/rename.c +++ /dev/null @@ -1,591 +0,0 @@ -/*------------------------------------------------------------------------- - * - * rename.c - * renameatt() and renamerel() reside here. - * - * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/Attic/rename.c,v 1.70 2002/04/12 20:38:24 tgl Exp $ - * - *------------------------------------------------------------------------- - */ -#include "postgres.h" - -#include <errno.h> - -#include "access/genam.h" -#include "access/heapam.h" -#include "access/itup.h" -#include "catalog/catname.h" -#include "catalog/pg_index.h" -#include "catalog/pg_trigger.h" -#include "catalog/pg_type.h" -#include "catalog/heap.h" -#include "catalog/indexing.h" -#include "catalog/catalog.h" -#include "commands/rename.h" -#include "commands/trigger.h" -#include "miscadmin.h" -#include "storage/smgr.h" -#include "optimizer/prep.h" -#include "rewrite/rewriteDefine.h" -#include "rewrite/rewriteSupport.h" -#include "utils/acl.h" -#include "utils/builtins.h" -#include "utils/fmgroids.h" -#include "utils/lsyscache.h" -#include "utils/relcache.h" -#include "utils/syscache.h" - - -#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); - - -/* - * renameatt - changes the name of a attribute in a relation - * - * Attname attribute is changed in attribute catalog. - * No record of the previous attname is kept (correct?). - * - * get proper relrelation from relation catalog (if not arg) - * scan attribute catalog - * for name conflict (within rel) - * for original attribute (if not arg) - * modify attname in attribute tuple - * insert modified attribute in attribute catalog - * delete original attribute from attribute catalog - */ -void -renameatt(Oid relid, - const char *oldattname, - const char *newattname, - bool recurse) -{ - Relation targetrelation; - Relation attrelation; - HeapTuple atttup; - List *indexoidlist; - List *indexoidscan; - - /* - * Grab an exclusive lock on the target table, which we will NOT - * release until end of transaction. - */ - targetrelation = heap_open(relid, AccessExclusiveLock); - - /* - * permissions checking. this would normally be done in utility.c, - * but this particular routine is recursive. - * - * normally, only the owner of a class can change its schema. - */ - if (!allowSystemTableMods - && IsSystemRelation(targetrelation)) - elog(ERROR, "renameatt: class \"%s\" is a system catalog", - RelationGetRelationName(targetrelation)); - if (!pg_class_ownercheck(relid, GetUserId())) - elog(ERROR, "renameatt: you do not own class \"%s\"", - RelationGetRelationName(targetrelation)); - - /* - * if the 'recurse' flag is set then we are supposed to rename this - * attribute in all classes that inherit from 'relname' (as well as in - * 'relname'). - * - * any permissions or problems with duplicate attributes will cause the - * whole transaction to abort, which is what we want -- all or - * nothing. - */ - if (recurse) - { - List *child, - *children; - - /* this routine is actually in the planner */ - children = find_all_inheritors(relid); - - /* - * find_all_inheritors does the recursive search of the - * inheritance hierarchy, so all we have to do is process all of - * the relids in the list that it returns. - */ - foreach(child, children) - { - Oid childrelid = lfirsti(child); - - if (childrelid == relid) - continue; - /* note we need not recurse again! */ - renameatt(childrelid, oldattname, newattname, false); - } - } - - attrelation = heap_openr(AttributeRelationName, RowExclusiveLock); - - atttup = SearchSysCacheCopy(ATTNAME, - ObjectIdGetDatum(relid), - PointerGetDatum(oldattname), - 0, 0); - if (!HeapTupleIsValid(atttup)) - elog(ERROR, "renameatt: attribute \"%s\" does not exist", oldattname); - - if (((Form_pg_attribute) GETSTRUCT(atttup))->attnum < 0) - elog(ERROR, "renameatt: system attribute \"%s\" not renamed", oldattname); - - /* should not already exist */ - if (SearchSysCacheExists(ATTNAME, - ObjectIdGetDatum(relid), - PointerGetDatum(newattname), - 0, 0)) - elog(ERROR, "renameatt: attribute \"%s\" exists", newattname); - - StrNCpy(NameStr(((Form_pg_attribute) GETSTRUCT(atttup))->attname), - newattname, NAMEDATALEN); - - simple_heap_update(attrelation, &atttup->t_self, atttup); - - /* keep system catalog indices current */ - { - Relation irelations[Num_pg_attr_indices]; - - CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, irelations); - CatalogIndexInsert(irelations, Num_pg_attr_indices, attrelation, atttup); - CatalogCloseIndices(Num_pg_attr_indices, irelations); - } - - heap_freetuple(atttup); - - /* - * Update column names of indexes that refer to the column being - * renamed. - */ - indexoidlist = RelationGetIndexList(targetrelation); - - foreach(indexoidscan, indexoidlist) - { - Oid indexoid = lfirsti(indexoidscan); - HeapTuple indextup; - - /* - * First check to see if index is a functional index. If so, its - * column name is a function name and shouldn't be renamed here. - */ - indextup = SearchSysCache(INDEXRELID, - ObjectIdGetDatum(indexoid), - 0, 0, 0); - if (!HeapTupleIsValid(indextup)) - elog(ERROR, "renameatt: can't find index id %u", indexoid); - if (OidIsValid(((Form_pg_index) GETSTRUCT(indextup))->indproc)) - { - ReleaseSysCache(indextup); - continue; - } - ReleaseSysCache(indextup); - - /* - * Okay, look to see if any column name of the index matches the - * old attribute name. - */ - atttup = SearchSysCacheCopy(ATTNAME, - ObjectIdGetDatum(indexoid), - PointerGetDatum(oldattname), - 0, 0); - if (!HeapTupleIsValid(atttup)) - continue; /* Nope, so ignore it */ - - /* - * Update the (copied) attribute tuple. - */ - StrNCpy(NameStr(((Form_pg_attribute) GETSTRUCT(atttup))->attname), - newattname, NAMEDATALEN); - - simple_heap_update(attrelation, &atttup->t_self, atttup); - - /* keep system catalog indices current */ - { - Relation irelations[Num_pg_attr_indices]; - - CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, irelations); - CatalogIndexInsert(irelations, Num_pg_attr_indices, attrelation, atttup); - CatalogCloseIndices(Num_pg_attr_indices, irelations); - } - heap_freetuple(atttup); - } - - freeList(indexoidlist); - - heap_close(attrelation, RowExclusiveLock); - - /* - * Update att name in any RI triggers associated with the relation. - */ - if (targetrelation->rd_rel->reltriggers > 0) - { - /* update tgargs column reference where att is primary key */ - update_ri_trigger_args(RelationGetRelid(targetrelation), - oldattname, newattname, - false, false); - /* update tgargs column reference where att is foreign key */ - update_ri_trigger_args(RelationGetRelid(targetrelation), - oldattname, newattname, - true, false); - } - - heap_close(targetrelation, NoLock); /* close rel but keep lock! */ -} - -/* - * renamerel - change the name of a relation - * - * XXX - When renaming sequences, we don't bother to modify the - * sequence name that is stored within the sequence itself - * (this would cause problems with MVCC). In the future, - * the sequence name should probably be removed from the - * sequence, AFAIK there's no need for it to be there. - */ -void -renamerel(Oid relid, const char *newrelname) -{ - Relation targetrelation; - Relation relrelation; /* for RELATION relation */ - HeapTuple reltup; - Oid namespaceId; - char relkind; - bool relhastriggers; - Relation irelations[Num_pg_class_indices]; - - /* - * Grab an exclusive lock on the target table or index, which we will - * NOT release until end of transaction. - */ - targetrelation = relation_open(relid, AccessExclusiveLock); - - namespaceId = RelationGetNamespace(targetrelation); - - /* Validity checks */ - if (!allowSystemTableMods && - IsSystemRelation(targetrelation)) - elog(ERROR, "renamerel: system relation \"%s\" may not be renamed", - RelationGetRelationName(targetrelation)); - - relkind = targetrelation->rd_rel->relkind; - relhastriggers = (targetrelation->rd_rel->reltriggers > 0); - - /* - * Find relation's pg_class tuple, and make sure newrelname isn't in - * use. - */ - relrelation = heap_openr(RelationRelationName, RowExclusiveLock); - - reltup = SearchSysCacheCopy(RELOID, - PointerGetDatum(relid), - 0, 0, 0); - if (!HeapTupleIsValid(reltup)) - elog(ERROR, "renamerel: relation \"%s\" does not exist", - RelationGetRelationName(targetrelation)); - - if (get_relname_relid(newrelname, namespaceId) != InvalidOid) - elog(ERROR, "renamerel: relation \"%s\" exists", newrelname); - - /* - * Update pg_class tuple with new relname. (Scribbling on reltup is - * OK because it's a copy...) - */ - StrNCpy(NameStr(((Form_pg_class) GETSTRUCT(reltup))->relname), - newrelname, NAMEDATALEN); - - simple_heap_update(relrelation, &reltup->t_self, reltup); - - /* keep the system catalog indices current */ - CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, irelations); - CatalogIndexInsert(irelations, Num_pg_class_indices, relrelation, reltup); - CatalogCloseIndices(Num_pg_class_indices, irelations); - - heap_close(relrelation, NoLock); - heap_freetuple(reltup); - - /* - * Also rename the associated type, if any. - */ - if (relkind != RELKIND_INDEX) - TypeRename(RelationGetRelationName(targetrelation), namespaceId, - newrelname); - - /* - * If it's a view, must also rename the associated ON SELECT rule. - */ - if (relkind == RELKIND_VIEW) - { - char *oldrulename, - *newrulename; - - oldrulename = MakeRetrieveViewRuleName(RelationGetRelationName(targetrelation)); - newrulename = MakeRetrieveViewRuleName(newrelname); - RenameRewriteRule(oldrulename, newrulename); - } - - /* - * Update rel name in any RI triggers associated with the relation. - */ - if (relhastriggers) - { - /* update tgargs where relname is primary key */ - update_ri_trigger_args(relid, - RelationGetRelationName(targetrelation), - newrelname, - false, true); - /* update tgargs where relname is foreign key */ - update_ri_trigger_args(relid, - RelationGetRelationName(targetrelation), - newrelname, - true, true); - } - - /* - * Close rel, but keep exclusive lock! - */ - relation_close(targetrelation, NoLock); -} - -/* - * Given a trigger function OID, determine whether it is an RI trigger, - * and if so whether it is attached to PK or FK relation. - * - * XXX this probably doesn't belong here; should be exported by - * ri_triggers.c - */ -static int -ri_trigger_type(Oid tgfoid) -{ - switch (tgfoid) - { - case F_RI_FKEY_CASCADE_DEL: - case F_RI_FKEY_CASCADE_UPD: - case F_RI_FKEY_RESTRICT_DEL: - case F_RI_FKEY_RESTRICT_UPD: - case F_RI_FKEY_SETNULL_DEL: - case F_RI_FKEY_SETNULL_UPD: - case F_RI_FKEY_SETDEFAULT_DEL: - case F_RI_FKEY_SETDEFAULT_UPD: - case F_RI_FKEY_NOACTION_DEL: - case F_RI_FKEY_NOACTION_UPD: - return RI_TRIGGER_PK; - - case F_RI_FKEY_CHECK_INS: - case F_RI_FKEY_CHECK_UPD: - return RI_TRIGGER_FK; - } - - return RI_TRIGGER_NONE; -} - -/* - * Scan pg_trigger for RI triggers that are on the specified relation - * (if fk_scan is false) or have it as the tgconstrrel (if fk_scan - * is true). Update RI trigger args fields matching oldname to contain - * newname instead. If update_relname is true, examine the relname - * fields; otherwise examine the attname fields. - */ -static void -update_ri_trigger_args(Oid relid, - const char *oldname, - const char *newname, - bool fk_scan, - bool update_relname) -{ - Relation tgrel; - Relation irel; - ScanKeyData skey[1]; - IndexScanDesc idxtgscan; - RetrieveIndexResult idxres; - Datum values[Natts_pg_trigger]; - char nulls[Natts_pg_trigger]; - char replaces[Natts_pg_trigger]; - - tgrel = heap_openr(TriggerRelationName, RowExclusiveLock); - if (fk_scan) - irel = index_openr(TriggerConstrRelidIndex); - else - irel = index_openr(TriggerRelidIndex); - - ScanKeyEntryInitialize(&skey[0], 0x0, - 1, /* always column 1 of index */ - F_OIDEQ, - ObjectIdGetDatum(relid)); - idxtgscan = index_beginscan(irel, false, 1, skey); - - while ((idxres = index_getnext(idxtgscan, ForwardScanDirection)) != NULL) - { - HeapTupleData tupledata; - Buffer buffer; - HeapTuple tuple; - Form_pg_trigger pg_trigger; - bytea *val; - bytea *newtgargs; - bool isnull; - int tg_type; - bool examine_pk; - bool changed; - int tgnargs; - int i; - int newlen; - const char *arga[RI_MAX_ARGUMENTS]; - const char *argp; - - tupledata.t_self = idxres->heap_iptr; - heap_fetch(tgrel, SnapshotNow, &tupledata, &buffer, idxtgscan); - pfree(idxres); - if (!tupledata.t_data) - continue; - tuple = &tupledata; - pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple); - tg_type = ri_trigger_type(pg_trigger->tgfoid); - if (tg_type == RI_TRIGGER_NONE) - { - /* Not an RI trigger, forget it */ - ReleaseBuffer(buffer); - continue; - } - - /* - * It is an RI trigger, so parse the tgargs bytea. - * - * NB: we assume the field will never be compressed or moved out of - * line; so does trigger.c ... - */ - tgnargs = pg_trigger->tgnargs; - val = (bytea *) fastgetattr(tuple, - Anum_pg_trigger_tgargs, - tgrel->rd_att, &isnull); - if (isnull || tgnargs < RI_FIRST_ATTNAME_ARGNO || - tgnargs > RI_MAX_ARGUMENTS) - { - /* This probably shouldn't happen, but ignore busted triggers */ - ReleaseBuffer(buffer); - continue; - } - argp = (const char *) VARDATA(val); - for (i = 0; i < tgnargs; i++) - { - arga[i] = argp; - argp += strlen(argp) + 1; - } - - /* - * Figure out which item(s) to look at. If the trigger is - * primary-key type and attached to my rel, I should look at the - * PK fields; if it is foreign-key type and attached to my rel, I - * should look at the FK fields. But the opposite rule holds when - * examining triggers found by tgconstrrel search. - */ - examine_pk = (tg_type == RI_TRIGGER_PK) == (!fk_scan); - - changed = false; - if (update_relname) - { - /* Change the relname if needed */ - i = examine_pk ? RI_PK_RELNAME_ARGNO : RI_FK_RELNAME_ARGNO; - if (strcmp(arga[i], oldname) == 0) - { - arga[i] = newname; - changed = true; - } - } - else - { - /* Change attname(s) if needed */ - i = examine_pk ? RI_FIRST_ATTNAME_ARGNO + RI_KEYPAIR_PK_IDX : - RI_FIRST_ATTNAME_ARGNO + RI_KEYPAIR_FK_IDX; - for (; i < tgnargs; i += 2) - { - if (strcmp(arga[i], oldname) == 0) - { - arga[i] = newname; - changed = true; - } - } - } - - if (!changed) - { - /* Don't need to update this tuple */ - ReleaseBuffer(buffer); - continue; - } - - /* - * Construct modified tgargs bytea. - */ - newlen = VARHDRSZ; - for (i = 0; i < tgnargs; i++) - newlen += strlen(arga[i]) + 1; - newtgargs = (bytea *) palloc(newlen); - VARATT_SIZEP(newtgargs) = newlen; - newlen = VARHDRSZ; - for (i = 0; i < tgnargs; i++) - { - strcpy(((char *) newtgargs) + newlen, arga[i]); - newlen += strlen(arga[i]) + 1; - } - - /* - * Build modified tuple. - */ - for (i = 0; i < Natts_pg_trigger; i++) - { - values[i] = (Datum) 0; - replaces[i] = ' '; - nulls[i] = ' '; - } - values[Anum_pg_trigger_tgargs - 1] = PointerGetDatum(newtgargs); - replaces[Anum_pg_trigger_tgargs - 1] = 'r'; - - tuple = heap_modifytuple(tuple, tgrel, values, nulls, replaces); - - /* - * Now we can release hold on original tuple. - */ - ReleaseBuffer(buffer); - - /* - * Update pg_trigger and its indexes - */ - simple_heap_update(tgrel, &tuple->t_self, tuple); - - { - Relation irelations[Num_pg_attr_indices]; - - CatalogOpenIndices(Num_pg_trigger_indices, Name_pg_trigger_indices, irelations); - CatalogIndexInsert(irelations, Num_pg_trigger_indices, tgrel, tuple); - CatalogCloseIndices(Num_pg_trigger_indices, irelations); - } - - /* free up our scratch memory */ - pfree(newtgargs); - heap_freetuple(tuple); - } - - index_endscan(idxtgscan); - index_close(irel); - - heap_close(tgrel, RowExclusiveLock); - - /* - * Increment cmd counter to make updates visible; this is needed in - * case the same tuple has to be updated again by next pass (can - * happen in case of a self-referential FK relationship). - */ - CommandCounterIncrement(); -} diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c new file mode 100644 index 0000000000000000000000000000000000000000..191d5e329b8a9ced0c198cbf411865f78c6b92c8 --- /dev/null +++ b/src/backend/commands/schemacmds.c @@ -0,0 +1,116 @@ +/*------------------------------------------------------------------------- + * + * schemacmds.c + * schema creation command support code + * + * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/commands/schemacmds.c,v 1.1 2002/04/15 05:22:03 tgl Exp $ + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "catalog/catalog.h" +#include "catalog/pg_namespace.h" +#include "commands/schemacmds.h" +#include "miscadmin.h" +#include "parser/analyze.h" +#include "tcop/utility.h" +#include "utils/lsyscache.h" + + +/* + * CREATE SCHEMA + */ +void +CreateSchemaCommand(CreateSchemaStmt *stmt) +{ + const char *schemaName = stmt->schemaname; + const char *authId = stmt->authid; + List *parsetree_list; + List *parsetree_item; + const char *owner_name; + Oid owner_userid; + Oid saved_userid; + + saved_userid = GetUserId(); + + if (!authId) + { + owner_userid = saved_userid; + owner_name = GetUserName(owner_userid); + } + else if (superuser()) + { + owner_name = authId; + /* The following will error out if user does not exist */ + owner_userid = get_usesysid(owner_name); + /* + * Set the current user to the requested authorization so + * that objects created in the statement have the requested + * owner. (This will revert to session user on error or at + * the end of this routine.) + */ + SetUserId(owner_userid); + } + else /* not superuser */ + { + owner_userid = saved_userid; + owner_name = GetUserName(owner_userid); + if (strcmp(authId, owner_name) != 0) + elog(ERROR, "CREATE SCHEMA: permission denied" + "\n\t\"%s\" is not a superuser, so cannot create a schema for \"%s\"", + owner_name, authId); + } + + if (!allowSystemTableMods && IsReservedName(schemaName)) + elog(ERROR, "CREATE SCHEMA: Illegal schema name: \"%s\" -- pg_ is reserved for system schemas", + schemaName); + + /* Create the schema's namespace */ + NamespaceCreate(schemaName, owner_userid); + + /* Let commands in the schema-element-list know about the schema */ + CommandCounterIncrement(); + + /* + * Examine the list of commands embedded in the CREATE SCHEMA command, + * and reorganize them into a sequentially executable order with no + * forward references. Note that the result is still a list of raw + * parsetrees in need of parse analysis --- we cannot, in general, + * run analyze.c on one statement until we have actually executed the + * prior ones. + */ + parsetree_list = analyzeCreateSchemaStmt(stmt); + + /* + * Analyze and execute each command contained in the CREATE SCHEMA + */ + foreach(parsetree_item, parsetree_list) + { + Node *parsetree = (Node *) lfirst(parsetree_item); + List *querytree_list, + *querytree_item; + + querytree_list = parse_analyze(parsetree, NULL); + + foreach(querytree_item, querytree_list) + { + Query *querytree = (Query *) lfirst(querytree_item); + + /* schemas should contain only utility stmts */ + Assert(querytree->commandType == CMD_UTILITY); + /* do this step */ + ProcessUtility(querytree->utilityStmt, None, NULL); + /* make sure later steps can see the object created here */ + CommandCounterIncrement(); + } + } + + /* Reset current user */ + SetUserId(saved_userid); +} diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c index a7678c5ce38484567e7ff38c25a27bf3f463947c..0fa56fcfb7db7fdba605fade2ae2ecc978069076 100644 --- a/src/backend/commands/sequence.c +++ b/src/backend/commands/sequence.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/sequence.c,v 1.76 2002/03/30 01:02:41 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/sequence.c,v 1.77 2002/04/15 05:22:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,7 +17,7 @@ #include "access/heapam.h" #include "catalog/namespace.h" #include "catalog/pg_type.h" -#include "commands/creatinh.h" +#include "commands/tablecmds.h" #include "commands/sequence.h" #include "miscadmin.h" #include "utils/acl.h" diff --git a/src/backend/commands/command.c b/src/backend/commands/tablecmds.c similarity index 52% rename from src/backend/commands/command.c rename to src/backend/commands/tablecmds.c index 9a20d8329ad9a60e7680c32ac936f10320f745a7..18cea28df12f79028abd87bb87f8631b3483a40e 100644 --- a/src/backend/commands/command.c +++ b/src/backend/commands/tablecmds.c @@ -1,19 +1,14 @@ /*------------------------------------------------------------------------- * - * command.c - * random postgres portal and utility support code + * tablecmds.c + * Commands for altering table structures and settings * - * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/Attic/command.c,v 1.175 2002/04/14 16:47:16 momjian Exp $ - * - * NOTES - * The PerformAddAttribute() code, like most of the relation - * manipulating code in the commands/ directory, should go - * someplace closer to the lib/catalog code. + * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.1 2002/04/15 05:22:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -28,27 +23,25 @@ #include "catalog/indexing.h" #include "catalog/namespace.h" #include "catalog/pg_attrdef.h" -#include "catalog/pg_index.h" +#include "catalog/pg_inherits.h" #include "catalog/pg_namespace.h" #include "catalog/pg_opclass.h" -#include "catalog/pg_relcheck.h" +#include "catalog/pg_trigger.h" #include "catalog/pg_type.h" -#include "commands/command.h" +#include "commands/tablecmds.h" #include "commands/trigger.h" -#include "executor/execdefs.h" #include "executor/executor.h" #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/clauses.h" #include "optimizer/planmain.h" #include "optimizer/prep.h" -#include "parser/analyze.h" #include "parser/parse.h" #include "parser/parse_expr.h" -#include "parser/parse_oper.h" #include "parser/parse_relation.h" #include "parser/parse_type.h" -#include "tcop/utility.h" +#include "rewrite/rewriteDefine.h" +#include "rewrite/rewriteSupport.h" #include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" @@ -61,220 +54,27 @@ static void drop_default(Oid relid, int16 attnum); static bool needs_toast_table(Relation rel); static void CheckTupleType(Form_pg_class tuple_class); +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 List *MergeDomainAttributes(List *schema); -/* - * PortalCleanup - */ -void -PortalCleanup(Portal portal) -{ - MemoryContext oldcontext; - - /* - * sanity checks - */ - AssertArg(PortalIsValid(portal)); - AssertArg(portal->cleanup == PortalCleanup); - - /* - * set proper portal-executor context before calling ExecMain. - */ - oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); - - /* - * tell the executor to shutdown the query - */ - ExecutorEnd(PortalGetQueryDesc(portal), PortalGetState(portal)); - - /* - * switch back to previous context - */ - MemoryContextSwitchTo(oldcontext); -} - - -/* - * PerformPortalFetch - * - * name: name of portal - * forward: forward or backward fetch? - * count: # of tuples to fetch (0 implies all) - * dest: where to send results - * completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE - * in which to store a command completion status string. - * - * completionTag may be NULL if caller doesn't want a status string. - */ -void -PerformPortalFetch(char *name, - bool forward, - int count, - CommandDest dest, - char *completionTag) -{ - Portal portal; - QueryDesc *queryDesc; - EState *estate; - MemoryContext oldcontext; - ScanDirection direction; - CommandId savedId; - bool temp_desc = false; - - /* initialize completion status in case of early exit */ - if (completionTag) - strcpy(completionTag, (dest == None) ? "MOVE 0" : "FETCH 0"); - - /* - * sanity checks - */ - if (name == NULL) - { - elog(WARNING, "PerformPortalFetch: missing portal name"); - return; - } - - /* - * get the portal from the portal name - */ - portal = GetPortalByName(name); - if (!PortalIsValid(portal)) - { - elog(WARNING, "PerformPortalFetch: portal \"%s\" not found", - name); - return; - } - - /* - * switch into the portal context - */ - oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); - - queryDesc = PortalGetQueryDesc(portal); - estate = PortalGetState(portal); - - /* - * If the requested destination is not the same as the query's - * original destination, make a temporary QueryDesc with the proper - * destination. This supports MOVE, for example, which will pass in - * dest = None. - * - * EXCEPTION: if the query's original dest is RemoteInternal (ie, it's a - * binary cursor) and the request is Remote, we do NOT override the - * original dest. This is necessary since a FETCH command will pass - * dest = Remote, not knowing whether the cursor is binary or not. - */ - if (dest != queryDesc->dest && - !(queryDesc->dest == RemoteInternal && dest == Remote)) - { - QueryDesc *qdesc = (QueryDesc *) palloc(sizeof(QueryDesc)); - - memcpy(qdesc, queryDesc, sizeof(QueryDesc)); - qdesc->dest = dest; - queryDesc = qdesc; - temp_desc = true; - } - - /* - * Restore the scanCommandId that was current when the cursor was - * opened. This ensures that we see the same tuples throughout the - * execution of the cursor. - */ - savedId = GetScanCommandId(); - SetScanCommandId(PortalGetCommandId(portal)); - - /* - * Determine which direction to go in, and check to see if we're - * already at the end of the available tuples in that direction. If - * so, set the direction to NoMovement to avoid trying to fetch any - * tuples. (This check exists because not all plan node types - * are robust about being called again if they've already returned - * NULL once.) Then call the executor (we must not skip this, because - * the destination needs to see a setup and shutdown even if no tuples - * are available). Finally, update the atStart/atEnd state depending - * on the number of tuples that were retrieved. - */ - if (forward) - { - if (portal->atEnd) - direction = NoMovementScanDirection; - else - direction = ForwardScanDirection; - - ExecutorRun(queryDesc, estate, direction, (long) count); - - if (estate->es_processed > 0) - portal->atStart = false; /* OK to back up now */ - if (count <= 0 || (int) estate->es_processed < count) - portal->atEnd = true; /* we retrieved 'em all */ - } - else - { - if (portal->atStart) - direction = NoMovementScanDirection; - else - direction = BackwardScanDirection; - - ExecutorRun(queryDesc, estate, direction, (long) count); - - if (estate->es_processed > 0) - portal->atEnd = false; /* OK to go forward now */ - if (count <= 0 || (int) estate->es_processed < count) - portal->atStart = true; /* we retrieved 'em all */ - } - - /* Return command status if wanted */ - if (completionTag) - snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %u", - (dest == None) ? "MOVE" : "FETCH", - estate->es_processed); - - /* - * Restore outer command ID. - */ - SetScanCommandId(savedId); - - /* - * Clean up and switch back to old context. - */ - if (temp_desc) - pfree(queryDesc); - - MemoryContextSwitchTo(oldcontext); -} - -/* - * PerformPortalClose - */ -void -PerformPortalClose(char *name, CommandDest dest) -{ - Portal portal; +/* Used by attribute and relation renaming routines: */ - /* - * sanity checks - */ - if (name == NULL) - { - elog(WARNING, "PerformPortalClose: missing portal name"); - return; - } +#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 */ - /* - * get the portal from the portal name - */ - portal = GetPortalByName(name); - if (!PortalIsValid(portal)) - { - elog(WARNING, "PerformPortalClose: portal \"%s\" not found", - name); - return; - } +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); - /* - * Note: PortalCleanup is called as a side-effect - */ - PortalDrop(portal); -} /* ---------------- * AlterTableAddColumn @@ -1856,142 +1656,1427 @@ needs_toast_table(Relation rel) return (tuple_length > TOAST_TUPLE_THRESHOLD); } -/* - * LOCK TABLE + +/* ---------------------------------------------------------------- + * DefineRelation + * Creates a new relation. + * + * If successful, returns the OID of the new relation. + * ---------------------------------------------------------------- */ -void -LockTableCommand(LockStmt *lockstmt) +Oid +DefineRelation(CreateStmt *stmt, char relkind) { - List *p; + char *relname = palloc(NAMEDATALEN); + Oid namespaceId; + List *schema = stmt->tableElts; + int numberOfAttributes; + 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); + + /* + * Look up the namespace in which we are supposed to create the + * relation. + */ + namespaceId = RangeVarGetCreationNamespace(stmt->relation); + + /* + * Merge domain attributes into the known columns before processing table + * inheritance. Otherwise we risk adding double constraints to a + * domain-type column that's inherited. + */ + schema = MergeDomainAttributes(schema); + + /* + * Look up inheritance ancestors and generate relation schema, + * including inherited attributes. + */ + schema = MergeAttributes(schema, stmt->inhRelations, + stmt->relation->istemp, + &inheritOids, &old_constraints, &parentHasOids); + + numberOfAttributes = length(schema); + if (numberOfAttributes <= 0) + elog(ERROR, "DefineRelation: please inherit from a relation or define an attribute"); /* - * Iterate over the list and open, lock, and close the relations one - * at a time + * 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); - foreach(p, lockstmt->relations) + if (old_constraints != NIL) { - RangeVar *relation = lfirst(p); - Oid reloid; - int32 aclresult; - Relation rel; + ConstrCheck *check = (ConstrCheck *) palloc(length(old_constraints) * + sizeof(ConstrCheck)); + int ncheck = 0; - /* - * We don't want to open the relation until we've checked privilege. - * So, manually get the relation OID. - */ - reloid = RangeVarGetRelid(relation, false); + foreach(listptr, old_constraints) + { + Constraint *cdef = (Constraint *) lfirst(listptr); - if (lockstmt->mode == AccessShareLock) - aclresult = pg_class_aclcheck(reloid, GetUserId(), - ACL_SELECT); - else - aclresult = pg_class_aclcheck(reloid, GetUserId(), - ACL_UPDATE | ACL_DELETE); + if (cdef->contype != CONSTR_CHECK) + continue; + + if (cdef->name != NULL) + { + for (i = 0; i < ncheck; i++) + { + if (strcmp(check[i].ccname, cdef->name) == 0) + elog(ERROR, "Duplicate CHECK constraint name: '%s'", + cdef->name); + } + check[ncheck].ccname = cdef->name; + } + else + { + check[ncheck].ccname = (char *) palloc(NAMEDATALEN); + snprintf(check[ncheck].ccname, NAMEDATALEN, "$%d", ncheck + 1); + } + 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, + stmt->hasoids || parentHasOids, + 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 = heap_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. + * + * First, scan schema to find new column defaults. + */ + rawDefaults = NIL; + attnum = 0; - if (aclresult != ACLCHECK_OK) - elog(ERROR, "LOCK TABLE: permission denied"); + foreach(listptr, schema) + { + ColumnDef *colDef = lfirst(listptr); + RawColumnDefault *rawEnt; - rel = relation_open(reloid, lockstmt->mode); + attnum++; - /* Currently, we only allow plain tables to be locked */ - if (rel->rd_rel->relkind != RELKIND_RELATION) - elog(ERROR, "LOCK TABLE: %s is not a table", - relation->relname); + if (colDef->raw_default == NULL) + continue; + Assert(colDef->cooked_default == NULL); - relation_close(rel, NoLock); /* close rel, keep lock */ + rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault)); + rawEnt->attnum = attnum; + rawEnt->raw_default = colDef->raw_default; + rawDefaults = lappend(rawDefaults, rawEnt); } + + /* + * 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). + */ + heap_close(rel, NoLock); + + return relationId; } +/* + * RemoveRelation + * Deletes a relation. + * + * Exceptions: + * BadArg if name is invalid. + * + * Note: + * If the relation has indices defined on it, then the index relations + * themselves will be destroyed, too. + */ +void +RemoveRelation(const RangeVar *relation) +{ + Oid relOid; + + relOid = RangeVarGetRelid(relation, false); + heap_drop_with_catalog(relOid, allowSystemTableMods); +} /* - * CREATE SCHEMA + * TruncateRelation + * Removes all the rows from a relation + * + * Exceptions: + * BadArg if name is invalid + * + * Note: + * Rows are removed, indices are truncated and reconstructed. */ void -CreateSchemaCommand(CreateSchemaStmt *stmt) +TruncateRelation(const RangeVar *relation) { - const char *schemaName = stmt->schemaname; - const char *authId = stmt->authid; - List *parsetree_list; - List *parsetree_item; - const char *owner_name; - Oid owner_userid; - Oid saved_userid; + Relation rel; + Oid relid; - saved_userid = GetUserId(); + /* Grab exclusive lock in preparation for truncate */ + rel = heap_openrv(relation, AccessExclusiveLock); + relid = RelationGetRelid(rel); - if (!authId) - { - owner_userid = saved_userid; - owner_name = GetUserName(owner_userid); - } - else if (superuser()) - { - owner_name = authId; - /* The following will error out if user does not exist */ - owner_userid = get_usesysid(owner_name); - /* - * Set the current user to the requested authorization so - * that objects created in the statement have the requested - * owner. (This will revert to session user on error or at - * the end of this routine.) - */ - SetUserId(owner_userid); - } - else /* not superuser */ - { - owner_userid = saved_userid; - owner_name = GetUserName(owner_userid); - if (strcmp(authId, owner_name) != 0) - elog(ERROR, "CREATE SCHEMA: permission denied" - "\n\t\"%s\" is not a superuser, so cannot create a schema for \"%s\"", - owner_name, authId); - } + if (rel->rd_rel->relkind == RELKIND_SEQUENCE) + elog(ERROR, "TRUNCATE cannot be used on sequences. '%s' is a sequence", + RelationGetRelationName(rel)); + + if (rel->rd_rel->relkind == RELKIND_VIEW) + elog(ERROR, "TRUNCATE cannot be used on views. '%s' is a view", + RelationGetRelationName(rel)); - if (!allowSystemTableMods && IsReservedName(schemaName)) - elog(ERROR, "CREATE SCHEMA: Illegal schema name: \"%s\" -- pg_ is reserved for system schemas", - schemaName); + if (!allowSystemTableMods && IsSystemRelation(rel)) + elog(ERROR, "TRUNCATE cannot be used on system tables. '%s' is a system table", + RelationGetRelationName(rel)); - /* Create the schema's namespace */ - NamespaceCreate(schemaName, owner_userid); + if (!pg_class_ownercheck(relid, GetUserId())) + elog(ERROR, "you do not own relation \"%s\"", + RelationGetRelationName(rel)); - /* Let commands in the schema-element-list know about the schema */ - CommandCounterIncrement(); + /* Keep the lock until transaction commit */ + heap_close(rel, NoLock); + + heap_truncate(relid); +} + + +/* + * MergeDomainAttributes + * Returns a new table schema with the constraints, types, and other + * attributes of domains resolved for fields using a domain as + * their type. + */ +static List * +MergeDomainAttributes(List *schema) +{ + List *entry; /* - * Examine the list of commands embedded in the CREATE SCHEMA command, - * and reorganize them into a sequentially executable order with no - * forward references. Note that the result is still a list of raw - * parsetrees in need of parse analysis --- we cannot, in general, - * run analyze.c on one statement until we have actually executed the - * prior ones. + * Loop through the table elements supplied. These should + * never include inherited domains else they'll be + * double (or more) processed. */ - parsetree_list = analyzeCreateSchemaStmt(stmt); + foreach(entry, schema) + { + ColumnDef *coldef = lfirst(entry); + HeapTuple tuple; + Form_pg_type typeTup; + + tuple = typenameType(coldef->typename); + typeTup = (Form_pg_type) GETSTRUCT(tuple); + + if (typeTup->typtype == 'd') + { + /* Force the column to have the correct typmod. */ + coldef->typename->typmod = typeTup->typtypmod; + /* XXX more to do here? */ + } + + /* Enforce type NOT NULL || column definition NOT NULL -> NOT NULL */ + /* Currently only used for domains, but could be valid for all */ + coldef->is_not_null |= typeTup->typnotnull; + + ReleaseSysCache(tuple); + } + + return schema; +} + +/*---------- + * 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 an integer 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 + * the column will not have a default. + * (3) If conflicting defaults are inherited from different parents + * (and not overridden by the child), an error is raised. + * (4) Otherwise the inherited default is used. + * Rule (3) is new in Postgres 7.1; in earlier releases you got a + * rather arbitrary choice of which parent default to use. + *---------- + */ +static List * +MergeAttributes(List *schema, List *supers, bool istemp, + List **supOids, List **supconstr, bool *supHasOids) +{ + List *entry; + List *inhSchema = NIL; + List *parentOids = NIL; + List *constraints = NIL; + bool parentHasOids = false; + bool have_bogus_defaults = false; + char *bogus_marker = "Bogus!"; /* marks conflicting + * defaults */ + int child_attno; /* - * Analyze and execute each command contained in the CREATE SCHEMA + * Check for duplicate names in the explicit list of attributes. + * + * Although we might consider merging such entries in the same way that + * we handle name conflicts for inherited attributes, it seems to make + * more sense to assume such conflicts are errors. */ - foreach(parsetree_item, parsetree_list) + foreach(entry, schema) { - Node *parsetree = (Node *) lfirst(parsetree_item); - List *querytree_list, - *querytree_item; + ColumnDef *coldef = lfirst(entry); + List *rest; - querytree_list = parse_analyze(parsetree, NULL); - - foreach(querytree_item, querytree_list) + foreach(rest, lnext(entry)) { - Query *querytree = (Query *) lfirst(querytree_item); - - /* schemas should contain only utility stmts */ - Assert(querytree->commandType == CMD_UTILITY); - /* do this step */ - ProcessUtility(querytree->utilityStmt, None, NULL); - /* make sure later steps can see the object created here */ - CommandCounterIncrement(); + ColumnDef *restdef = lfirst(rest); + + if (strcmp(coldef->colname, restdef->colname) == 0) + elog(ERROR, "CREATE TABLE: attribute \"%s\" duplicated", + coldef->colname); } } - /* Reset current user */ - SetUserId(saved_userid); + /* + * Scan the parents left-to-right, and merge their attributes to form + * a list of inherited attributes (inhSchema). Also check to see if + * we need to inherit an OID column. + */ + child_attno = 0; + foreach(entry, supers) + { + RangeVar *parent = (RangeVar *) lfirst(entry); + Relation relation; + TupleDesc tupleDesc; + TupleConstr *constr; + AttrNumber *newattno; + AttrNumber parent_attno; + + relation = heap_openrv(parent, AccessShareLock); + + if (relation->rd_rel->relkind != RELKIND_RELATION) + elog(ERROR, "CREATE TABLE: inherited relation \"%s\" is not a table", + parent->relname); + /* Permanent rels cannot inherit from temporary ones */ + if (!istemp && isTempNamespace(RelationGetNamespace(relation))) + elog(ERROR, "CREATE TABLE: cannot inherit from temp relation \"%s\"", + parent->relname); + + /* + * We should have an UNDER permission flag for this, but for now, + * demand that creator of a child table own the parent. + */ + if (!pg_class_ownercheck(RelationGetRelid(relation), GetUserId())) + elog(ERROR, "you do not own table \"%s\"", + parent->relname); + + /* + * Reject duplications in the list of parents. + */ + if (intMember(RelationGetRelid(relation), parentOids)) + elog(ERROR, "CREATE TABLE: inherited relation \"%s\" duplicated", + parent->relname); + + parentOids = lappendi(parentOids, RelationGetRelid(relation)); + setRelhassubclassInRelation(RelationGetRelid(relation), true); + + parentHasOids |= relation->rd_rel->relhasoids; + + tupleDesc = RelationGetDescr(relation); + constr = tupleDesc->constr; + + /* + * newattno[] will contain the child-table attribute numbers for + * the attributes of this parent table. (They are not the same + * for parents after the first one.) + */ + newattno = (AttrNumber *) palloc(tupleDesc->natts * sizeof(AttrNumber)); + + for (parent_attno = 1; parent_attno <= tupleDesc->natts; + parent_attno++) + { + Form_pg_attribute attribute = tupleDesc->attrs[parent_attno - 1]; + char *attributeName = NameStr(attribute->attname); + int exist_attno; + ColumnDef *def; + TypeName *typename; + + /* + * Does it conflict with some previously inherited column? + */ + exist_attno = findAttrByName(attributeName, inhSchema); + if (exist_attno > 0) + { + /* + * Yes, try to merge the two column definitions. They must + * have the same type and typmod. + */ + elog(NOTICE, "CREATE TABLE: merging multiple inherited definitions of attribute \"%s\"", + attributeName); + def = (ColumnDef *) nth(exist_attno - 1, inhSchema); + if (typenameTypeId(def->typename) != attribute->atttypid || + def->typename->typmod != attribute->atttypmod) + elog(ERROR, "CREATE TABLE: inherited attribute \"%s\" type conflict (%s and %s)", + attributeName, + TypeNameToString(def->typename), + typeidTypeName(attribute->atttypid)); + /* Merge of NOT NULL constraints = OR 'em together */ + def->is_not_null |= attribute->attnotnull; + /* Default and other constraints are handled below */ + newattno[parent_attno - 1] = exist_attno; + } + else + { + /* + * No, create a new inherited column + */ + def = makeNode(ColumnDef); + def->colname = pstrdup(attributeName); + typename = makeNode(TypeName); + typename->typeid = attribute->atttypid; + typename->typmod = attribute->atttypmod; + def->typename = typename; + def->is_not_null = attribute->attnotnull; + def->raw_default = NULL; + def->cooked_default = NULL; + def->constraints = NIL; + inhSchema = lappend(inhSchema, def); + newattno[parent_attno - 1] = ++child_attno; + } + + /* + * Copy default if any + */ + if (attribute->atthasdef) + { + char *this_default = NULL; + AttrDefault *attrdef; + int i; + + /* Find default in constraint structure */ + Assert(constr != NULL); + attrdef = constr->defval; + for (i = 0; i < constr->num_defval; i++) + { + if (attrdef[i].adnum == parent_attno) + { + this_default = attrdef[i].adbin; + break; + } + } + Assert(this_default != NULL); + + /* + * If default expr could contain any vars, we'd need to + * fix 'em, but it can't; so default is ready to apply to + * child. + * + * If we already had a default from some prior parent, check + * to see if they are the same. If so, no problem; if + * not, mark the column as having a bogus default. Below, + * we will complain if the bogus default isn't overridden + * by the child schema. + */ + Assert(def->raw_default == NULL); + if (def->cooked_default == NULL) + def->cooked_default = pstrdup(this_default); + else if (strcmp(def->cooked_default, this_default) != 0) + { + def->cooked_default = bogus_marker; + have_bogus_defaults = true; + } + } + } + + /* + * Now copy the constraints of this parent, adjusting attnos using + * the completed newattno[] map + */ + if (constr && constr->num_check > 0) + { + ConstrCheck *check = constr->check; + int i; + + for (i = 0; i < constr->num_check; i++) + { + Constraint *cdef = makeNode(Constraint); + Node *expr; + + cdef->contype = CONSTR_CHECK; + if (check[i].ccname[0] == '$') + cdef->name = NULL; + else + cdef->name = pstrdup(check[i].ccname); + cdef->raw_expr = NULL; + /* adjust varattnos of ccbin here */ + expr = stringToNode(check[i].ccbin); + change_varattnos_of_a_node(expr, newattno); + cdef->cooked_expr = nodeToString(expr); + constraints = lappend(constraints, cdef); + } + } + + pfree(newattno); + + /* + * Close the parent rel, but keep our AccessShareLock on it until + * xact commit. That will prevent someone else from deleting or + * ALTERing the parent before the child is committed. + */ + heap_close(relation, NoLock); + } + + /* + * If we had no inherited attributes, the result schema is just the + * explicitly declared columns. Otherwise, we need to merge the + * declared columns into the inherited schema list. + */ + if (inhSchema != NIL) + { + foreach(entry, schema) + { + ColumnDef *newdef = lfirst(entry); + char *attributeName = newdef->colname; + int exist_attno; + + /* + * Does it conflict with some previously inherited column? + */ + exist_attno = findAttrByName(attributeName, inhSchema); + if (exist_attno > 0) + { + ColumnDef *def; + + /* + * Yes, try to merge the two column definitions. They must + * have the same type and typmod. + */ + elog(NOTICE, "CREATE TABLE: merging attribute \"%s\" with inherited definition", + attributeName); + def = (ColumnDef *) nth(exist_attno - 1, inhSchema); + if (typenameTypeId(def->typename) != typenameTypeId(newdef->typename) || + def->typename->typmod != newdef->typename->typmod) + elog(ERROR, "CREATE TABLE: attribute \"%s\" type conflict (%s and %s)", + attributeName, + TypeNameToString(def->typename), + TypeNameToString(newdef->typename)); + /* Merge of NOT NULL constraints = OR 'em together */ + def->is_not_null |= newdef->is_not_null; + /* If new def has a default, override previous default */ + if (newdef->raw_default != NULL) + { + def->raw_default = newdef->raw_default; + def->cooked_default = newdef->cooked_default; + } + } + else + { + /* + * No, attach new column to result schema + */ + inhSchema = lappend(inhSchema, newdef); + } + } + + schema = inhSchema; + } + + /* + * If we found any conflicting parent default values, check to make + * sure they were overridden by the child. + */ + if (have_bogus_defaults) + { + foreach(entry, schema) + { + ColumnDef *def = lfirst(entry); + + if (def->cooked_default == bogus_marker) + elog(ERROR, "CREATE TABLE: attribute \"%s\" inherits conflicting default values" + "\n\tTo resolve the conflict, specify a default explicitly", + def->colname); + } + } + + *supOids = parentOids; + *supconstr = constraints; + *supHasOids = parentHasOids; + return schema; +} + +/* + * complementary static functions for MergeAttributes(). + * + * Varattnos of pg_relcheck.rcbin must be rewritten when subclasses inherit + * constraints from parent classes, since the inherited attributes could + * be given different column numbers in multiple-inheritance cases. + * + * Note that the passed node tree is modified in place! + */ +static bool +change_varattnos_walker(Node *node, const AttrNumber *newattno) +{ + if (node == NULL) + return false; + if (IsA(node, Var)) + { + Var *var = (Var *) node; + + if (var->varlevelsup == 0 && var->varno == 1 && + var->varattno > 0) + { + /* + * ??? the following may be a problem when the node is + * multiply referenced though stringToNode() doesn't create + * such a node currently. + */ + Assert(newattno[var->varattno - 1] > 0); + var->varattno = newattno[var->varattno - 1]; + } + return false; + } + return expression_tree_walker(node, change_varattnos_walker, + (void *) newattno); +} + +static bool +change_varattnos_of_a_node(Node *node, const AttrNumber *newattno) +{ + return change_varattnos_walker(node, newattno); +} + +/* + * StoreCatalogInheritance + * Updates the system catalogs with proper inheritance information. + * + * supers is an integer list of the OIDs of the new relation's direct + * ancestors. NB: it is destructively changed to include indirect ancestors. + */ +static void +StoreCatalogInheritance(Oid relationId, List *supers) +{ + Relation relation; + TupleDesc desc; + int16 seqNumber; + List *entry; + HeapTuple tuple; + + /* + * sanity checks + */ + AssertArg(OidIsValid(relationId)); + + if (supers == NIL) + return; + + /* + * Catalog INHERITS information using direct ancestors only. + */ + relation = heap_openr(InheritsRelationName, RowExclusiveLock); + desc = RelationGetDescr(relation); + + seqNumber = 1; + foreach(entry, supers) + { + Oid entryOid = lfirsti(entry); + Datum datum[Natts_pg_inherits]; + char nullarr[Natts_pg_inherits]; + + datum[0] = ObjectIdGetDatum(relationId); /* inhrel */ + datum[1] = ObjectIdGetDatum(entryOid); /* inhparent */ + datum[2] = Int16GetDatum(seqNumber); /* inhseqno */ + + nullarr[0] = ' '; + nullarr[1] = ' '; + nullarr[2] = ' '; + + tuple = heap_formtuple(desc, datum, nullarr); + + heap_insert(relation, tuple); + + if (RelationGetForm(relation)->relhasindex) + { + Relation idescs[Num_pg_inherits_indices]; + + CatalogOpenIndices(Num_pg_inherits_indices, Name_pg_inherits_indices, idescs); + CatalogIndexInsert(idescs, Num_pg_inherits_indices, relation, tuple); + CatalogCloseIndices(Num_pg_inherits_indices, idescs); + } + + heap_freetuple(tuple); + + seqNumber += 1; + } + + heap_close(relation, RowExclusiveLock); + + /* ---------------- + * Expand supers list to include indirect ancestors as well. + * + * Algorithm: + * 0. begin with list of direct superclasses. + * 1. append after each relationId, its superclasses, recursively. + * 2. remove all but last of duplicates. + * ---------------- + */ + + /* + * 1. append after each relationId, its superclasses, recursively. + */ + foreach(entry, supers) + { + HeapTuple tuple; + Oid id; + int16 number; + List *next; + List *current; + + id = (Oid) lfirsti(entry); + current = entry; + next = lnext(entry); + + for (number = 1;; number += 1) + { + tuple = SearchSysCache(INHRELID, + ObjectIdGetDatum(id), + Int16GetDatum(number), + 0, 0); + if (!HeapTupleIsValid(tuple)) + break; + + lnext(current) = lconsi(((Form_pg_inherits) + GETSTRUCT(tuple))->inhparent, + NIL); + + ReleaseSysCache(tuple); + + current = lnext(current); + } + lnext(current) = next; + } + + /* + * 2. remove all but last of duplicates. + */ + foreach(entry, supers) + { + Oid thisone; + bool found; + List *rest; + +again: + thisone = lfirsti(entry); + found = false; + foreach(rest, lnext(entry)) + { + if (thisone == lfirsti(rest)) + { + found = true; + break; + } + } + if (found) + { + /* + * found a later duplicate, so remove this entry. + */ + lfirsti(entry) = lfirsti(lnext(entry)); + lnext(entry) = lnext(lnext(entry)); + + goto again; + } + } +} + +/* + * Look for an existing schema entry with the given name. + * + * Returns the index (starting with 1) if attribute already exists in schema, + * 0 if it doesn't. + */ +static int +findAttrByName(const char *attributeName, List *schema) +{ + List *s; + int i = 0; + + foreach(s, schema) + { + ColumnDef *def = lfirst(s); + + ++i; + if (strcmp(attributeName, def->colname) == 0) + return i; + } + return 0; +} + +/* + * Update a relation's pg_class.relhassubclass entry to the given value + */ +static void +setRelhassubclassInRelation(Oid relationId, bool relhassubclass) +{ + Relation relationRelation; + HeapTuple tuple; + Relation idescs[Num_pg_class_indices]; + + /* + * Fetch a modifiable copy of the tuple, modify it, update pg_class. + */ + relationRelation = heap_openr(RelationRelationName, RowExclusiveLock); + tuple = SearchSysCacheCopy(RELOID, + ObjectIdGetDatum(relationId), + 0, 0, 0); + if (!HeapTupleIsValid(tuple)) + elog(ERROR, "setRelhassubclassInRelation: cache lookup failed for relation %u", relationId); + + ((Form_pg_class) GETSTRUCT(tuple))->relhassubclass = relhassubclass; + simple_heap_update(relationRelation, &tuple->t_self, tuple); + + /* keep the catalog indices up to date */ + CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, idescs); + CatalogIndexInsert(idescs, Num_pg_class_indices, relationRelation, tuple); + CatalogCloseIndices(Num_pg_class_indices, idescs); + + heap_freetuple(tuple); + heap_close(relationRelation, RowExclusiveLock); +} + + +/* + * renameatt - changes the name of a attribute in a relation + * + * Attname attribute is changed in attribute catalog. + * No record of the previous attname is kept (correct?). + * + * get proper relrelation from relation catalog (if not arg) + * scan attribute catalog + * for name conflict (within rel) + * for original attribute (if not arg) + * modify attname in attribute tuple + * insert modified attribute in attribute catalog + * delete original attribute from attribute catalog + */ +void +renameatt(Oid relid, + const char *oldattname, + const char *newattname, + bool recurse) +{ + Relation targetrelation; + Relation attrelation; + HeapTuple atttup; + List *indexoidlist; + List *indexoidscan; + + /* + * Grab an exclusive lock on the target table, which we will NOT + * release until end of transaction. + */ + targetrelation = heap_open(relid, AccessExclusiveLock); + + /* + * permissions checking. this would normally be done in utility.c, + * but this particular routine is recursive. + * + * normally, only the owner of a class can change its schema. + */ + if (!allowSystemTableMods + && IsSystemRelation(targetrelation)) + elog(ERROR, "renameatt: class \"%s\" is a system catalog", + RelationGetRelationName(targetrelation)); + if (!pg_class_ownercheck(relid, GetUserId())) + elog(ERROR, "renameatt: you do not own class \"%s\"", + RelationGetRelationName(targetrelation)); + + /* + * if the 'recurse' flag is set then we are supposed to rename this + * attribute in all classes that inherit from 'relname' (as well as in + * 'relname'). + * + * any permissions or problems with duplicate attributes will cause the + * whole transaction to abort, which is what we want -- all or + * nothing. + */ + if (recurse) + { + List *child, + *children; + + /* this routine is actually in the planner */ + children = find_all_inheritors(relid); + + /* + * find_all_inheritors does the recursive search of the + * inheritance hierarchy, so all we have to do is process all of + * the relids in the list that it returns. + */ + foreach(child, children) + { + Oid childrelid = lfirsti(child); + + if (childrelid == relid) + continue; + /* note we need not recurse again! */ + renameatt(childrelid, oldattname, newattname, false); + } + } + + attrelation = heap_openr(AttributeRelationName, RowExclusiveLock); + + atttup = SearchSysCacheCopy(ATTNAME, + ObjectIdGetDatum(relid), + PointerGetDatum(oldattname), + 0, 0); + if (!HeapTupleIsValid(atttup)) + elog(ERROR, "renameatt: attribute \"%s\" does not exist", oldattname); + + if (((Form_pg_attribute) GETSTRUCT(atttup))->attnum < 0) + elog(ERROR, "renameatt: system attribute \"%s\" not renamed", oldattname); + + /* should not already exist */ + if (SearchSysCacheExists(ATTNAME, + ObjectIdGetDatum(relid), + PointerGetDatum(newattname), + 0, 0)) + elog(ERROR, "renameatt: attribute \"%s\" exists", newattname); + + StrNCpy(NameStr(((Form_pg_attribute) GETSTRUCT(atttup))->attname), + newattname, NAMEDATALEN); + + simple_heap_update(attrelation, &atttup->t_self, atttup); + + /* keep system catalog indices current */ + { + Relation irelations[Num_pg_attr_indices]; + + CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, irelations); + CatalogIndexInsert(irelations, Num_pg_attr_indices, attrelation, atttup); + CatalogCloseIndices(Num_pg_attr_indices, irelations); + } + + heap_freetuple(atttup); + + /* + * Update column names of indexes that refer to the column being + * renamed. + */ + indexoidlist = RelationGetIndexList(targetrelation); + + foreach(indexoidscan, indexoidlist) + { + Oid indexoid = lfirsti(indexoidscan); + HeapTuple indextup; + + /* + * First check to see if index is a functional index. If so, its + * column name is a function name and shouldn't be renamed here. + */ + indextup = SearchSysCache(INDEXRELID, + ObjectIdGetDatum(indexoid), + 0, 0, 0); + if (!HeapTupleIsValid(indextup)) + elog(ERROR, "renameatt: can't find index id %u", indexoid); + if (OidIsValid(((Form_pg_index) GETSTRUCT(indextup))->indproc)) + { + ReleaseSysCache(indextup); + continue; + } + ReleaseSysCache(indextup); + + /* + * Okay, look to see if any column name of the index matches the + * old attribute name. + */ + atttup = SearchSysCacheCopy(ATTNAME, + ObjectIdGetDatum(indexoid), + PointerGetDatum(oldattname), + 0, 0); + if (!HeapTupleIsValid(atttup)) + continue; /* Nope, so ignore it */ + + /* + * Update the (copied) attribute tuple. + */ + StrNCpy(NameStr(((Form_pg_attribute) GETSTRUCT(atttup))->attname), + newattname, NAMEDATALEN); + + simple_heap_update(attrelation, &atttup->t_self, atttup); + + /* keep system catalog indices current */ + { + Relation irelations[Num_pg_attr_indices]; + + CatalogOpenIndices(Num_pg_attr_indices, Name_pg_attr_indices, irelations); + CatalogIndexInsert(irelations, Num_pg_attr_indices, attrelation, atttup); + CatalogCloseIndices(Num_pg_attr_indices, irelations); + } + heap_freetuple(atttup); + } + + freeList(indexoidlist); + + heap_close(attrelation, RowExclusiveLock); + + /* + * Update att name in any RI triggers associated with the relation. + */ + if (targetrelation->rd_rel->reltriggers > 0) + { + /* update tgargs column reference where att is primary key */ + update_ri_trigger_args(RelationGetRelid(targetrelation), + oldattname, newattname, + false, false); + /* update tgargs column reference where att is foreign key */ + update_ri_trigger_args(RelationGetRelid(targetrelation), + oldattname, newattname, + true, false); + } + + heap_close(targetrelation, NoLock); /* close rel but keep lock! */ +} + +/* + * renamerel - change the name of a relation + * + * XXX - When renaming sequences, we don't bother to modify the + * sequence name that is stored within the sequence itself + * (this would cause problems with MVCC). In the future, + * the sequence name should probably be removed from the + * sequence, AFAIK there's no need for it to be there. + */ +void +renamerel(Oid relid, const char *newrelname) +{ + Relation targetrelation; + Relation relrelation; /* for RELATION relation */ + HeapTuple reltup; + Oid namespaceId; + char relkind; + bool relhastriggers; + Relation irelations[Num_pg_class_indices]; + + /* + * Grab an exclusive lock on the target table or index, which we will + * NOT release until end of transaction. + */ + targetrelation = relation_open(relid, AccessExclusiveLock); + + namespaceId = RelationGetNamespace(targetrelation); + + /* Validity checks */ + if (!allowSystemTableMods && + IsSystemRelation(targetrelation)) + elog(ERROR, "renamerel: system relation \"%s\" may not be renamed", + RelationGetRelationName(targetrelation)); + + relkind = targetrelation->rd_rel->relkind; + relhastriggers = (targetrelation->rd_rel->reltriggers > 0); + + /* + * Find relation's pg_class tuple, and make sure newrelname isn't in + * use. + */ + relrelation = heap_openr(RelationRelationName, RowExclusiveLock); + + reltup = SearchSysCacheCopy(RELOID, + PointerGetDatum(relid), + 0, 0, 0); + if (!HeapTupleIsValid(reltup)) + elog(ERROR, "renamerel: relation \"%s\" does not exist", + RelationGetRelationName(targetrelation)); + + if (get_relname_relid(newrelname, namespaceId) != InvalidOid) + elog(ERROR, "renamerel: relation \"%s\" exists", newrelname); + + /* + * Update pg_class tuple with new relname. (Scribbling on reltup is + * OK because it's a copy...) + */ + StrNCpy(NameStr(((Form_pg_class) GETSTRUCT(reltup))->relname), + newrelname, NAMEDATALEN); + + simple_heap_update(relrelation, &reltup->t_self, reltup); + + /* keep the system catalog indices current */ + CatalogOpenIndices(Num_pg_class_indices, Name_pg_class_indices, irelations); + CatalogIndexInsert(irelations, Num_pg_class_indices, relrelation, reltup); + CatalogCloseIndices(Num_pg_class_indices, irelations); + + heap_close(relrelation, NoLock); + heap_freetuple(reltup); + + /* + * Also rename the associated type, if any. + */ + if (relkind != RELKIND_INDEX) + TypeRename(RelationGetRelationName(targetrelation), namespaceId, + newrelname); + + /* + * If it's a view, must also rename the associated ON SELECT rule. + */ + if (relkind == RELKIND_VIEW) + { + char *oldrulename, + *newrulename; + + oldrulename = MakeRetrieveViewRuleName(RelationGetRelationName(targetrelation)); + newrulename = MakeRetrieveViewRuleName(newrelname); + RenameRewriteRule(oldrulename, newrulename); + } + + /* + * Update rel name in any RI triggers associated with the relation. + */ + if (relhastriggers) + { + /* update tgargs where relname is primary key */ + update_ri_trigger_args(relid, + RelationGetRelationName(targetrelation), + newrelname, + false, true); + /* update tgargs where relname is foreign key */ + update_ri_trigger_args(relid, + RelationGetRelationName(targetrelation), + newrelname, + true, true); + } + + /* + * Close rel, but keep exclusive lock! + */ + relation_close(targetrelation, NoLock); +} + +/* + * Given a trigger function OID, determine whether it is an RI trigger, + * and if so whether it is attached to PK or FK relation. + * + * XXX this probably doesn't belong here; should be exported by + * ri_triggers.c + */ +static int +ri_trigger_type(Oid tgfoid) +{ + switch (tgfoid) + { + case F_RI_FKEY_CASCADE_DEL: + case F_RI_FKEY_CASCADE_UPD: + case F_RI_FKEY_RESTRICT_DEL: + case F_RI_FKEY_RESTRICT_UPD: + case F_RI_FKEY_SETNULL_DEL: + case F_RI_FKEY_SETNULL_UPD: + case F_RI_FKEY_SETDEFAULT_DEL: + case F_RI_FKEY_SETDEFAULT_UPD: + case F_RI_FKEY_NOACTION_DEL: + case F_RI_FKEY_NOACTION_UPD: + return RI_TRIGGER_PK; + + case F_RI_FKEY_CHECK_INS: + case F_RI_FKEY_CHECK_UPD: + return RI_TRIGGER_FK; + } + + return RI_TRIGGER_NONE; +} + +/* + * Scan pg_trigger for RI triggers that are on the specified relation + * (if fk_scan is false) or have it as the tgconstrrel (if fk_scan + * is true). Update RI trigger args fields matching oldname to contain + * newname instead. If update_relname is true, examine the relname + * fields; otherwise examine the attname fields. + */ +static void +update_ri_trigger_args(Oid relid, + const char *oldname, + const char *newname, + bool fk_scan, + bool update_relname) +{ + Relation tgrel; + Relation irel; + ScanKeyData skey[1]; + IndexScanDesc idxtgscan; + RetrieveIndexResult idxres; + Datum values[Natts_pg_trigger]; + char nulls[Natts_pg_trigger]; + char replaces[Natts_pg_trigger]; + + tgrel = heap_openr(TriggerRelationName, RowExclusiveLock); + if (fk_scan) + irel = index_openr(TriggerConstrRelidIndex); + else + irel = index_openr(TriggerRelidIndex); + + ScanKeyEntryInitialize(&skey[0], 0x0, + 1, /* always column 1 of index */ + F_OIDEQ, + ObjectIdGetDatum(relid)); + idxtgscan = index_beginscan(irel, false, 1, skey); + + while ((idxres = index_getnext(idxtgscan, ForwardScanDirection)) != NULL) + { + HeapTupleData tupledata; + Buffer buffer; + HeapTuple tuple; + Form_pg_trigger pg_trigger; + bytea *val; + bytea *newtgargs; + bool isnull; + int tg_type; + bool examine_pk; + bool changed; + int tgnargs; + int i; + int newlen; + const char *arga[RI_MAX_ARGUMENTS]; + const char *argp; + + tupledata.t_self = idxres->heap_iptr; + heap_fetch(tgrel, SnapshotNow, &tupledata, &buffer, idxtgscan); + pfree(idxres); + if (!tupledata.t_data) + continue; + tuple = &tupledata; + pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple); + tg_type = ri_trigger_type(pg_trigger->tgfoid); + if (tg_type == RI_TRIGGER_NONE) + { + /* Not an RI trigger, forget it */ + ReleaseBuffer(buffer); + continue; + } + + /* + * It is an RI trigger, so parse the tgargs bytea. + * + * NB: we assume the field will never be compressed or moved out of + * line; so does trigger.c ... + */ + tgnargs = pg_trigger->tgnargs; + val = (bytea *) fastgetattr(tuple, + Anum_pg_trigger_tgargs, + tgrel->rd_att, &isnull); + if (isnull || tgnargs < RI_FIRST_ATTNAME_ARGNO || + tgnargs > RI_MAX_ARGUMENTS) + { + /* This probably shouldn't happen, but ignore busted triggers */ + ReleaseBuffer(buffer); + continue; + } + argp = (const char *) VARDATA(val); + for (i = 0; i < tgnargs; i++) + { + arga[i] = argp; + argp += strlen(argp) + 1; + } + + /* + * Figure out which item(s) to look at. If the trigger is + * primary-key type and attached to my rel, I should look at the + * PK fields; if it is foreign-key type and attached to my rel, I + * should look at the FK fields. But the opposite rule holds when + * examining triggers found by tgconstrrel search. + */ + examine_pk = (tg_type == RI_TRIGGER_PK) == (!fk_scan); + + changed = false; + if (update_relname) + { + /* Change the relname if needed */ + i = examine_pk ? RI_PK_RELNAME_ARGNO : RI_FK_RELNAME_ARGNO; + if (strcmp(arga[i], oldname) == 0) + { + arga[i] = newname; + changed = true; + } + } + else + { + /* Change attname(s) if needed */ + i = examine_pk ? RI_FIRST_ATTNAME_ARGNO + RI_KEYPAIR_PK_IDX : + RI_FIRST_ATTNAME_ARGNO + RI_KEYPAIR_FK_IDX; + for (; i < tgnargs; i += 2) + { + if (strcmp(arga[i], oldname) == 0) + { + arga[i] = newname; + changed = true; + } + } + } + + if (!changed) + { + /* Don't need to update this tuple */ + ReleaseBuffer(buffer); + continue; + } + + /* + * Construct modified tgargs bytea. + */ + newlen = VARHDRSZ; + for (i = 0; i < tgnargs; i++) + newlen += strlen(arga[i]) + 1; + newtgargs = (bytea *) palloc(newlen); + VARATT_SIZEP(newtgargs) = newlen; + newlen = VARHDRSZ; + for (i = 0; i < tgnargs; i++) + { + strcpy(((char *) newtgargs) + newlen, arga[i]); + newlen += strlen(arga[i]) + 1; + } + + /* + * Build modified tuple. + */ + for (i = 0; i < Natts_pg_trigger; i++) + { + values[i] = (Datum) 0; + replaces[i] = ' '; + nulls[i] = ' '; + } + values[Anum_pg_trigger_tgargs - 1] = PointerGetDatum(newtgargs); + replaces[Anum_pg_trigger_tgargs - 1] = 'r'; + + tuple = heap_modifytuple(tuple, tgrel, values, nulls, replaces); + + /* + * Now we can release hold on original tuple. + */ + ReleaseBuffer(buffer); + + /* + * Update pg_trigger and its indexes + */ + simple_heap_update(tgrel, &tuple->t_self, tuple); + + { + Relation irelations[Num_pg_attr_indices]; + + CatalogOpenIndices(Num_pg_trigger_indices, Name_pg_trigger_indices, irelations); + CatalogIndexInsert(irelations, Num_pg_trigger_indices, tgrel, tuple); + CatalogCloseIndices(Num_pg_trigger_indices, irelations); + } + + /* free up our scratch memory */ + pfree(newtgargs); + heap_freetuple(tuple); + } + + index_endscan(idxtgscan); + index_close(irel); + + heap_close(tgrel, RowExclusiveLock); + + /* + * Increment cmd counter to make updates visible; this is needed in + * case the same tuple has to be updated again by next pass (can + * happen in case of a self-referential FK relationship). + */ + CommandCounterIncrement(); } diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c new file mode 100644 index 0000000000000000000000000000000000000000..13dbf04aaacf6e30b9e2007a652521aeebd56092 --- /dev/null +++ b/src/backend/commands/typecmds.c @@ -0,0 +1,660 @@ +/*------------------------------------------------------------------------- + * + * typecmds.c + * Routines for SQL commands that manipulate types (and domains). + * + * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.1 2002/04/15 05:22:03 tgl Exp $ + * + * DESCRIPTION + * The "DefineFoo" routines take the parse tree and pick out the + * appropriate arguments/flags, passing the results to the + * corresponding "FooDefine" routines (in src/catalog) that do + * the actual catalog-munging. These routines also verify permission + * of the user to execute the command. + * + * NOTES + * These things must be defined and committed in the following order: + * "create function": + * input/output, recv/send procedures + * "create type": + * type + * "create operator": + * operators + * + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/heapam.h" +#include "catalog/catname.h" +#include "catalog/heap.h" +#include "catalog/namespace.h" +#include "catalog/pg_type.h" +#include "commands/comment.h" +#include "commands/defrem.h" +#include "miscadmin.h" +#include "parser/parse.h" +#include "parser/parse_func.h" +#include "parser/parse_type.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/fmgroids.h" +#include "utils/syscache.h" + + +static Oid findTypeIOFunction(List *procname, bool isOutput); + + +/* + * DefineType + * Registers a new type. + */ +void +DefineType(List *names, List *parameters) +{ + char *typeName; + Oid typeNamespace; + int16 internalLength = -1; /* int2 */ + int16 externalLength = -1; /* int2 */ + Oid elemType = InvalidOid; + List *inputName = NIL; + List *outputName = NIL; + List *sendName = NIL; + List *receiveName = NIL; + char *defaultValue = NULL; + bool byValue = false; + char delimiter = DEFAULT_TYPDELIM; + char alignment = 'i'; /* default alignment */ + char storage = 'p'; /* default TOAST storage method */ + Oid inputOid; + Oid outputOid; + Oid sendOid; + Oid receiveOid; + char *shadow_type; + List *pl; + Oid typoid; + + /* Convert list of names to a name and namespace */ + typeNamespace = QualifiedNameGetCreationNamespace(names, &typeName); + + /* + * Type names must be one character shorter than other names, allowing + * room to create the corresponding array type name with prepended + * "_". + */ + if (strlen(typeName) > (NAMEDATALEN - 2)) + elog(ERROR, "DefineType: type names must be %d characters or less", + NAMEDATALEN - 2); + + foreach(pl, parameters) + { + DefElem *defel = (DefElem *) lfirst(pl); + + if (strcasecmp(defel->defname, "internallength") == 0) + internalLength = defGetTypeLength(defel); + else if (strcasecmp(defel->defname, "externallength") == 0) + externalLength = defGetTypeLength(defel); + else if (strcasecmp(defel->defname, "input") == 0) + inputName = defGetQualifiedName(defel); + else if (strcasecmp(defel->defname, "output") == 0) + outputName = defGetQualifiedName(defel); + else if (strcasecmp(defel->defname, "send") == 0) + sendName = defGetQualifiedName(defel); + else if (strcasecmp(defel->defname, "receive") == 0) + receiveName = defGetQualifiedName(defel); + else if (strcasecmp(defel->defname, "delimiter") == 0) + { + char *p = defGetString(defel); + + delimiter = p[0]; + } + else if (strcasecmp(defel->defname, "element") == 0) + elemType = typenameTypeId(defGetTypeName(defel)); + else if (strcasecmp(defel->defname, "default") == 0) + defaultValue = defGetString(defel); + else if (strcasecmp(defel->defname, "passedbyvalue") == 0) + byValue = true; + else if (strcasecmp(defel->defname, "alignment") == 0) + { + char *a = defGetString(defel); + + /* + * Note: if argument was an unquoted identifier, parser will + * have applied xlateSqlType() to it, so be prepared to + * recognize translated type names as well as the nominal + * form. + */ + if (strcasecmp(a, "double") == 0) + alignment = 'd'; + else if (strcasecmp(a, "float8") == 0) + alignment = 'd'; + else if (strcasecmp(a, "int4") == 0) + alignment = 'i'; + else if (strcasecmp(a, "int2") == 0) + alignment = 's'; + else if (strcasecmp(a, "char") == 0) + alignment = 'c'; + else if (strcasecmp(a, "bpchar") == 0) + alignment = 'c'; + else + elog(ERROR, "DefineType: \"%s\" alignment not recognized", + a); + } + else if (strcasecmp(defel->defname, "storage") == 0) + { + char *a = defGetString(defel); + + if (strcasecmp(a, "plain") == 0) + storage = 'p'; + else if (strcasecmp(a, "external") == 0) + storage = 'e'; + else if (strcasecmp(a, "extended") == 0) + storage = 'x'; + else if (strcasecmp(a, "main") == 0) + storage = 'm'; + else + elog(ERROR, "DefineType: \"%s\" storage not recognized", + a); + } + else + { + elog(WARNING, "DefineType: attribute \"%s\" not recognized", + defel->defname); + } + } + + /* + * make sure we have our required definitions + */ + if (inputName == NIL) + elog(ERROR, "Define: \"input\" unspecified"); + if (outputName == NIL) + elog(ERROR, "Define: \"output\" unspecified"); + + /* Convert I/O proc names to OIDs */ + inputOid = findTypeIOFunction(inputName, false); + outputOid = findTypeIOFunction(outputName, true); + if (sendName) + sendOid = findTypeIOFunction(sendName, true); + else + sendOid = outputOid; + if (receiveName) + receiveOid = findTypeIOFunction(receiveName, false); + else + receiveOid = inputOid; + + /* + * now have TypeCreate do all the real work. + */ + typoid = + TypeCreate(typeName, /* type name */ + typeNamespace, /* namespace */ + InvalidOid, /* preassigned type oid (not done here) */ + InvalidOid, /* relation oid (n/a here) */ + internalLength, /* internal size */ + externalLength, /* external size */ + 'b', /* type-type (base type) */ + delimiter, /* array element delimiter */ + inputOid, /* input procedure */ + outputOid, /* output procedure */ + receiveOid, /* receive procedure */ + sendOid, /* send procedure */ + elemType, /* element type ID */ + InvalidOid, /* base type ID (only for domains) */ + defaultValue, /* default type value */ + NULL, /* no binary form available */ + byValue, /* passed by value */ + alignment, /* required alignment */ + storage, /* TOAST strategy */ + -1, /* typMod (Domains only) */ + 0, /* Array Dimensions of typbasetype */ + false); /* Type NOT NULL */ + + /* + * When we create a base type (as opposed to a complex type) we need + * to have an array entry for it in pg_type as well. + */ + shadow_type = makeArrayTypeName(typeName); + + /* alignment must be 'i' or 'd' for arrays */ + alignment = (alignment == 'd') ? 'd' : 'i'; + + TypeCreate(shadow_type, /* type name */ + typeNamespace, /* namespace */ + InvalidOid, /* preassigned type oid (not done here) */ + InvalidOid, /* relation oid (n/a here) */ + -1, /* internal size */ + -1, /* external size */ + 'b', /* type-type (base type) */ + DEFAULT_TYPDELIM, /* array element delimiter */ + F_ARRAY_IN, /* input procedure */ + F_ARRAY_OUT, /* output procedure */ + F_ARRAY_IN, /* receive procedure */ + F_ARRAY_OUT, /* send procedure */ + typoid, /* element type ID */ + InvalidOid, /* base type ID */ + NULL, /* never a default type value */ + NULL, /* binary default isn't sent either */ + false, /* never passed by value */ + alignment, /* see above */ + 'x', /* ARRAY is always toastable */ + -1, /* typMod (Domains only) */ + 0, /* Array dimensions of typbasetype */ + false); /* Type NOT NULL */ + + pfree(shadow_type); +} + + +/* + * RemoveType + * Removes a datatype. + * + * NOTE: since this tries to remove the associated array type too, it'll + * only work on scalar types. + */ +void +RemoveType(List *names) +{ + TypeName *typename; + Relation relation; + Oid typeoid; + HeapTuple tup; + + /* Make a TypeName so we can use standard type lookup machinery */ + typename = makeNode(TypeName); + typename->names = names; + typename->typmod = -1; + typename->arrayBounds = NIL; + + relation = heap_openr(TypeRelationName, RowExclusiveLock); + + /* Use LookupTypeName here so that shell types can be removed. */ + typeoid = LookupTypeName(typename); + if (!OidIsValid(typeoid)) + elog(ERROR, "Type \"%s\" does not exist", + TypeNameToString(typename)); + + tup = SearchSysCache(TYPEOID, + ObjectIdGetDatum(typeoid), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "Type \"%s\" does not exist", + TypeNameToString(typename)); + + if (!pg_type_ownercheck(typeoid, GetUserId())) + elog(ERROR, "RemoveType: type '%s': permission denied", + TypeNameToString(typename)); + + /* Delete any comments associated with this type */ + DeleteComments(typeoid, RelationGetRelid(relation)); + + /* Remove the type tuple from pg_type */ + simple_heap_delete(relation, &tup->t_self); + + ReleaseSysCache(tup); + + /* Now, delete the "array of" that type */ + typename->arrayBounds = makeList1(makeInteger(1)); + + typeoid = LookupTypeName(typename); + if (!OidIsValid(typeoid)) + elog(ERROR, "Type \"%s\" does not exist", + TypeNameToString(typename)); + + tup = SearchSysCache(TYPEOID, + ObjectIdGetDatum(typeoid), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "Type \"%s\" does not exist", + TypeNameToString(typename)); + + DeleteComments(typeoid, RelationGetRelid(relation)); + + simple_heap_delete(relation, &tup->t_self); + + ReleaseSysCache(tup); + + heap_close(relation, RowExclusiveLock); +} + + +/* + * DefineDomain + * Registers a new domain. + */ +void +DefineDomain(CreateDomainStmt *stmt) +{ + char *domainName; + Oid domainNamespace; + int16 internalLength; + int16 externalLength; + Oid inputProcedure; + Oid outputProcedure; + Oid receiveProcedure; + Oid sendProcedure; + bool byValue; + char delimiter; + char alignment; + char storage; + char typtype; + Datum datum; + bool isnull; + char *defaultValue = NULL; + char *defaultValueBin = NULL; + bool typNotNull = false; + Oid basetypelem; + int32 typNDims = length(stmt->typename->arrayBounds); + HeapTuple typeTup; + List *schema = stmt->constraints; + List *listptr; + + /* Convert list of names to a name and namespace */ + domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname, + &domainName); + + /* + * Domainnames, unlike typenames don't need to account for the '_' + * prefix. So they can be one character longer. + */ + if (strlen(domainName) > (NAMEDATALEN - 1)) + elog(ERROR, "CREATE DOMAIN: domain names must be %d characters or less", + NAMEDATALEN - 1); + + /* + * Look up the base type. + */ + typeTup = typenameType(stmt->typename); + + /* + * What we really don't want is domains of domains. This could cause all sorts + * of neat issues if we allow that. + * + * With testing, we may determine complex types should be allowed + */ + typtype = ((Form_pg_type) GETSTRUCT(typeTup))->typtype; + if (typtype != 'b') + elog(ERROR, "DefineDomain: %s is not a basetype", + TypeNameToString(stmt->typename)); + + /* passed by value */ + byValue = ((Form_pg_type) GETSTRUCT(typeTup))->typbyval; + + /* Required Alignment */ + alignment = ((Form_pg_type) GETSTRUCT(typeTup))->typalign; + + /* TOAST Strategy */ + storage = ((Form_pg_type) GETSTRUCT(typeTup))->typstorage; + + /* Storage Length */ + internalLength = ((Form_pg_type) GETSTRUCT(typeTup))->typlen; + + /* External Length (unused) */ + externalLength = ((Form_pg_type) GETSTRUCT(typeTup))->typprtlen; + + /* Array element Delimiter */ + delimiter = ((Form_pg_type) GETSTRUCT(typeTup))->typdelim; + + /* I/O Functions */ + inputProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typinput; + outputProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typoutput; + receiveProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typreceive; + sendProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typsend; + + /* Inherited default value */ + datum = SysCacheGetAttr(TYPEOID, typeTup, + Anum_pg_type_typdefault, &isnull); + if (!isnull) + defaultValue = DatumGetCString(DirectFunctionCall1(textout, datum)); + + /* Inherited default binary value */ + datum = SysCacheGetAttr(TYPEOID, typeTup, + Anum_pg_type_typdefaultbin, &isnull); + if (!isnull) + defaultValueBin = DatumGetCString(DirectFunctionCall1(textout, datum)); + + /* + * Pull out the typelem name of the parent OID. + * + * This is what enables us to make a domain of an array + */ + basetypelem = ((Form_pg_type) GETSTRUCT(typeTup))->typelem; + + /* + * Run through constraints manually to avoid the additional + * processing conducted by DefineRelation() and friends. + * + * Besides, we don't want any constraints to be cooked. We'll + * do that when the table is created via MergeDomainAttributes(). + */ + foreach(listptr, schema) + { + Constraint *colDef = lfirst(listptr); + bool nullDefined = false; + Node *expr; + ParseState *pstate; + + switch (colDef->contype) + { + /* + * The inherited default value may be overridden by the user + * with the DEFAULT <expr> statement. + * + * We have to search the entire constraint tree returned as we + * don't want to cook or fiddle too much. + */ + case CONSTR_DEFAULT: + /* Create a dummy ParseState for transformExpr */ + pstate = make_parsestate(NULL); + /* + * Cook the colDef->raw_expr into an expression. + * Note: Name is strictly for error message + */ + expr = cookDefault(pstate, colDef->raw_expr, + typeTup->t_data->t_oid, + stmt->typename->typmod, + domainName); + /* + * Expression must be stored as a nodeToString result, + * but we also require a valid textual representation + * (mainly to make life easier for pg_dump). + */ + defaultValue = deparse_expression(expr, + deparse_context_for(domainName, + InvalidOid), + false); + defaultValueBin = nodeToString(expr); + break; + + /* + * Find the NULL constraint. + */ + case CONSTR_NOTNULL: + if (nullDefined) { + elog(ERROR, "CREATE DOMAIN has conflicting NULL / NOT NULL constraint"); + } else { + typNotNull = true; + nullDefined = true; + } + break; + + case CONSTR_NULL: + if (nullDefined) { + elog(ERROR, "CREATE DOMAIN has conflicting NULL / NOT NULL constraint"); + } else { + typNotNull = false; + nullDefined = true; + } + break; + + case CONSTR_UNIQUE: + elog(ERROR, "CREATE DOMAIN / UNIQUE indexes not supported"); + break; + + case CONSTR_PRIMARY: + elog(ERROR, "CREATE DOMAIN / PRIMARY KEY indexes not supported"); + break; + + case CONSTR_CHECK: + elog(ERROR, "DefineDomain: CHECK Constraints not supported"); + break; + + case CONSTR_ATTR_DEFERRABLE: + case CONSTR_ATTR_NOT_DEFERRABLE: + case CONSTR_ATTR_DEFERRED: + case CONSTR_ATTR_IMMEDIATE: + elog(ERROR, "DefineDomain: DEFERRABLE, NON DEFERRABLE, DEFERRED and IMMEDIATE not supported"); + break; + + default: + elog(ERROR, "DefineDomain: unrecognized constraint node type"); + break; + } + } + + /* + * Have TypeCreate do all the real work. + */ + TypeCreate(domainName, /* type name */ + domainNamespace, /* namespace */ + InvalidOid, /* preassigned type oid (not done here) */ + InvalidOid, /* relation oid (n/a here) */ + internalLength, /* internal size */ + externalLength, /* external size */ + 'd', /* type-type (domain type) */ + delimiter, /* array element delimiter */ + inputProcedure, /* input procedure */ + outputProcedure, /* output procedure */ + receiveProcedure, /* receive procedure */ + sendProcedure, /* send procedure */ + basetypelem, /* element type ID */ + typeTup->t_data->t_oid, /* base type ID */ + defaultValue, /* default type value (text) */ + defaultValueBin, /* default type value (binary) */ + byValue, /* passed by value */ + alignment, /* required alignment */ + storage, /* TOAST strategy */ + stmt->typename->typmod, /* typeMod value */ + typNDims, /* Array dimensions for base type */ + typNotNull); /* Type NOT NULL */ + + /* + * Now we can clean up. + */ + ReleaseSysCache(typeTup); +} + + +/* + * RemoveDomain + * Removes a domain. + */ +void +RemoveDomain(List *names, int behavior) +{ + TypeName *typename; + Relation relation; + Oid typeoid; + HeapTuple tup; + char typtype; + + /* CASCADE unsupported */ + if (behavior == CASCADE) + elog(ERROR, "DROP DOMAIN does not support the CASCADE keyword"); + + /* Make a TypeName so we can use standard type lookup machinery */ + typename = makeNode(TypeName); + typename->names = names; + typename->typmod = -1; + typename->arrayBounds = NIL; + + relation = heap_openr(TypeRelationName, RowExclusiveLock); + + typeoid = typenameTypeId(typename); + + tup = SearchSysCache(TYPEOID, + ObjectIdGetDatum(typeoid), + 0, 0, 0); + if (!HeapTupleIsValid(tup)) + elog(ERROR, "RemoveDomain: type '%s' does not exist", + TypeNameToString(typename)); + + if (!pg_type_ownercheck(typeoid, GetUserId())) + elog(ERROR, "RemoveDomain: type '%s': permission denied", + TypeNameToString(typename)); + + /* Check that this is actually a domain */ + typtype = ((Form_pg_type) GETSTRUCT(tup))->typtype; + + if (typtype != 'd') + elog(ERROR, "%s is not a domain", + TypeNameToString(typename)); + + /* Delete any comments associated with this type */ + DeleteComments(typeoid, RelationGetRelid(relation)); + + /* Remove the type tuple from pg_type */ + simple_heap_delete(relation, &tup->t_self); + + ReleaseSysCache(tup); + + /* At present, domains don't have associated array types */ + + heap_close(relation, RowExclusiveLock); +} + + +/* + * Find a suitable I/O function for a type. + */ +static Oid +findTypeIOFunction(List *procname, bool isOutput) +{ + Oid argList[FUNC_MAX_ARGS]; + int nargs; + Oid procOid; + + /* + * First look for a 1-argument func with all argtypes 0. This is + * valid for all kinds of procedure. + */ + MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid)); + + procOid = LookupFuncName(procname, 1, argList); + + if (!OidIsValid(procOid)) + { + /* + * Alternatively, input procedures may take 3 args (data + * value, element OID, atttypmod); the pg_proc argtype + * signature is 0,OIDOID,INT4OID. Output procedures may + * take 2 args (data value, element OID). + */ + if (isOutput) + { + /* output proc */ + nargs = 2; + argList[1] = OIDOID; + } + else + { + /* input proc */ + nargs = 3; + argList[1] = OIDOID; + argList[2] = INT4OID; + } + procOid = LookupFuncName(procname, nargs, argList); + + if (!OidIsValid(procOid)) + func_error("TypeCreate", procname, 1, argList, NULL); + } + + return procOid; +} diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 6b8652db5b72e5c4adba96046a00dad33917600c..bb4f2185b08baac403fedd2e693812864714a18c 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: view.c,v 1.61 2002/03/29 19:06:08 tgl Exp $ + * $Id: view.c,v 1.62 2002/04/15 05:22:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -15,7 +15,7 @@ #include "access/xact.h" #include "catalog/heap.h" #include "catalog/namespace.h" -#include "commands/creatinh.h" +#include "commands/tablecmds.h" #include "commands/view.h" #include "miscadmin.h" #include "nodes/makefuncs.h" diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 659ab63f5536dfd4ff2f65b6b95e50123ba6bfa4..a2045a9eb80e2dc808020df999865d9932defdd5 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -27,7 +27,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.157 2002/04/08 22:42:18 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.158 2002/04/15 05:22:04 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -36,7 +36,7 @@ #include "access/heapam.h" #include "catalog/heap.h" #include "catalog/namespace.h" -#include "commands/command.h" +#include "commands/tablecmds.h" #include "commands/trigger.h" #include "executor/execdebug.h" #include "executor/execdefs.h" diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index f09f90744e8fcae68cf416368dccadc3295d56af..b9bcce30e29acab992940a0f59ff650b7d638349 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.68 2002/03/21 16:00:38 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.69 2002/04/15 05:22:04 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -16,7 +16,7 @@ #include "access/printtup.h" #include "catalog/heap.h" -#include "commands/command.h" +#include "commands/portalcmds.h" #include "executor/spi_priv.h" #include "tcop/tcopprot.h" #include "utils/lsyscache.h" diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c index 4599a27a6428512fd8c4b634270373e8c621299b..e8df96a53defeb4bc6e26b6a6674f36f0745a2a3 100644 --- a/src/backend/tcop/pquery.c +++ b/src/backend/tcop/pquery.c @@ -8,14 +8,14 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.51 2002/03/21 16:01:27 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.52 2002/04/15 05:22:04 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" -#include "commands/command.h" +#include "commands/portalcmds.h" #include "executor/execdefs.h" #include "executor/executor.h" #include "tcop/pquery.h" diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index 77dfe4a23651b9124ea60ce1bfb3673ae81dc3af..63e6b5dd9b2184ccc8a453b0d2c6bade4644696b 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.148 2002/04/12 20:38:27 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.149 2002/04/15 05:22:04 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -22,16 +22,17 @@ #include "catalog/pg_shadow.h" #include "commands/async.h" #include "commands/cluster.h" -#include "commands/command.h" #include "commands/comment.h" #include "commands/copy.h" -#include "commands/creatinh.h" #include "commands/dbcommands.h" #include "commands/defrem.h" #include "commands/explain.h" +#include "commands/lockcmds.h" +#include "commands/portalcmds.h" #include "commands/proclang.h" -#include "commands/rename.h" +#include "commands/schemacmds.h" #include "commands/sequence.h" +#include "commands/tablecmds.h" #include "commands/trigger.h" #include "commands/user.h" #include "commands/vacuum.h" diff --git a/src/include/commands/command.h b/src/include/commands/command.h deleted file mode 100644 index cfb8f20890eb7117e7aa0099ee9db56ff2ab66f9..0000000000000000000000000000000000000000 --- a/src/include/commands/command.h +++ /dev/null @@ -1,81 +0,0 @@ -/*------------------------------------------------------------------------- - * - * command.h - * prototypes for command.c. - * - * - * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * $Id: command.h,v 1.37 2002/04/01 04:35:39 tgl Exp $ - * - *------------------------------------------------------------------------- - */ -#ifndef COMMAND_H -#define COMMAND_H - -#include "utils/portal.h" - - -/* - * PerformPortalFetch - * Performs the POSTQUEL function FETCH. Fetches count (or all if 0) - * tuples in portal with name in the forward direction iff goForward. - * - * Exceptions: - * BadArg if forward invalid. - * "ERROR" if portal not found. - */ -extern void PerformPortalFetch(char *name, bool forward, int count, - CommandDest dest, char *completionTag); - -/* - * PerformPortalClose - * Performs the POSTQUEL function CLOSE. - */ -extern void PerformPortalClose(char *name, CommandDest dest); - -extern void PortalCleanup(Portal portal); - -/* - * ALTER TABLE variants - */ -extern void AlterTableAddColumn(Oid myrelid, bool inherits, ColumnDef *colDef); - -extern void AlterTableAlterColumnDefault(Oid myrelid, bool inh, - const char *colName, Node *newDefault); - -extern void AlterTableAlterColumnDropNotNull(Oid myrelid, - bool inh, const char *colName); - -extern void AlterTableAlterColumnSetNotNull(Oid myrelid, - bool inh, const char *colName); - -extern void AlterTableAlterColumnFlags(Oid myrelid, - bool inh, const char *colName, - Node *flagValue, const char *flagType); - -extern void AlterTableDropColumn(Oid myrelid, bool inh, - const char *colName, int behavior); - -extern void AlterTableAddConstraint(Oid myrelid, - bool inh, List *newConstraints); - -extern void AlterTableDropConstraint(Oid myrelid, - bool inh, const char *constrName, int behavior); - -extern void AlterTableCreateToastTable(Oid relOid, bool silent); - -extern void AlterTableOwner(Oid relationOid, int32 newOwnerSysId); - -/* - * LOCK - */ -extern void LockTableCommand(LockStmt *lockstmt); - -/* - * SCHEMA - */ -extern void CreateSchemaCommand(CreateSchemaStmt *parsetree); - -#endif /* COMMAND_H */ diff --git a/src/include/commands/creatinh.h b/src/include/commands/creatinh.h deleted file mode 100644 index fe0f2bc35128a16f6e7c2664f149f460a089236d..0000000000000000000000000000000000000000 --- a/src/include/commands/creatinh.h +++ /dev/null @@ -1,23 +0,0 @@ -/*------------------------------------------------------------------------- - * - * creatinh.h - * prototypes for creatinh.c. - * - * - * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * $Id: creatinh.h,v 1.20 2002/03/29 19:06:22 tgl Exp $ - * - *------------------------------------------------------------------------- - */ -#ifndef CREATINH_H -#define CREATINH_H - -#include "nodes/parsenodes.h" - -extern Oid DefineRelation(CreateStmt *stmt, char relkind); -extern void RemoveRelation(const RangeVar *relation); -extern void TruncateRelation(const RangeVar *relation); - -#endif /* CREATINH_H */ diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h index 83b8fec6d77cb05cc6e06f9561186fcf7231181d..711bc91f57993f3a37e29795635e466e698bc35b 100644 --- a/src/include/commands/defrem.h +++ b/src/include/commands/defrem.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: defrem.h,v 1.34 2002/04/09 20:35:54 tgl Exp $ + * $Id: defrem.h,v 1.35 2002/04/15 05:22:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -16,6 +16,8 @@ #include "nodes/parsenodes.h" +#define DEFAULT_TYPDELIM ',' + /* * prototypes in indexcmds.c */ @@ -33,22 +35,33 @@ extern void ReindexTable(RangeVar *relation, bool force); extern void ReindexDatabase(const char *databaseName, bool force, bool all); /* - * prototypes in define.c + * DefineFoo and RemoveFoo are now both in foocmds.c */ + extern void CreateFunction(ProcedureStmt *stmt); +extern void RemoveFunction(List *functionName, List *argTypes); + extern void DefineOperator(List *names, List *parameters); +extern void RemoveOperator(char *operatorName, + TypeName *typeName1, TypeName *typeName2); + extern void DefineAggregate(List *names, List *parameters); +extern void RemoveAggregate(List *aggName, TypeName *aggType); + extern void DefineType(List *names, List *parameters); +extern void RemoveType(List *names); extern void DefineDomain(CreateDomainStmt *stmt); - -/* - * prototypes in remove.c - */ extern void RemoveDomain(List *names, int behavior); -extern void RemoveFunction(List *functionName, List *argTypes); -extern void RemoveOperator(char *operatorName, - TypeName *typeName1, TypeName *typeName2); -extern void RemoveType(List *names); -extern void RemoveAggregate(List *aggName, TypeName *aggType); + + +/* support routines in define.c */ + +extern void case_translate_language_name(const char *input, char *output); + +extern char *defGetString(DefElem *def); +extern double defGetNumeric(DefElem *def); +extern List *defGetQualifiedName(DefElem *def); +extern TypeName *defGetTypeName(DefElem *def); +extern int defGetTypeLength(DefElem *def); #endif /* DEFREM_H */ diff --git a/src/include/commands/lockcmds.h b/src/include/commands/lockcmds.h new file mode 100644 index 0000000000000000000000000000000000000000..04335c31c4e01ba7c56ba767ad6749dc2dc493d1 --- /dev/null +++ b/src/include/commands/lockcmds.h @@ -0,0 +1,24 @@ +/*------------------------------------------------------------------------- + * + * lockcmds.h + * prototypes for lockcmds.c. + * + * + * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * $Id: lockcmds.h,v 1.1 2002/04/15 05:22:03 tgl Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef LOCKCMDS_H +#define LOCKCMDS_H + +#include "nodes/parsenodes.h" + +/* + * LOCK + */ +extern void LockTableCommand(LockStmt *lockstmt); + +#endif /* LOCKCMDS_H */ diff --git a/src/include/commands/portalcmds.h b/src/include/commands/portalcmds.h new file mode 100644 index 0000000000000000000000000000000000000000..b62e3638a9133b0c7faa124c161d31f72132a7ea --- /dev/null +++ b/src/include/commands/portalcmds.h @@ -0,0 +1,39 @@ +/*------------------------------------------------------------------------- + * + * portalcmds.h + * prototypes for portalcmds.c. + * + * + * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * $Id: portalcmds.h,v 1.1 2002/04/15 05:22:03 tgl Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef PORTALCMDS_H +#define PORTALCMDS_H + +#include "utils/portal.h" + +/* + * PerformPortalFetch + * Performs the POSTQUEL function FETCH. Fetches count (or all if 0) + * tuples in portal with name in the forward direction iff goForward. + * + * Exceptions: + * BadArg if forward invalid. + * "ERROR" if portal not found. + */ +extern void PerformPortalFetch(char *name, bool forward, int count, + CommandDest dest, char *completionTag); + +/* + * PerformPortalClose + * Performs the POSTQUEL function CLOSE. + */ +extern void PerformPortalClose(char *name, CommandDest dest); + +extern void PortalCleanup(Portal portal); + +#endif /* PORTALCMDS_H */ diff --git a/src/include/commands/rename.h b/src/include/commands/rename.h deleted file mode 100644 index 48e1a1cbe0dbfb205f85a3e7a83d166996d3790a..0000000000000000000000000000000000000000 --- a/src/include/commands/rename.h +++ /dev/null @@ -1,25 +0,0 @@ -/*------------------------------------------------------------------------- - * - * rename.h - * - * - * - * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * $Id: rename.h,v 1.16 2002/03/31 07:49:30 tgl Exp $ - * - *------------------------------------------------------------------------- - */ -#ifndef RENAME_H -#define RENAME_H - -extern void renameatt(Oid relid, - const char *oldattname, - const char *newattname, - bool recurse); - -extern void renamerel(Oid relid, - const char *newrelname); - -#endif /* RENAME_H */ diff --git a/src/include/commands/schemacmds.h b/src/include/commands/schemacmds.h new file mode 100644 index 0000000000000000000000000000000000000000..6adf9a4517ea0357520cb7858d8957b0d8171865 --- /dev/null +++ b/src/include/commands/schemacmds.h @@ -0,0 +1,22 @@ +/*------------------------------------------------------------------------- + * + * schemacmds.h + * prototypes for schemacmds.c. + * + * + * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * $Id: schemacmds.h,v 1.1 2002/04/15 05:22:04 tgl Exp $ + * + *------------------------------------------------------------------------- + */ + +#ifndef SCHEMACMDS_H +#define SCHEMACMDS_H + +#include "nodes/parsenodes.h" + +extern void CreateSchemaCommand(CreateSchemaStmt *parsetree); + +#endif /* SCHEMACMDS_H */ diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h new file mode 100644 index 0000000000000000000000000000000000000000..5d895972f5b069fbabcbf521ff366a594ecdb906 --- /dev/null +++ b/src/include/commands/tablecmds.h @@ -0,0 +1,63 @@ +/*------------------------------------------------------------------------- + * + * tablecmds.h + * prototypes for tablecmds.c. + * + * + * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * $Id: tablecmds.h,v 1.1 2002/04/15 05:22:04 tgl Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef TABLECMDS_H +#define TABLECMDS_H + +#include "nodes/parsenodes.h" + +extern void AlterTableAddColumn(Oid myrelid, bool inherits, + ColumnDef *colDef); + +extern void AlterTableAlterColumnDefault(Oid myrelid, bool inh, + const char *colName, + Node *newDefault); + +extern void AlterTableAlterColumnDropNotNull(Oid myrelid, bool inh, + const char *colName); + +extern void AlterTableAlterColumnSetNotNull(Oid myrelid, bool inh, + const char *colName); + +extern void AlterTableAlterColumnFlags(Oid myrelid, bool inh, + const char *colName, + Node *flagValue, const char *flagType); + +extern void AlterTableDropColumn(Oid myrelid, bool inh, + const char *colName, int behavior); + +extern void AlterTableAddConstraint(Oid myrelid, bool inh, + List *newConstraints); + +extern void AlterTableDropConstraint(Oid myrelid, bool inh, + const char *constrName, int behavior); + +extern void AlterTableCreateToastTable(Oid relOid, bool silent); + +extern void AlterTableOwner(Oid relationOid, int32 newOwnerSysId); + +extern Oid DefineRelation(CreateStmt *stmt, char relkind); + +extern void RemoveRelation(const RangeVar *relation); + +extern void TruncateRelation(const RangeVar *relation); + +extern void renameatt(Oid relid, + const char *oldattname, + const char *newattname, + bool recurse); + +extern void renamerel(Oid relid, + const char *newrelname); + +#endif /* TABLECMDS_H */