mirror of https://github.com/postgres/postgres
have been divided according to the type of object manipulated - so ALTER TABLE code is in tablecmds.c, aggregate commands in aggregatecmds.c and so on. A few common support routines remain in define.c (prototypes in src/include/commands/defrem.h). No code has been changed except for includes to reflect the new files. The prototypes for aggregatecmds.c, functioncmds.c, operatorcmds.c, and typecmds.c remain in src/include/commands/defrem.h. From John Gray <jgray@azuli.co.uk>ecpg_big_bison
parent
ab1ead6b97
commit
71dc300a37
@ -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); |
||||
} |
||||
@ -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); |
||||
} |
||||
File diff suppressed because it is too large
Load Diff
@ -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); |
||||
} |
||||
@ -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 */ |
||||
} |
||||
} |
||||
@ -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); |
||||
} |
||||
@ -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); |
||||
} |
||||
@ -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); |
||||
} |
||||
@ -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(); |
||||
} |
||||
@ -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); |
||||
} |
||||
File diff suppressed because it is too large
Load Diff
@ -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; |
||||
} |
||||
@ -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 */ |
||||
@ -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 */ |
||||
@ -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 */ |
||||
@ -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 */ |
||||
@ -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 */ |
||||
@ -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 */ |
||||
@ -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 */ |
||||
Loading…
Reference in new issue