mirror of
https://github.com/postgres/postgres.git
synced 2025-04-21 12:05:57 +03:00
The contents of command.c, creatinh.c, define.c, remove.c and rename.c
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>
This commit is contained in:
parent
ab1ead6b97
commit
71dc300a37
@ -1,10 +1,10 @@
|
||||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Makefile--
|
||||
# Makefile for commands
|
||||
# Makefile for backend/commands
|
||||
#
|
||||
# IDENTIFICATION
|
||||
# $Header: /cvsroot/pgsql/src/backend/commands/Makefile,v 1.27 2001/07/13 22:55:59 tgl Exp $
|
||||
# $Header: /cvsroot/pgsql/src/backend/commands/Makefile,v 1.28 2002/04/15 05:22:03 tgl Exp $
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
@ -12,10 +12,11 @@ subdir = src/backend/commands
|
||||
top_builddir = ../../..
|
||||
include $(top_builddir)/src/Makefile.global
|
||||
|
||||
OBJS = async.o creatinh.o command.o comment.o copy.o indexcmds.o define.o \
|
||||
remove.o rename.o vacuum.o vacuumlazy.o analyze.o view.o cluster.o \
|
||||
explain.o sequence.o trigger.o user.o proclang.o \
|
||||
dbcommands.o variable.o
|
||||
OBJS = aggregatecmds.o analyze.o async.o cluster.o comment.o copy.o \
|
||||
dbcommands.o define.o explain.o functioncmds.o \
|
||||
indexcmds.o lockcmds.o operatorcmds.o portalcmds.o proclang.o \
|
||||
schemacmds.o sequence.o tablecmds.o trigger.o typecmds.o user.o \
|
||||
vacuum.o vacuumlazy.o variable.o view.o
|
||||
|
||||
all: SUBSYS.o
|
||||
|
||||
|
208
src/backend/commands/aggregatecmds.c
Normal file
208
src/backend/commands/aggregatecmds.c
Normal file
@ -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);
|
||||
}
|
@ -15,7 +15,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.77 2002/03/31 07:49:30 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/cluster.c,v 1.78 2002/04/15 05:22:03 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -29,8 +29,7 @@
|
||||
#include "catalog/pg_index.h"
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "commands/cluster.h"
|
||||
#include "commands/command.h"
|
||||
#include "commands/rename.h"
|
||||
#include "commands/tablecmds.h"
|
||||
#include "miscadmin.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/lsyscache.h"
|
||||
|
@ -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
431
src/backend/commands/functioncmds.c
Normal file
431
src/backend/commands/functioncmds.c
Normal file
@ -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);
|
||||
}
|
69
src/backend/commands/lockcmds.c
Normal file
69
src/backend/commands/lockcmds.c
Normal file
@ -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 */
|
||||
}
|
||||
}
|
247
src/backend/commands/operatorcmds.c
Normal file
247
src/backend/commands/operatorcmds.c
Normal file
@ -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);
|
||||
}
|
234
src/backend/commands/portalcmds.c
Normal file
234
src/backend/commands/portalcmds.c
Normal file
@ -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);
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/proclang.c,v 1.30 2002/04/09 20:35:48 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/proclang.c,v 1.31 2002/04/15 05:22:03 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -22,6 +22,7 @@
|
||||
#include "catalog/pg_language.h"
|
||||
#include "catalog/pg_proc.h"
|
||||
#include "commands/proclang.h"
|
||||
#include "commands/defrem.h"
|
||||
#include "fmgr.h"
|
||||
#include "miscadmin.h"
|
||||
#include "parser/parse_func.h"
|
||||
@ -30,21 +31,6 @@
|
||||
#include "utils/syscache.h"
|
||||
|
||||
|
||||
/*
|
||||
* Translate the input language name to lower case.
|
||||
*/
|
||||
static void
|
||||
case_translate_language_name(const char *input, char *output)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NAMEDATALEN && input[i]; ++i)
|
||||
output[i] = tolower((unsigned char) input[i]);
|
||||
|
||||
output[i] = '\0';
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
* CREATE PROCEDURAL LANGUAGE
|
||||
* ---------------------------------------------------------------------
|
||||
|
@ -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();
|
||||
}
|
116
src/backend/commands/schemacmds.c
Normal file
116
src/backend/commands/schemacmds.c
Normal file
@ -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);
|
||||
}
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/sequence.c,v 1.76 2002/03/30 01:02:41 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/sequence.c,v 1.77 2002/04/15 05:22:03 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -17,7 +17,7 @@
|
||||
#include "access/heapam.h"
|
||||
#include "catalog/namespace.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "commands/creatinh.h"
|
||||
#include "commands/tablecmds.h"
|
||||
#include "commands/sequence.h"
|
||||
#include "miscadmin.h"
|
||||
#include "utils/acl.h"
|
||||
|
File diff suppressed because it is too large
Load Diff
660
src/backend/commands/typecmds.c
Normal file
660
src/backend/commands/typecmds.c
Normal file
@ -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;
|
||||
}
|
@ -6,7 +6,7 @@
|
||||
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: view.c,v 1.61 2002/03/29 19:06:08 tgl Exp $
|
||||
* $Id: view.c,v 1.62 2002/04/15 05:22:03 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -15,7 +15,7 @@
|
||||
#include "access/xact.h"
|
||||
#include "catalog/heap.h"
|
||||
#include "catalog/namespace.h"
|
||||
#include "commands/creatinh.h"
|
||||
#include "commands/tablecmds.h"
|
||||
#include "commands/view.h"
|
||||
#include "miscadmin.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
|
@ -27,7 +27,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.157 2002/04/08 22:42:18 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.158 2002/04/15 05:22:04 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -36,7 +36,7 @@
|
||||
#include "access/heapam.h"
|
||||
#include "catalog/heap.h"
|
||||
#include "catalog/namespace.h"
|
||||
#include "commands/command.h"
|
||||
#include "commands/tablecmds.h"
|
||||
#include "commands/trigger.h"
|
||||
#include "executor/execdebug.h"
|
||||
#include "executor/execdefs.h"
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.68 2002/03/21 16:00:38 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.69 2002/04/15 05:22:04 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -16,7 +16,7 @@
|
||||
|
||||
#include "access/printtup.h"
|
||||
#include "catalog/heap.h"
|
||||
#include "commands/command.h"
|
||||
#include "commands/portalcmds.h"
|
||||
#include "executor/spi_priv.h"
|
||||
#include "tcop/tcopprot.h"
|
||||
#include "utils/lsyscache.h"
|
||||
|
@ -8,14 +8,14 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.51 2002/03/21 16:01:27 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.52 2002/04/15 05:22:04 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include "commands/command.h"
|
||||
#include "commands/portalcmds.h"
|
||||
#include "executor/execdefs.h"
|
||||
#include "executor/executor.h"
|
||||
#include "tcop/pquery.h"
|
||||
|
@ -10,7 +10,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.148 2002/04/12 20:38:27 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.149 2002/04/15 05:22:04 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -22,16 +22,17 @@
|
||||
#include "catalog/pg_shadow.h"
|
||||
#include "commands/async.h"
|
||||
#include "commands/cluster.h"
|
||||
#include "commands/command.h"
|
||||
#include "commands/comment.h"
|
||||
#include "commands/copy.h"
|
||||
#include "commands/creatinh.h"
|
||||
#include "commands/dbcommands.h"
|
||||
#include "commands/defrem.h"
|
||||
#include "commands/explain.h"
|
||||
#include "commands/lockcmds.h"
|
||||
#include "commands/portalcmds.h"
|
||||
#include "commands/proclang.h"
|
||||
#include "commands/rename.h"
|
||||
#include "commands/schemacmds.h"
|
||||
#include "commands/sequence.h"
|
||||
#include "commands/tablecmds.h"
|
||||
#include "commands/trigger.h"
|
||||
#include "commands/user.h"
|
||||
#include "commands/vacuum.h"
|
||||
|
@ -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 */
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: defrem.h,v 1.34 2002/04/09 20:35:54 tgl Exp $
|
||||
* $Id: defrem.h,v 1.35 2002/04/15 05:22:03 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -16,6 +16,8 @@
|
||||
|
||||
#include "nodes/parsenodes.h"
|
||||
|
||||
#define DEFAULT_TYPDELIM ','
|
||||
|
||||
/*
|
||||
* prototypes in indexcmds.c
|
||||
*/
|
||||
@ -33,22 +35,33 @@ extern void ReindexTable(RangeVar *relation, bool force);
|
||||
extern void ReindexDatabase(const char *databaseName, bool force, bool all);
|
||||
|
||||
/*
|
||||
* prototypes in define.c
|
||||
* DefineFoo and RemoveFoo are now both in foocmds.c
|
||||
*/
|
||||
extern void CreateFunction(ProcedureStmt *stmt);
|
||||
extern void DefineOperator(List *names, List *parameters);
|
||||
extern void DefineAggregate(List *names, List *parameters);
|
||||
extern void DefineType(List *names, List *parameters);
|
||||
extern void DefineDomain(CreateDomainStmt *stmt);
|
||||
|
||||
/*
|
||||
* prototypes in remove.c
|
||||
*/
|
||||
extern void RemoveDomain(List *names, int behavior);
|
||||
extern void CreateFunction(ProcedureStmt *stmt);
|
||||
extern void RemoveFunction(List *functionName, List *argTypes);
|
||||
|
||||
extern void DefineOperator(List *names, List *parameters);
|
||||
extern void RemoveOperator(char *operatorName,
|
||||
TypeName *typeName1, TypeName *typeName2);
|
||||
extern void RemoveType(List *names);
|
||||
TypeName *typeName1, TypeName *typeName2);
|
||||
|
||||
extern void DefineAggregate(List *names, List *parameters);
|
||||
extern void RemoveAggregate(List *aggName, TypeName *aggType);
|
||||
|
||||
extern void DefineType(List *names, List *parameters);
|
||||
extern void RemoveType(List *names);
|
||||
extern void DefineDomain(CreateDomainStmt *stmt);
|
||||
extern void RemoveDomain(List *names, int behavior);
|
||||
|
||||
|
||||
/* support routines in define.c */
|
||||
|
||||
extern void case_translate_language_name(const char *input, char *output);
|
||||
|
||||
extern char *defGetString(DefElem *def);
|
||||
extern double defGetNumeric(DefElem *def);
|
||||
extern List *defGetQualifiedName(DefElem *def);
|
||||
extern TypeName *defGetTypeName(DefElem *def);
|
||||
extern int defGetTypeLength(DefElem *def);
|
||||
|
||||
#endif /* DEFREM_H */
|
||||
|
24
src/include/commands/lockcmds.h
Normal file
24
src/include/commands/lockcmds.h
Normal file
@ -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 */
|
39
src/include/commands/portalcmds.h
Normal file
39
src/include/commands/portalcmds.h
Normal file
@ -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 */
|
22
src/include/commands/schemacmds.h
Normal file
22
src/include/commands/schemacmds.h
Normal file
@ -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 */
|
63
src/include/commands/tablecmds.h
Normal file
63
src/include/commands/tablecmds.h
Normal file
@ -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…
x
Reference in New Issue
Block a user