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--
|
||||||
# Makefile for commands
|
# Makefile for backend/commands
|
||||||
#
|
#
|
||||||
# IDENTIFICATION
|
# 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 = ../../..
|
top_builddir = ../../..
|
||||||
include $(top_builddir)/src/Makefile.global
|
include $(top_builddir)/src/Makefile.global
|
||||||
|
|
||||||
OBJS = async.o creatinh.o command.o comment.o copy.o indexcmds.o define.o \
|
OBJS = aggregatecmds.o analyze.o async.o cluster.o comment.o copy.o \
|
||||||
remove.o rename.o vacuum.o vacuumlazy.o analyze.o view.o cluster.o \
|
dbcommands.o define.o explain.o functioncmds.o \
|
||||||
explain.o sequence.o trigger.o user.o proclang.o \
|
indexcmds.o lockcmds.o operatorcmds.o portalcmds.o proclang.o \
|
||||||
dbcommands.o variable.o
|
schemacmds.o sequence.o tablecmds.o trigger.o typecmds.o user.o \
|
||||||
|
vacuum.o vacuumlazy.o variable.o view.o
|
||||||
|
|
||||||
all: SUBSYS.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
|
* 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_index.h"
|
||||||
#include "catalog/pg_proc.h"
|
#include "catalog/pg_proc.h"
|
||||||
#include "commands/cluster.h"
|
#include "commands/cluster.h"
|
||||||
#include "commands/command.h"
|
#include "commands/tablecmds.h"
|
||||||
#include "commands/rename.h"
|
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "utils/builtins.h"
|
#include "utils/builtins.h"
|
||||||
#include "utils/lsyscache.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
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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_language.h"
|
||||||
#include "catalog/pg_proc.h"
|
#include "catalog/pg_proc.h"
|
||||||
#include "commands/proclang.h"
|
#include "commands/proclang.h"
|
||||||
|
#include "commands/defrem.h"
|
||||||
#include "fmgr.h"
|
#include "fmgr.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "parser/parse_func.h"
|
#include "parser/parse_func.h"
|
||||||
@ -30,21 +31,6 @@
|
|||||||
#include "utils/syscache.h"
|
#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
|
* 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
|
* 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 "access/heapam.h"
|
||||||
#include "catalog/namespace.h"
|
#include "catalog/namespace.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "commands/creatinh.h"
|
#include "commands/tablecmds.h"
|
||||||
#include "commands/sequence.h"
|
#include "commands/sequence.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "utils/acl.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) 1996-2001, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 "access/xact.h"
|
||||||
#include "catalog/heap.h"
|
#include "catalog/heap.h"
|
||||||
#include "catalog/namespace.h"
|
#include "catalog/namespace.h"
|
||||||
#include "commands/creatinh.h"
|
#include "commands/tablecmds.h"
|
||||||
#include "commands/view.h"
|
#include "commands/view.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "nodes/makefuncs.h"
|
#include "nodes/makefuncs.h"
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 "access/heapam.h"
|
||||||
#include "catalog/heap.h"
|
#include "catalog/heap.h"
|
||||||
#include "catalog/namespace.h"
|
#include "catalog/namespace.h"
|
||||||
#include "commands/command.h"
|
#include "commands/tablecmds.h"
|
||||||
#include "commands/trigger.h"
|
#include "commands/trigger.h"
|
||||||
#include "executor/execdebug.h"
|
#include "executor/execdebug.h"
|
||||||
#include "executor/execdefs.h"
|
#include "executor/execdefs.h"
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 "access/printtup.h"
|
||||||
#include "catalog/heap.h"
|
#include "catalog/heap.h"
|
||||||
#include "commands/command.h"
|
#include "commands/portalcmds.h"
|
||||||
#include "executor/spi_priv.h"
|
#include "executor/spi_priv.h"
|
||||||
#include "tcop/tcopprot.h"
|
#include "tcop/tcopprot.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
|
@ -8,14 +8,14 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 "postgres.h"
|
||||||
|
|
||||||
#include "commands/command.h"
|
#include "commands/portalcmds.h"
|
||||||
#include "executor/execdefs.h"
|
#include "executor/execdefs.h"
|
||||||
#include "executor/executor.h"
|
#include "executor/executor.h"
|
||||||
#include "tcop/pquery.h"
|
#include "tcop/pquery.h"
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 "catalog/pg_shadow.h"
|
||||||
#include "commands/async.h"
|
#include "commands/async.h"
|
||||||
#include "commands/cluster.h"
|
#include "commands/cluster.h"
|
||||||
#include "commands/command.h"
|
|
||||||
#include "commands/comment.h"
|
#include "commands/comment.h"
|
||||||
#include "commands/copy.h"
|
#include "commands/copy.h"
|
||||||
#include "commands/creatinh.h"
|
|
||||||
#include "commands/dbcommands.h"
|
#include "commands/dbcommands.h"
|
||||||
#include "commands/defrem.h"
|
#include "commands/defrem.h"
|
||||||
#include "commands/explain.h"
|
#include "commands/explain.h"
|
||||||
|
#include "commands/lockcmds.h"
|
||||||
|
#include "commands/portalcmds.h"
|
||||||
#include "commands/proclang.h"
|
#include "commands/proclang.h"
|
||||||
#include "commands/rename.h"
|
#include "commands/schemacmds.h"
|
||||||
#include "commands/sequence.h"
|
#include "commands/sequence.h"
|
||||||
|
#include "commands/tablecmds.h"
|
||||||
#include "commands/trigger.h"
|
#include "commands/trigger.h"
|
||||||
#include "commands/user.h"
|
#include "commands/user.h"
|
||||||
#include "commands/vacuum.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) 1996-2001, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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"
|
#include "nodes/parsenodes.h"
|
||||||
|
|
||||||
|
#define DEFAULT_TYPDELIM ','
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* prototypes in indexcmds.c
|
* 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);
|
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);
|
|
||||||
|
|
||||||
/*
|
extern void CreateFunction(ProcedureStmt *stmt);
|
||||||
* prototypes in remove.c
|
|
||||||
*/
|
|
||||||
extern void RemoveDomain(List *names, int behavior);
|
|
||||||
extern void RemoveFunction(List *functionName, List *argTypes);
|
extern void RemoveFunction(List *functionName, List *argTypes);
|
||||||
|
|
||||||
|
extern void DefineOperator(List *names, List *parameters);
|
||||||
extern void RemoveOperator(char *operatorName,
|
extern void RemoveOperator(char *operatorName,
|
||||||
TypeName *typeName1, TypeName *typeName2);
|
TypeName *typeName1, TypeName *typeName2);
|
||||||
extern void RemoveType(List *names);
|
|
||||||
|
extern void DefineAggregate(List *names, List *parameters);
|
||||||
extern void RemoveAggregate(List *aggName, TypeName *aggType);
|
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 */
|
#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