mirror of
https://github.com/postgres/postgres.git
synced 2025-11-09 06:21:09 +03:00
Add DOMAIN support. Includes manual pages and regression tests, from
Rod Taylor.
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.186 2002/03/07 16:35:33 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.187 2002/03/19 02:18:14 momjian Exp $
|
||||
*
|
||||
*
|
||||
* INTERFACE ROUTINES
|
||||
@@ -49,6 +49,7 @@
|
||||
#include "optimizer/planmain.h"
|
||||
#include "optimizer/prep.h"
|
||||
#include "optimizer/var.h"
|
||||
#include "parser/parse_coerce.h"
|
||||
#include "parser/parse_expr.h"
|
||||
#include "parser/parse_relation.h"
|
||||
#include "parser/parse_target.h"
|
||||
@@ -698,10 +699,15 @@ AddNewRelationType(char *typeName, Oid new_rel_oid, Oid new_type_oid)
|
||||
"oidin", /* receive procedure */
|
||||
"oidout", /* send procedure */
|
||||
NULL, /* array element type - irrelevant */
|
||||
NULL, /* baseType Name -- typically for domains */
|
||||
NULL, /* default type value - none */
|
||||
NULL, /* default type binary representation */
|
||||
true, /* passed by value */
|
||||
'i', /* default alignment - same as for OID */
|
||||
'p'); /* Not TOASTable */
|
||||
'p', /* Not TOASTable */
|
||||
-1, /* Type mod length */
|
||||
0, /* array dimensions for typBaseType */
|
||||
false); /* Type NOT NULL */
|
||||
}
|
||||
|
||||
/* --------------------------------
|
||||
@@ -1584,6 +1590,10 @@ AddRelationRawConstraints(Relation rel,
|
||||
int numchecks;
|
||||
List *listptr;
|
||||
|
||||
/* Probably shouldn't be null by default */
|
||||
Node *expr = NULL;
|
||||
|
||||
|
||||
/*
|
||||
* Get info about existing constraints.
|
||||
*/
|
||||
@@ -1614,68 +1624,13 @@ AddRelationRawConstraints(Relation rel,
|
||||
foreach(listptr, rawColDefaults)
|
||||
{
|
||||
RawColumnDefault *colDef = (RawColumnDefault *) lfirst(listptr);
|
||||
Node *expr;
|
||||
Oid type_id;
|
||||
|
||||
Assert(colDef->raw_default != NULL);
|
||||
|
||||
/*
|
||||
* Transform raw parsetree to executable expression.
|
||||
*/
|
||||
expr = transformExpr(pstate, colDef->raw_default, EXPR_COLUMN_FIRST);
|
||||
Form_pg_attribute atp = rel->rd_att->attrs[colDef->attnum - 1];
|
||||
|
||||
/*
|
||||
* Make sure default expr does not refer to any vars.
|
||||
*/
|
||||
if (contain_var_clause(expr))
|
||||
elog(ERROR, "cannot use column references in DEFAULT clause");
|
||||
|
||||
/*
|
||||
* No subplans or aggregates, either...
|
||||
*/
|
||||
if (contain_subplans(expr))
|
||||
elog(ERROR, "cannot use subselects in DEFAULT clause");
|
||||
if (contain_agg_clause(expr))
|
||||
elog(ERROR, "cannot use aggregate functions in DEFAULT clause");
|
||||
|
||||
/*
|
||||
* Check that it will be possible to coerce the expression to the
|
||||
* column's type. We store the expression without coercion,
|
||||
* however, to avoid premature coercion in cases like
|
||||
*
|
||||
* CREATE TABLE tbl (fld datetime DEFAULT 'now'::text);
|
||||
*
|
||||
* NB: this should match the code in optimizer/prep/preptlist.c that
|
||||
* will actually do the coercion, to ensure we don't accept an
|
||||
* unusable default expression.
|
||||
*/
|
||||
type_id = exprType(expr);
|
||||
if (type_id != InvalidOid)
|
||||
{
|
||||
Form_pg_attribute atp = rel->rd_att->attrs[colDef->attnum - 1];
|
||||
|
||||
if (type_id != atp->atttypid)
|
||||
{
|
||||
if (CoerceTargetExpr(NULL, expr, type_id,
|
||||
atp->atttypid, atp->atttypmod) == NULL)
|
||||
elog(ERROR, "Column \"%s\" is of type %s"
|
||||
" but default expression is of type %s"
|
||||
"\n\tYou will need to rewrite or cast the expression",
|
||||
NameStr(atp->attname),
|
||||
format_type_be(atp->atttypid),
|
||||
format_type_be(type_id));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Might as well try to reduce any constant expressions.
|
||||
*/
|
||||
expr = eval_const_expressions(expr);
|
||||
|
||||
/*
|
||||
* Must fix opids, in case any operators remain...
|
||||
*/
|
||||
fix_opids(expr);
|
||||
expr = cookDefault(pstate, colDef->raw_default
|
||||
, atp->atttypid, atp->atttypmod
|
||||
, NameStr(atp->attname));
|
||||
|
||||
/*
|
||||
* OK, store it.
|
||||
@@ -1892,6 +1847,88 @@ SetRelationNumChecks(Relation rel, int numchecks)
|
||||
heap_close(relrel, RowExclusiveLock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Take a raw default and convert it to a cooked format ready for
|
||||
* storage.
|
||||
*
|
||||
* Parse state, attypid, attypmod and attname are required for
|
||||
* CoerceTargetExpr() and more importantly transformExpr().
|
||||
*/
|
||||
Node *
|
||||
cookDefault(ParseState *pstate,
|
||||
Node *raw_default,
|
||||
Oid atttypid,
|
||||
int32 atttypmod,
|
||||
char *attname) {
|
||||
|
||||
Oid type_id;
|
||||
Node *expr;
|
||||
|
||||
Assert(raw_default != NULL);
|
||||
|
||||
/*
|
||||
* Transform raw parsetree to executable expression.
|
||||
*/
|
||||
expr = transformExpr(pstate, raw_default, EXPR_COLUMN_FIRST);
|
||||
|
||||
/*
|
||||
* Make sure default expr does not refer to any vars.
|
||||
*/
|
||||
if (contain_var_clause(expr))
|
||||
elog(ERROR, "cannot use column references in DEFAULT clause");
|
||||
|
||||
/*
|
||||
* No subplans or aggregates, either...
|
||||
*/
|
||||
if (contain_subplans(expr))
|
||||
elog(ERROR, "cannot use subselects in DEFAULT clause");
|
||||
if (contain_agg_clause(expr))
|
||||
elog(ERROR, "cannot use aggregate functions in DEFAULT clause");
|
||||
|
||||
/*
|
||||
* Check that it will be possible to coerce the expression to the
|
||||
* column's type. We store the expression without coercion,
|
||||
* however, to avoid premature coercion in cases like
|
||||
*
|
||||
* CREATE TABLE tbl (fld datetime DEFAULT 'now'::text);
|
||||
*
|
||||
* NB: this should match the code in optimizer/prep/preptlist.c that
|
||||
* will actually do the coercion, to ensure we don't accept an
|
||||
* unusable default expression.
|
||||
*/
|
||||
type_id = exprType(expr);
|
||||
if (type_id != InvalidOid && atttypid != InvalidOid) {
|
||||
if (type_id != atttypid) {
|
||||
|
||||
/* Try coercing to the base type of the domain if available */
|
||||
if (CoerceTargetExpr(pstate, expr, type_id,
|
||||
getBaseType(atttypid),
|
||||
atttypmod) == NULL) {
|
||||
|
||||
elog(ERROR, "Column \"%s\" is of type %s"
|
||||
" but default expression is of type %s"
|
||||
"\n\tYou will need to rewrite or cast the expression",
|
||||
attname,
|
||||
format_type_be(atttypid),
|
||||
format_type_be(type_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Might as well try to reduce any constant expressions.
|
||||
*/
|
||||
expr = eval_const_expressions(expr);
|
||||
|
||||
/*
|
||||
* Must fix opids, in case any operators remain...
|
||||
*/
|
||||
fix_opids(expr);
|
||||
|
||||
return(expr);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
RemoveAttrDefaults(Relation rel)
|
||||
{
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.67 2002/03/07 16:35:33 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.68 2002/03/19 02:18:14 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -176,10 +176,16 @@ TypeShellMakeWithOpenRelation(Relation pg_type_desc, char *typeName)
|
||||
values[i++] = ObjectIdGetDatum(InvalidOid); /* 12 */
|
||||
values[i++] = ObjectIdGetDatum(InvalidOid); /* 13 */
|
||||
values[i++] = ObjectIdGetDatum(InvalidOid); /* 14 */
|
||||
values[i++] = CharGetDatum('i'); /* 15 */
|
||||
values[i++] = CharGetDatum('p'); /* 16 */
|
||||
values[i++] = CharGetDatum('i'); /* 15 */
|
||||
values[i++] = CharGetDatum('p'); /* 16 */
|
||||
values[i++] = BoolGetDatum(false); /* 17 */
|
||||
values[i++] = Int32GetDatum(-1); /* 18 */
|
||||
values[i++] = ObjectIdGetDatum(InvalidOid); /* 19 */
|
||||
values[i++] = Int32GetDatum(0); /* 20 */
|
||||
values[i++] = DirectFunctionCall1(textin,
|
||||
CStringGetDatum(typeName)); /* 17 */
|
||||
CStringGetDatum(typeName)); /* 21 */
|
||||
values[i++] = DirectFunctionCall1(textin,
|
||||
CStringGetDatum(typeName)); /* 22 */
|
||||
|
||||
/*
|
||||
* create a new type tuple with FormHeapTuple
|
||||
@@ -264,7 +270,7 @@ TypeShellMake(char *typeName)
|
||||
Oid
|
||||
TypeCreate(char *typeName,
|
||||
Oid assignedTypeOid,
|
||||
Oid relationOid, /* only for 'c'atalog typeTypes */
|
||||
Oid relationOid, /* only for 'c'atalog typeTypes */
|
||||
int16 internalSize,
|
||||
int16 externalSize,
|
||||
char typeType,
|
||||
@@ -274,10 +280,15 @@ TypeCreate(char *typeName,
|
||||
char *receiveProcedure,
|
||||
char *sendProcedure,
|
||||
char *elementTypeName,
|
||||
char *defaultTypeValue, /* internal rep */
|
||||
char *baseTypeName,
|
||||
char *defaultTypeValue, /* human readable rep */
|
||||
char *defaultTypeBin, /* cooked rep */
|
||||
bool passedByValue,
|
||||
char alignment,
|
||||
char storage)
|
||||
char storage,
|
||||
int32 typeMod,
|
||||
int32 typNDims, /* Array dimensions for baseTypeName */
|
||||
bool typeNotNull) /* binary default representation (cooked) */
|
||||
{
|
||||
int i,
|
||||
j;
|
||||
@@ -285,6 +296,7 @@ TypeCreate(char *typeName,
|
||||
HeapScanDesc pg_type_scan;
|
||||
Oid typeObjectId;
|
||||
Oid elementObjectId = InvalidOid;
|
||||
Oid baseObjectId = InvalidOid;
|
||||
HeapTuple tup;
|
||||
char nulls[Natts_pg_type];
|
||||
char replaces[Natts_pg_type];
|
||||
@@ -317,6 +329,17 @@ TypeCreate(char *typeName,
|
||||
elog(ERROR, "type %s does not exist", elementTypeName);
|
||||
}
|
||||
|
||||
/*
|
||||
* if this type has an associated baseType, then we check that it
|
||||
* is defined.
|
||||
*/
|
||||
if (baseTypeName)
|
||||
{
|
||||
baseObjectId = TypeGet(baseTypeName, &defined);
|
||||
if (!defined)
|
||||
elog(ERROR, "type %s does not exist", baseTypeName);
|
||||
}
|
||||
|
||||
/*
|
||||
* validate size specifications: either positive (fixed-length) or -1
|
||||
* (variable-length).
|
||||
@@ -388,7 +411,7 @@ TypeCreate(char *typeName,
|
||||
* signature is 0,OIDOID,INT4OID. The output procedures may
|
||||
* take 2 args (data value, element OID).
|
||||
*/
|
||||
if (OidIsValid(elementObjectId))
|
||||
if (OidIsValid(elementObjectId) || OidIsValid(baseObjectId))
|
||||
{
|
||||
int nargs;
|
||||
|
||||
@@ -411,6 +434,7 @@ TypeCreate(char *typeName,
|
||||
PointerGetDatum(argList),
|
||||
0);
|
||||
}
|
||||
|
||||
if (!OidIsValid(procOid))
|
||||
func_error("TypeCreate", procname, 1, argList, NULL);
|
||||
}
|
||||
@@ -428,6 +452,34 @@ TypeCreate(char *typeName,
|
||||
*/
|
||||
values[i++] = CharGetDatum(storage); /* 16 */
|
||||
|
||||
/*
|
||||
* set the typenotnull value
|
||||
*/
|
||||
values[i++] = BoolGetDatum(typeNotNull); /* 17 */
|
||||
|
||||
/*
|
||||
* set the typemod value
|
||||
*/
|
||||
values[i++] = Int32GetDatum(typeMod); /* 18 */
|
||||
|
||||
values[i++] = ObjectIdGetDatum(baseObjectId); /* 19 */
|
||||
|
||||
/*
|
||||
* Dimension number for an array base type
|
||||
*/
|
||||
values[i++] = Int32GetDatum(typNDims); /* 20 */
|
||||
|
||||
/*
|
||||
* initialize the default binary value for this type. Check for
|
||||
* nulls of course.
|
||||
*/
|
||||
if (defaultTypeBin)
|
||||
values[i] = DirectFunctionCall1(textin,
|
||||
CStringGetDatum(defaultTypeBin));
|
||||
else
|
||||
nulls[i] = 'n';
|
||||
i++; /* 21 */
|
||||
|
||||
/*
|
||||
* initialize the default value for this type.
|
||||
*/
|
||||
@@ -436,7 +488,7 @@ TypeCreate(char *typeName,
|
||||
CStringGetDatum(defaultTypeValue));
|
||||
else
|
||||
nulls[i] = 'n';
|
||||
i++; /* 17 */
|
||||
i++; /* 22 */
|
||||
|
||||
/*
|
||||
* open pg_type and begin a scan for the type name.
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/creatinh.c,v 1.85 2002/03/07 16:35:34 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/creatinh.c,v 1.86 2002/03/19 02:18:15 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -39,7 +39,7 @@ 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
|
||||
@@ -69,6 +69,13 @@ DefineRelation(CreateStmt *stmt, char relkind)
|
||||
*/
|
||||
StrNCpy(relname, stmt->relname, NAMEDATALEN);
|
||||
|
||||
/*
|
||||
* Inherit domain attributes into the known columns before table inheritance
|
||||
* applies it's changes otherwise we risk adding double constraints
|
||||
* to a domain thats inherited.
|
||||
*/
|
||||
schema = MergeDomainAttributes(schema);
|
||||
|
||||
/*
|
||||
* Look up inheritance ancestors and generate relation schema,
|
||||
* including inherited attributes.
|
||||
@@ -237,6 +244,66 @@ TruncateRelation(char *name)
|
||||
heap_truncate(name);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* MergeDomainAttributes
|
||||
* Returns a new table schema with the constraints, types, and other
|
||||
* attributes of the domain resolved for fields using the domain as
|
||||
* their type.
|
||||
*
|
||||
* Defaults are pulled out by the table attribute as required, similar to
|
||||
* how all types defaults are processed.
|
||||
*/
|
||||
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 = SearchSysCache(TYPENAME,
|
||||
CStringGetDatum(coldef->typename->name),
|
||||
0,0,0);
|
||||
|
||||
if (!HeapTupleIsValid(tuple))
|
||||
elog(ERROR, "MergeDomainAttributes: Type %s does not exist",
|
||||
coldef->typename->name);
|
||||
|
||||
typeTup = (Form_pg_type) GETSTRUCT(tuple);
|
||||
if (typeTup->typtype == 'd') {
|
||||
/*
|
||||
* This is a domain, lets force the properties of the domain on to
|
||||
* the new column.
|
||||
*/
|
||||
|
||||
/* Enforce the typmod value */
|
||||
coldef->typename->typmod = typeTup->typmod;
|
||||
|
||||
/* Enforce type NOT NULL || column definition NOT NULL -> NOT NULL */
|
||||
coldef->is_not_null |= typeTup->typnotnull;
|
||||
|
||||
/* Enforce the element type in the event the domain is an array
|
||||
*
|
||||
* BUG: How do we fill out arrayBounds and attrname from typelem and typNDimms?
|
||||
*/
|
||||
|
||||
}
|
||||
ReleaseSysCache(tuple);
|
||||
}
|
||||
|
||||
return schema;
|
||||
}
|
||||
|
||||
/*----------
|
||||
* MergeAttributes
|
||||
* Returns new schema given initial schema and superclasses.
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.69 2002/03/07 16:35:34 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.70 2002/03/19 02:18:15 momjian Exp $
|
||||
*
|
||||
* DESCRIPTION
|
||||
* The "DefineFoo" routines take the parse tree and pick out the
|
||||
@@ -40,6 +40,7 @@
|
||||
|
||||
#include "access/heapam.h"
|
||||
#include "catalog/catname.h"
|
||||
#include "catalog/heap.h"
|
||||
#include "catalog/pg_aggregate.h"
|
||||
#include "catalog/pg_language.h"
|
||||
#include "catalog/pg_operator.h"
|
||||
@@ -475,6 +476,323 @@ DefineAggregate(char *aggName, List *parameters)
|
||||
initval); /* initial condition */
|
||||
}
|
||||
|
||||
/*
|
||||
* DefineDomain
|
||||
* Registers a new domain.
|
||||
*/
|
||||
void
|
||||
DefineDomain(CreateDomainStmt *stmt)
|
||||
{
|
||||
int16 internalLength = -1; /* int2 */
|
||||
int16 externalLength = -1; /* int2 */
|
||||
char *inputName = NULL;
|
||||
char *outputName = NULL;
|
||||
char *sendName = NULL;
|
||||
char *receiveName = NULL;
|
||||
|
||||
/*
|
||||
* Domains store the external representation in defaultValue
|
||||
* and the interal Node representation in defaultValueBin
|
||||
*/
|
||||
char *defaultValue = NULL;
|
||||
char *defaultValueBin = NULL;
|
||||
|
||||
bool byValue = false;
|
||||
char delimiter = DEFAULT_TYPDELIM;
|
||||
char alignment = 'i'; /* default alignment */
|
||||
char storage = 'p'; /* default TOAST storage method */
|
||||
char typtype;
|
||||
Datum datum;
|
||||
bool typNotNull = false;
|
||||
char *elemName = NULL;
|
||||
int32 typNDims = 0; /* No array dimensions by default */
|
||||
|
||||
bool isnull;
|
||||
Relation pg_type_rel;
|
||||
TupleDesc pg_type_dsc;
|
||||
HeapTuple typeTup;
|
||||
char *typeName = stmt->typename->name;
|
||||
|
||||
List *listptr;
|
||||
List *schema = stmt->constraints;
|
||||
|
||||
/*
|
||||
* Domainnames, unlike typenames don't need to account for the '_'
|
||||
* prefix. So they can be one character longer.
|
||||
*/
|
||||
if (strlen(stmt->domainname) > (NAMEDATALEN - 1))
|
||||
elog(ERROR, "CREATE DOMAIN: domain names must be %d characters or less",
|
||||
NAMEDATALEN - 1);
|
||||
|
||||
|
||||
/* Test for existing Domain (or type) of that name */
|
||||
typeTup = SearchSysCache( TYPENAME
|
||||
, PointerGetDatum(stmt->domainname)
|
||||
, 0, 0, 0
|
||||
);
|
||||
|
||||
if (HeapTupleIsValid(typeTup))
|
||||
{
|
||||
elog(ERROR, "CREATE DOMAIN: domain or type %s already exists",
|
||||
stmt->domainname);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the information about old types
|
||||
*/
|
||||
pg_type_rel = heap_openr(TypeRelationName, RowExclusiveLock);
|
||||
pg_type_dsc = RelationGetDescr(pg_type_rel);
|
||||
|
||||
|
||||
/*
|
||||
* When the type is an array for some reason we don't actually receive
|
||||
* the name here. We receive the base types name. Lets set Dims while
|
||||
* were at it.
|
||||
*/
|
||||
if (stmt->typename->arrayBounds > 0) {
|
||||
typeName = makeArrayTypeName(stmt->typename->name);
|
||||
|
||||
typNDims = length(stmt->typename->arrayBounds);
|
||||
}
|
||||
|
||||
|
||||
typeTup = SearchSysCache( TYPENAME
|
||||
, PointerGetDatum(typeName)
|
||||
, 0, 0, 0
|
||||
);
|
||||
|
||||
if (!HeapTupleIsValid(typeTup))
|
||||
{
|
||||
elog(ERROR, "CREATE DOMAIN: type %s does not exist",
|
||||
stmt->typename->name);
|
||||
}
|
||||
|
||||
|
||||
/* Check that this is a basetype */
|
||||
typtype = DatumGetChar(heap_getattr(typeTup, Anum_pg_type_typtype, pg_type_dsc, &isnull));
|
||||
Assert(!isnull);
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
if (typtype != 'b') {
|
||||
elog(ERROR, "DefineDomain: %s is not a basetype", stmt->typename->name);
|
||||
}
|
||||
|
||||
/* passed by value */
|
||||
byValue = ((Form_pg_type) GETSTRUCT(typeTup))->typbyval;
|
||||
|
||||
/* Required Alignment */
|
||||
alignment = ((Form_pg_type) GETSTRUCT(typeTup))->typalign;
|
||||
|
||||
/* 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;
|
||||
|
||||
/* Input Function Name */
|
||||
datum = heap_getattr(typeTup, Anum_pg_type_typinput, pg_type_dsc, &isnull);
|
||||
Assert(!isnull);
|
||||
|
||||
inputName = DatumGetCString(DirectFunctionCall1(regprocout, datum));
|
||||
|
||||
/* Output Function Name */
|
||||
datum = heap_getattr(typeTup, Anum_pg_type_typoutput, pg_type_dsc, &isnull);
|
||||
Assert(!isnull);
|
||||
|
||||
outputName = DatumGetCString(DirectFunctionCall1(regprocout, datum));
|
||||
|
||||
/* ReceiveName */
|
||||
datum = heap_getattr(typeTup, Anum_pg_type_typreceive, pg_type_dsc, &isnull);
|
||||
Assert(!isnull);
|
||||
|
||||
receiveName = DatumGetCString(DirectFunctionCall1(regprocout, datum));
|
||||
|
||||
/* SendName */
|
||||
datum = heap_getattr(typeTup, Anum_pg_type_typsend, pg_type_dsc, &isnull);
|
||||
Assert(!isnull);
|
||||
|
||||
sendName = DatumGetCString(DirectFunctionCall1(regprocout, datum));
|
||||
|
||||
/* TOAST Strategy */
|
||||
storage = ((Form_pg_type) GETSTRUCT(typeTup))->typstorage;
|
||||
Assert(!isnull);
|
||||
|
||||
/* Inherited default value */
|
||||
datum = heap_getattr(typeTup, Anum_pg_type_typdefault, pg_type_dsc, &isnull);
|
||||
if (!isnull) {
|
||||
defaultValue = DatumGetCString(DirectFunctionCall1(textout, datum));
|
||||
}
|
||||
|
||||
/* Inherited default binary value */
|
||||
datum = heap_getattr(typeTup, Anum_pg_type_typdefaultbin, pg_type_dsc, &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
|
||||
*/
|
||||
datum = heap_getattr(typeTup, Anum_pg_type_typelem, pg_type_dsc, &isnull);
|
||||
Assert(!isnull);
|
||||
|
||||
if (DatumGetObjectId(datum) != InvalidOid) {
|
||||
HeapTuple tup;
|
||||
|
||||
tup = SearchSysCache( TYPEOID
|
||||
, datum
|
||||
, 0, 0, 0
|
||||
);
|
||||
|
||||
elemName = NameStr(((Form_pg_type) GETSTRUCT(tup))->typname);
|
||||
|
||||
ReleaseSysCache(tup);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Run through constraints manually avoids 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)
|
||||
{
|
||||
bool nullDefined = false;
|
||||
Node *expr;
|
||||
Constraint *colDef = lfirst(listptr);
|
||||
|
||||
/* Used for the statement transformation */
|
||||
ParseState *pstate;
|
||||
|
||||
/*
|
||||
* Create a dummy ParseState and insert the target relation as its
|
||||
* sole rangetable entry. We need a ParseState for transformExpr.
|
||||
*/
|
||||
pstate = make_parsestate(NULL);
|
||||
|
||||
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:
|
||||
|
||||
/*
|
||||
* Cook the colDef->raw_expr into an expression to ensure
|
||||
* that it can be done. We store the text version of the
|
||||
* raw value.
|
||||
*
|
||||
* Note: Name is strictly for error message
|
||||
*/
|
||||
expr = cookDefault(pstate, colDef->raw_expr
|
||||
, typeTup->t_data->t_oid
|
||||
, stmt->typename->typmod
|
||||
, stmt->typename->name);
|
||||
|
||||
/* Binary default required */
|
||||
defaultValue = deparse_expression(expr,
|
||||
deparse_context_for(stmt->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 indecies not supported");
|
||||
break;
|
||||
|
||||
case CONSTR_PRIMARY:
|
||||
elog(ERROR, "CREATE DOMAIN / PRIMARY KEY indecies 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Have TypeCreate do all the real work.
|
||||
*/
|
||||
TypeCreate(stmt->domainname, /* type name */
|
||||
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 */
|
||||
inputName, /* input procedure */
|
||||
outputName, /* output procedure */
|
||||
receiveName, /* receive procedure */
|
||||
sendName, /* send procedure */
|
||||
elemName, /* element type name */
|
||||
typeName, /* base type name */
|
||||
defaultValue, /* default type value */
|
||||
defaultValueBin, /* default type value */
|
||||
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);
|
||||
heap_close(pg_type_rel, NoLock);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* DefineType
|
||||
* Registers a new type.
|
||||
@@ -490,6 +808,8 @@ DefineType(char *typeName, List *parameters)
|
||||
char *sendName = NULL;
|
||||
char *receiveName = NULL;
|
||||
char *defaultValue = NULL;
|
||||
char *defaultValueBin = NULL;
|
||||
Node *defaultRaw = (Node *) NULL;
|
||||
bool byValue = false;
|
||||
char delimiter = DEFAULT_TYPDELIM;
|
||||
char *shadow_type;
|
||||
@@ -531,7 +851,7 @@ DefineType(char *typeName, List *parameters)
|
||||
else if (strcasecmp(defel->defname, "element") == 0)
|
||||
elemName = defGetString(defel);
|
||||
else if (strcasecmp(defel->defname, "default") == 0)
|
||||
defaultValue = defGetString(defel);
|
||||
defaultRaw = defel->arg;
|
||||
else if (strcasecmp(defel->defname, "passedbyvalue") == 0)
|
||||
byValue = true;
|
||||
else if (strcasecmp(defel->defname, "alignment") == 0)
|
||||
@@ -591,6 +911,32 @@ DefineType(char *typeName, List *parameters)
|
||||
if (outputName == NULL)
|
||||
elog(ERROR, "Define: \"output\" unspecified");
|
||||
|
||||
|
||||
if (defaultRaw) {
|
||||
Node *expr;
|
||||
ParseState *pstate;
|
||||
|
||||
/*
|
||||
* Create a dummy ParseState and insert the target relation as its
|
||||
* sole rangetable entry. We need a ParseState for transformExpr.
|
||||
*/
|
||||
pstate = make_parsestate(NULL);
|
||||
|
||||
expr = cookDefault(pstate, defaultRaw,
|
||||
InvalidOid,
|
||||
-1,
|
||||
typeName);
|
||||
|
||||
/* Binary default required */
|
||||
defaultValue = deparse_expression(expr,
|
||||
deparse_context_for(typeName,
|
||||
InvalidOid),
|
||||
false);
|
||||
|
||||
defaultValueBin = nodeToString(expr);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* now have TypeCreate do all the real work.
|
||||
*/
|
||||
@@ -606,10 +952,15 @@ DefineType(char *typeName, List *parameters)
|
||||
receiveName, /* receive procedure */
|
||||
sendName, /* send procedure */
|
||||
elemName, /* element type name */
|
||||
NULL, /* base type name (Non-zero for domains) */
|
||||
defaultValue, /* default type value */
|
||||
defaultValueBin, /* default type value (Binary form) */
|
||||
byValue, /* passed by value */
|
||||
alignment, /* required alignment */
|
||||
storage); /* TOAST strategy */
|
||||
storage, /* TOAST strategy */
|
||||
-1, /* typMod (Domains only) */
|
||||
0, /* Array Dimensions of typbasetype */
|
||||
'f'); /* Type NOT NULL */
|
||||
|
||||
/*
|
||||
* When we create a base type (as opposed to a complex type) we need
|
||||
@@ -632,10 +983,15 @@ DefineType(char *typeName, List *parameters)
|
||||
"array_in", /* receive procedure */
|
||||
"array_out", /* send procedure */
|
||||
typeName, /* element type name */
|
||||
NULL, /* base type name */
|
||||
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 */
|
||||
'x', /* ARRAY is always toastable */
|
||||
-1, /* typMod (Domains only) */
|
||||
0, /* Array dimensions of typbasetype */
|
||||
'f'); /* Type NOT NULL */
|
||||
|
||||
pfree(shadow_type);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* remove.c
|
||||
* POSTGRES remove (function | type | operator ) utilty code.
|
||||
* 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.68 2002/03/07 16:35:34 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/remove.c,v 1.69 2002/03/19 02:18:16 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "commands/comment.h"
|
||||
#include "commands/defrem.h"
|
||||
#include "miscadmin.h"
|
||||
#include "parser/parse.h"
|
||||
#include "parser/parse_agg.h"
|
||||
#include "parser/parse_expr.h"
|
||||
#include "parser/parse_func.h"
|
||||
@@ -275,6 +276,60 @@ RemoveType(char *typeName) /* type name to be removed */
|
||||
heap_close(relation, RowExclusiveLock);
|
||||
}
|
||||
|
||||
/*
|
||||
* RemoveDomain
|
||||
* Removes the domain 'typeName' and all attributes and relations that
|
||||
* use it.
|
||||
*/
|
||||
void
|
||||
RemoveDomain(char *domainName, int behavior) /* domain name to be removed */
|
||||
{
|
||||
Relation relation;
|
||||
HeapTuple tup;
|
||||
TupleDesc description;
|
||||
char typtype;
|
||||
bool isnull;
|
||||
|
||||
|
||||
/* Domains are stored as types. Check for permissions on the type */
|
||||
if (!pg_ownercheck(GetUserId(), domainName, TYPENAME))
|
||||
elog(ERROR, "RemoveDomain: type '%s': permission denied",
|
||||
domainName);
|
||||
|
||||
|
||||
relation = heap_openr(TypeRelationName, RowExclusiveLock);
|
||||
description = RelationGetDescr(relation);
|
||||
|
||||
tup = SearchSysCache(TYPENAME,
|
||||
PointerGetDatum(domainName),
|
||||
0, 0, 0);
|
||||
if (!HeapTupleIsValid(tup))
|
||||
elog(ERROR, "RemoveType: type '%s' does not exist", domainName);
|
||||
|
||||
|
||||
/* Check that this is actually a domain */
|
||||
typtype = DatumGetChar(heap_getattr(tup, Anum_pg_type_typtype, description, &isnull));
|
||||
Assert(!isnull);
|
||||
|
||||
if (typtype != 'd') {
|
||||
elog(ERROR, "%s is not a domain", domainName);
|
||||
}
|
||||
|
||||
/* CASCADE unsupported */
|
||||
if (behavior == CASCADE) {
|
||||
elog(ERROR, "DROP DOMAIN does not support the CASCADE keyword");
|
||||
}
|
||||
|
||||
/* Delete any comments associated with this type */
|
||||
DeleteComments(tup->t_data->t_oid, RelationGetRelid(relation));
|
||||
|
||||
simple_heap_delete(relation, &tup->t_self);
|
||||
|
||||
ReleaseSysCache(tup);
|
||||
|
||||
heap_close(relation, RowExclusiveLock);
|
||||
}
|
||||
|
||||
/*
|
||||
* RemoveFunction
|
||||
* Deletes a function.
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.169 2002/03/12 00:51:37 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.170 2002/03/19 02:18:16 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -2231,6 +2231,20 @@ _copyLoadStmt(LoadStmt *from)
|
||||
return newnode;
|
||||
}
|
||||
|
||||
static CreateDomainStmt *
|
||||
_copyCreateDomainStmt(CreateDomainStmt *from)
|
||||
{
|
||||
CreateDomainStmt *newnode = makeNode(CreateDomainStmt);
|
||||
|
||||
if (from->domainname)
|
||||
newnode->domainname = pstrdup(from->domainname);
|
||||
|
||||
Node_Copy(from, newnode, typename);
|
||||
Node_Copy(from, newnode, constraints);
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
||||
static CreatedbStmt *
|
||||
_copyCreatedbStmt(CreatedbStmt *from)
|
||||
{
|
||||
@@ -3031,6 +3045,9 @@ copyObject(void *from)
|
||||
case T_FuncWithArgs:
|
||||
retval = _copyFuncWithArgs(from);
|
||||
break;
|
||||
case T_CreateDomainStmt:
|
||||
retval = _copyCreateDomainStmt(from);
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(ERROR, "copyObject: don't know how to copy node type %d",
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.117 2002/03/12 00:51:37 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.118 2002/03/19 02:18:16 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -1093,6 +1093,19 @@ _equalLoadStmt(LoadStmt *a, LoadStmt *b)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalCreateDomainStmt(CreateDomainStmt *a, CreateDomainStmt *b)
|
||||
{
|
||||
if (!equalstr(a->domainname, b->domainname))
|
||||
return false;
|
||||
if (!equal(a->typename, b->typename))
|
||||
return false;
|
||||
if (!equal(a->constraints, b->constraints))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalCreatedbStmt(CreatedbStmt *a, CreatedbStmt *b)
|
||||
{
|
||||
@@ -2021,6 +2034,9 @@ equal(void *a, void *b)
|
||||
case T_LoadStmt:
|
||||
retval = _equalLoadStmt(a, b);
|
||||
break;
|
||||
case T_CreateDomainStmt:
|
||||
retval = _equalCreateDomainStmt(a, b);
|
||||
break;
|
||||
case T_CreatedbStmt:
|
||||
retval = _equalCreatedbStmt(a, b);
|
||||
break;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.48 2002/03/07 16:35:35 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.49 2002/03/19 02:18:17 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -355,8 +355,6 @@ build_column_default(Relation rel, int attrno)
|
||||
Form_pg_attribute att_tup = rd_att->attrs[attrno - 1];
|
||||
Oid atttype = att_tup->atttypid;
|
||||
int32 atttypmod = att_tup->atttypmod;
|
||||
bool hasdefault;
|
||||
Datum typedefault;
|
||||
int16 typlen;
|
||||
bool typbyval;
|
||||
Node *expr;
|
||||
@@ -392,7 +390,7 @@ build_column_default(Relation rel, int attrno)
|
||||
if (type_id != atttype)
|
||||
{
|
||||
expr = CoerceTargetExpr(NULL, expr, type_id,
|
||||
atttype, atttypmod);
|
||||
getBaseType(atttype), atttypmod);
|
||||
|
||||
/*
|
||||
* This really shouldn't fail; should have checked the
|
||||
@@ -430,41 +428,53 @@ build_column_default(Relation rel, int attrno)
|
||||
* element type is, and the element type's default is irrelevant
|
||||
* too.
|
||||
*/
|
||||
hasdefault = false;
|
||||
typedefault = (Datum) 0;
|
||||
typlen = sizeof(Oid);
|
||||
typbyval = true;
|
||||
|
||||
expr = (Node *) makeConst(atttype,
|
||||
typlen,
|
||||
(Datum) 0,
|
||||
true,
|
||||
typbyval,
|
||||
false, /* not a set */
|
||||
false);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef _DROP_COLUMN_HACK__
|
||||
if (COLUMN_IS_DROPPED(att_tup))
|
||||
{
|
||||
hasdefault = false;
|
||||
typedefault = (Datum) 0;
|
||||
|
||||
expr = (Node *) makeConst(atttype,
|
||||
typlen,
|
||||
(Datum) 0,
|
||||
true,
|
||||
typbyval,
|
||||
false, /* not a set */
|
||||
false);
|
||||
}
|
||||
else
|
||||
#endif /* _DROP_COLUMN_HACK__ */
|
||||
hasdefault = get_typdefault(atttype, &typedefault);
|
||||
expr = get_typdefault(atttype, atttypmod);
|
||||
|
||||
if (expr == NULL) {
|
||||
expr = (Node *) makeConst(atttype,
|
||||
typlen,
|
||||
(Datum) 0,
|
||||
true,
|
||||
typbyval,
|
||||
false, /* not a set */
|
||||
false);
|
||||
}
|
||||
get_typlenbyval(atttype, &typlen, &typbyval);
|
||||
}
|
||||
|
||||
expr = (Node *) makeConst(atttype,
|
||||
typlen,
|
||||
typedefault,
|
||||
!hasdefault,
|
||||
typbyval,
|
||||
false, /* not a set */
|
||||
false);
|
||||
|
||||
/*
|
||||
* If the column is a fixed-length type, it may need a length coercion
|
||||
* as well as a type coercion. But NULLs don't need that.
|
||||
* as well as a type coercion, as well as direction to the final type.
|
||||
*/
|
||||
if (hasdefault)
|
||||
expr = coerce_type_typmod(NULL, expr,
|
||||
atttype, atttypmod);
|
||||
expr = coerce_type_typmod(NULL, expr,
|
||||
atttype, atttypmod);
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.291 2002/03/10 06:02:23 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.292 2002/03/19 02:18:18 momjian Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@@ -98,7 +98,6 @@ static void doNegateFloat(Value *v);
|
||||
|
||||
%}
|
||||
|
||||
|
||||
%union
|
||||
{
|
||||
int ival;
|
||||
@@ -134,9 +133,10 @@ static void doNegateFloat(Value *v);
|
||||
AlterDatabaseSetStmt, AlterGroupStmt, AlterSchemaStmt, AlterTableStmt,
|
||||
AlterUserStmt, AlterUserSetStmt, AnalyzeStmt,
|
||||
ClosePortalStmt, ClusterStmt, CommentStmt, ConstraintsSetStmt,
|
||||
CopyStmt, CreateAsStmt, CreateGroupStmt, CreatePLangStmt,
|
||||
CopyStmt, CreateAsStmt, CreateDomainStmt, CreateGroupStmt, CreatePLangStmt,
|
||||
CreateSchemaStmt, CreateSeqStmt, CreateStmt, CreateTrigStmt,
|
||||
CreateUserStmt, CreatedbStmt, CursorStmt, DefineStmt, DeleteStmt,
|
||||
CreateUserStmt, CreatedbStmt, CursorStmt,
|
||||
DefineStmt, DeleteStmt,
|
||||
DropGroupStmt, DropPLangStmt, DropSchemaStmt, DropStmt, DropTrigStmt,
|
||||
DropUserStmt, DropdbStmt, ExplainStmt, FetchStmt,
|
||||
GrantStmt, IndexStmt, InsertStmt, ListenStmt, LoadStmt, LockStmt,
|
||||
@@ -289,6 +289,8 @@ static void doNegateFloat(Value *v);
|
||||
%type <list> constraints_set_list
|
||||
%type <boolean> constraints_set_mode
|
||||
|
||||
%type <boolean> opt_as
|
||||
|
||||
/*
|
||||
* If you make any token changes, remember to:
|
||||
* - use "yacc -d" and update parse.h
|
||||
@@ -343,7 +345,7 @@ static void doNegateFloat(Value *v);
|
||||
WITHOUT
|
||||
|
||||
/* Keywords (in SQL92 non-reserved words) */
|
||||
%token COMMITTED, SERIALIZABLE, TYPE_P
|
||||
%token COMMITTED, SERIALIZABLE, TYPE_P, DOMAIN_P
|
||||
|
||||
/* Keywords for Postgres support (not in SQL92 reserved words)
|
||||
*
|
||||
@@ -446,6 +448,7 @@ stmt : AlterDatabaseSetStmt
|
||||
| CopyStmt
|
||||
| CreateStmt
|
||||
| CreateAsStmt
|
||||
| CreateDomainStmt
|
||||
| CreateSchemaStmt
|
||||
| CreateGroupStmt
|
||||
| CreateSeqStmt
|
||||
@@ -776,8 +779,11 @@ DropSchemaStmt: DROP SCHEMA UserId
|
||||
n->dbname = $3;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Set PG internal variable
|
||||
@@ -1439,7 +1445,10 @@ ColConstraintElem:
|
||||
n->name = NULL;
|
||||
if (exprIsNullConstant($2))
|
||||
{
|
||||
/* DEFAULT NULL should be reported as empty expr */
|
||||
/*
|
||||
* DEFAULT NULL should be reported as empty expr
|
||||
* Required for NOT NULL Domain overrides
|
||||
*/
|
||||
n->raw_expr = NULL;
|
||||
}
|
||||
else
|
||||
@@ -2021,13 +2030,22 @@ def_list: def_elem { $$ = makeList1($1); }
|
||||
| def_list ',' def_elem { $$ = lappend($1, $3); }
|
||||
;
|
||||
|
||||
def_elem: ColLabel '=' def_arg
|
||||
def_elem: DEFAULT '=' b_expr
|
||||
{
|
||||
$$ = makeNode(DefElem);
|
||||
$$->defname = "default";
|
||||
if (exprIsNullConstant($3))
|
||||
$$->arg = (Node *)NULL;
|
||||
else
|
||||
$$->arg = $3;
|
||||
}
|
||||
| ColId '=' def_arg
|
||||
{
|
||||
$$ = makeNode(DefElem);
|
||||
$$->defname = $1;
|
||||
$$->arg = (Node *)$3;
|
||||
}
|
||||
| ColLabel
|
||||
| ColId
|
||||
{
|
||||
$$ = makeNode(DefElem);
|
||||
$$->defname = $1;
|
||||
@@ -2056,6 +2074,15 @@ DropStmt: DROP drop_type name_list
|
||||
DropStmt *n = makeNode(DropStmt);
|
||||
n->removeType = $2;
|
||||
n->names = $3;
|
||||
n->behavior = RESTRICT; /* Restricted by default */
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| DROP DOMAIN_P name_list drop_behavior
|
||||
{
|
||||
DropStmt *n = makeNode(DropStmt);
|
||||
n->removeType = DROP_DOMAIN_P;
|
||||
n->names = $3;
|
||||
n->behavior = $4;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
;
|
||||
@@ -2088,7 +2115,7 @@ TruncateStmt: TRUNCATE opt_table relation_name
|
||||
* The COMMENT ON statement can take different forms based upon the type of
|
||||
* the object associated with the comment. The form of the statement is:
|
||||
*
|
||||
* COMMENT ON [ [ DATABASE | INDEX | RULE | SEQUENCE | TABLE | TYPE | VIEW ]
|
||||
* COMMENT ON [ [ DATABASE | DOMAIN | INDEX | RULE | SEQUENCE | TABLE | TYPE | VIEW ]
|
||||
* <objname> | AGGREGATE <aggname> (<aggtype>) | FUNCTION
|
||||
* <funcname> (arg1, arg2, ...) | OPERATOR <op>
|
||||
* (leftoperand_typ rightoperand_typ) | TRIGGER <triggername> ON
|
||||
@@ -2174,6 +2201,7 @@ comment_type: DATABASE { $$ = DATABASE; }
|
||||
| RULE { $$ = RULE; }
|
||||
| SEQUENCE { $$ = SEQUENCE; }
|
||||
| TABLE { $$ = TABLE; }
|
||||
| DOMAIN_P { $$ = TYPE_P; }
|
||||
| TYPE_P { $$ = TYPE_P; }
|
||||
| VIEW { $$ = VIEW; }
|
||||
;
|
||||
@@ -3156,6 +3184,22 @@ createdb_opt_item: LOCATION opt_equal Sconst
|
||||
{
|
||||
$$ = lconsi(3, makeListi1(-1));
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* DROP DATABASE
|
||||
*
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
DropdbStmt: DROP DATABASE database_name
|
||||
{
|
||||
DropdbStmt *n = makeNode(DropdbStmt);
|
||||
n->dbname = $3;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| OWNER opt_equal name
|
||||
{
|
||||
$$ = lconsi(4, makeList1($3));
|
||||
@@ -3200,22 +3244,30 @@ AlterDatabaseSetStmt: ALTER DATABASE database_name VariableSetStmt
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* DROP DATABASE
|
||||
* Manipulate a domain
|
||||
*
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
DropdbStmt: DROP DATABASE database_name
|
||||
CreateDomainStmt: CREATE DOMAIN_P name opt_as Typename ColQualList opt_collate
|
||||
{
|
||||
DropdbStmt *n = makeNode(DropdbStmt);
|
||||
n->dbname = $3;
|
||||
CreateDomainStmt *n = makeNode(CreateDomainStmt);
|
||||
n->domainname = $3;
|
||||
n->typename = $5;
|
||||
n->constraints = $6;
|
||||
|
||||
if ($7 != NULL)
|
||||
elog(NOTICE,"CREATE DOMAIN / COLLATE %s not yet "
|
||||
"implemented; clause ignored", $7);
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
;
|
||||
|
||||
opt_as: AS {$$ = TRUE; }
|
||||
| /* EMPTY */ {$$ = FALSE; }
|
||||
;
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
@@ -5857,6 +5909,7 @@ unreserved_keyword:
|
||||
| DEFERRED { $$ = "deferred"; }
|
||||
| DELETE { $$ = "delete"; }
|
||||
| DELIMITERS { $$ = "delimiters"; }
|
||||
| DOMAIN_P { $$ = "domain"; }
|
||||
| DOUBLE { $$ = "double"; }
|
||||
| DROP { $$ = "drop"; }
|
||||
| EACH { $$ = "each"; }
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.103 2002/03/07 16:35:35 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.104 2002/03/19 02:18:19 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -97,6 +97,7 @@ static ScanKeyword ScanKeywords[] = {
|
||||
{"desc", DESC},
|
||||
{"distinct", DISTINCT},
|
||||
{"do", DO},
|
||||
{"domain", DOMAIN_P},
|
||||
{"double", DOUBLE},
|
||||
{"drop", DROP},
|
||||
{"each", EACH},
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.66 2002/03/07 16:35:35 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.67 2002/03/19 02:18:20 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -605,3 +605,32 @@ PreferredType(CATEGORY category, Oid type)
|
||||
}
|
||||
return result;
|
||||
} /* PreferredType() */
|
||||
|
||||
|
||||
/*
|
||||
* If the targetTypeId is a domain, we really want to coerce
|
||||
* the tuple to the domain type -- not the domain itself
|
||||
*/
|
||||
Oid
|
||||
getBaseType(Oid inType)
|
||||
{
|
||||
HeapTuple tup;
|
||||
Form_pg_type typTup;
|
||||
|
||||
tup = SearchSysCache(TYPEOID,
|
||||
ObjectIdGetDatum(inType),
|
||||
0, 0, 0);
|
||||
|
||||
typTup = ((Form_pg_type) GETSTRUCT(tup));
|
||||
|
||||
/*
|
||||
* Assume that typbasetype exists and is a base type, where inType
|
||||
* was a domain
|
||||
*/
|
||||
if (typTup->typtype == 'd')
|
||||
inType = typTup->typbasetype;
|
||||
|
||||
ReleaseSysCache(tup);
|
||||
|
||||
return inType;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.108 2002/03/12 00:51:54 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.109 2002/03/19 02:18:20 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -1027,7 +1027,8 @@ parser_typecast_expression(ParseState *pstate,
|
||||
if (inputType != targetType)
|
||||
{
|
||||
expr = CoerceTargetExpr(pstate, expr, inputType,
|
||||
targetType, typename->typmod);
|
||||
getBaseType(targetType),
|
||||
typename->typmod);
|
||||
if (expr == NULL)
|
||||
elog(ERROR, "Cannot cast type '%s' to '%s'",
|
||||
format_type_be(inputType),
|
||||
@@ -1039,7 +1040,7 @@ parser_typecast_expression(ParseState *pstate,
|
||||
* as well as a type coercion.
|
||||
*/
|
||||
expr = coerce_type_typmod(pstate, expr,
|
||||
targetType, typename->typmod);
|
||||
getBaseType(targetType), typename->typmod);
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.254 2002/03/06 06:10:09 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.255 2002/03/19 02:18:20 momjian Exp $
|
||||
*
|
||||
* NOTES
|
||||
* this is the "main" module of the postgres backend and
|
||||
@@ -1722,7 +1722,7 @@ PostgresMain(int argc, char *argv[], const char *username)
|
||||
if (!IsUnderPostmaster)
|
||||
{
|
||||
puts("\nPOSTGRES backend interactive interface ");
|
||||
puts("$Revision: 1.254 $ $Date: 2002/03/06 06:10:09 $\n");
|
||||
puts("$Revision: 1.255 $ $Date: 2002/03/19 02:18:20 $\n");
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2212,8 +2212,9 @@ CreateCommandTag(Node *parsetree)
|
||||
}
|
||||
break;
|
||||
|
||||
case T_CreateDomainStmt:
|
||||
case T_CreateStmt:
|
||||
tag = "CREATE";
|
||||
tag = "CREATE DOMAIN";
|
||||
break;
|
||||
|
||||
case T_DropStmt:
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.132 2002/03/07 16:35:36 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.133 2002/03/19 02:18:20 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -282,6 +282,11 @@ ProcessUtility(Node *parsetree,
|
||||
/* RemoveType does its own permissions checks */
|
||||
RemoveType(relname);
|
||||
break;
|
||||
|
||||
case DROP_DOMAIN_P:
|
||||
/* RemoveDomain does its own permissions checks */
|
||||
RemoveDomain(relname, stmt->behavior);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -726,6 +731,19 @@ ProcessUtility(Node *parsetree,
|
||||
DropProceduralLanguage((DropPLangStmt *) parsetree);
|
||||
break;
|
||||
|
||||
/*
|
||||
* ******************************** DOMAIN statements ****
|
||||
*
|
||||
*/
|
||||
case T_CreateDomainStmt:
|
||||
DefineDomain((CreateDomainStmt *) parsetree);
|
||||
break;
|
||||
|
||||
/*
|
||||
* ******************************** USER statements ****
|
||||
*
|
||||
*/
|
||||
|
||||
case T_CreateUserStmt:
|
||||
CreateUser((CreateUserStmt *) parsetree);
|
||||
break;
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/format_type.c,v 1.26 2002/03/07 16:35:36 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/format_type.c,v 1.27 2002/03/19 02:18:21 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -126,6 +126,7 @@ format_type_internal(Oid type_oid, int32 typemod,
|
||||
bool is_array;
|
||||
char *name;
|
||||
char *buf;
|
||||
char typtype;
|
||||
|
||||
if (type_oid == InvalidOid && allow_invalid)
|
||||
return pstrdup("-");
|
||||
@@ -144,6 +145,31 @@ format_type_internal(Oid type_oid, int32 typemod,
|
||||
|
||||
array_base_type = ((Form_pg_type) GETSTRUCT(tuple))->typelem;
|
||||
typlen = ((Form_pg_type) GETSTRUCT(tuple))->typlen;
|
||||
typtype = ((Form_pg_type) GETSTRUCT(tuple))->typtype;
|
||||
|
||||
/*
|
||||
* Domains look alot like arrays, so lets process them first, and return
|
||||
* back to avoid the array and 'standard' formatting procedures that are
|
||||
* use for base types.
|
||||
*/
|
||||
if (typtype == 'd') {
|
||||
name = NameStr(((Form_pg_type) GETSTRUCT(tuple))->typname);
|
||||
|
||||
/*
|
||||
* Double-quote the name if it's not a standard identifier.
|
||||
* Note this is *necessary* for ruleutils.c's use.
|
||||
*/
|
||||
if (strspn(name, "abcdefghijklmnopqrstuvwxyz0123456789_") != strlen(name)
|
||||
|| isdigit((unsigned char) name[0]))
|
||||
buf = psnprintf(strlen(name) + 3, "\"%s\"", name);
|
||||
else
|
||||
buf = pstrdup(name);
|
||||
|
||||
ReleaseSysCache(tuple);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
if (array_base_type != InvalidOid && typlen < 0)
|
||||
{
|
||||
/* Switch our attention to the array element type */
|
||||
|
||||
65
src/backend/utils/cache/lsyscache.c
vendored
65
src/backend/utils/cache/lsyscache.c
vendored
@@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.62 2002/03/07 16:35:36 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.63 2002/03/19 02:18:21 momjian Exp $
|
||||
*
|
||||
* NOTES
|
||||
* Eventually, the index information should go through here, too.
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "catalog/pg_shadow.h"
|
||||
#include "catalog/pg_statistic.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "parser/parse_coerce.h"
|
||||
#include "utils/array.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/lsyscache.h"
|
||||
@@ -822,16 +823,17 @@ get_typstorage(Oid typid)
|
||||
* Returns FALSE if there is no default (effectively, default is NULL).
|
||||
* The result points to palloc'd storage for pass-by-reference types.
|
||||
*/
|
||||
bool
|
||||
get_typdefault(Oid typid, Datum *defaultValue)
|
||||
Node *
|
||||
get_typdefault(Oid typid, int32 atttypmod)
|
||||
{
|
||||
HeapTuple typeTuple;
|
||||
Form_pg_type type;
|
||||
Oid typinput,
|
||||
typelem;
|
||||
Datum textDefaultVal;
|
||||
Oid typinput;
|
||||
Oid typbasetype;
|
||||
char typtype;
|
||||
Datum datum;
|
||||
bool isNull;
|
||||
char *strDefaultVal;
|
||||
Node *expr;
|
||||
|
||||
typeTuple = SearchSysCache(TYPEOID,
|
||||
ObjectIdGetDatum(typid),
|
||||
@@ -843,38 +845,41 @@ get_typdefault(Oid typid, Datum *defaultValue)
|
||||
type = (Form_pg_type) GETSTRUCT(typeTuple);
|
||||
|
||||
typinput = type->typinput;
|
||||
typelem = type->typelem;
|
||||
typbasetype = type->typbasetype;
|
||||
typtype = type->typtype;
|
||||
|
||||
/*
|
||||
* typdefault is potentially null, so don't try to access it as a
|
||||
* typdefaultbin is potentially null, so don't try to access it as a
|
||||
* struct field. Must do it the hard way with SysCacheGetAttr.
|
||||
*/
|
||||
textDefaultVal = SysCacheGetAttr(TYPEOID,
|
||||
typeTuple,
|
||||
Anum_pg_type_typdefault,
|
||||
&isNull);
|
||||
datum = SysCacheGetAttr(TYPEOID,
|
||||
typeTuple,
|
||||
Anum_pg_type_typdefaultbin,
|
||||
&isNull);
|
||||
|
||||
ReleaseSysCache(typeTuple);
|
||||
if (isNull)
|
||||
{
|
||||
ReleaseSysCache(typeTuple);
|
||||
*defaultValue = (Datum) 0;
|
||||
return false;
|
||||
return (Node *) NULL;
|
||||
|
||||
/* Convert Datum to a Node */
|
||||
expr = stringToNode(DatumGetCString(
|
||||
DirectFunctionCall1(textout, datum)));
|
||||
|
||||
|
||||
/*
|
||||
* Ensure we goto the basetype before the domain type.
|
||||
*
|
||||
* Prevents scenarios like the below from failing:
|
||||
* CREATE DOMAIN dom text DEFAULT random();
|
||||
*
|
||||
*/
|
||||
if (typbasetype != InvalidOid) {
|
||||
expr = coerce_type(NULL, expr, typid,
|
||||
typbasetype, atttypmod);
|
||||
}
|
||||
|
||||
/* Convert text datum to C string */
|
||||
strDefaultVal = DatumGetCString(DirectFunctionCall1(textout,
|
||||
textDefaultVal));
|
||||
|
||||
/* Convert C string to a value of the given type */
|
||||
*defaultValue = OidFunctionCall3(typinput,
|
||||
CStringGetDatum(strDefaultVal),
|
||||
ObjectIdGetDatum(typelem),
|
||||
Int32GetDatum(-1));
|
||||
|
||||
pfree(strDefaultVal);
|
||||
ReleaseSysCache(typeTuple);
|
||||
|
||||
return true;
|
||||
return expr;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
10
src/backend/utils/cache/relcache.c
vendored
10
src/backend/utils/cache/relcache.c
vendored
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.156 2002/03/07 16:35:36 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.157 2002/03/19 02:18:22 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -512,8 +512,12 @@ RelationBuildTupleDesc(RelationBuildDescInfo buildinfo,
|
||||
(char *) attp,
|
||||
ATTRIBUTE_TUPLE_SIZE);
|
||||
|
||||
/* Update constraint/default info */
|
||||
if (attp->attnotnull)
|
||||
|
||||
|
||||
/*
|
||||
* Update constraint/default info
|
||||
*/
|
||||
if (attp->attnotnull)
|
||||
constr->has_not_null = true;
|
||||
|
||||
if (attp->atthasdef)
|
||||
|
||||
Reference in New Issue
Block a user