mirror of
https://github.com/postgres/postgres.git
synced 2025-11-09 06:21:09 +03:00
Ok. Updated patch attached.
- domain.patch -> source patch against pgsql in cvs - drop_domain.sgml and create_domain.sgml -> New doc/src/sgml/ref docs - dominfo.txt -> basic domain related queries I used for testing [ ADDED TO /doc] Enables domains of array elements -> CREATE DOMAIN dom int4[3][2]; Uses a typbasetype column to describe the origin of the domain. Copies data to attnotnull rather than processing in execMain(). Some documentation differences from earlier. If this is approved, I'll start working on pg_dump, and a \dD <domain> option in psql, and regression tests. I don't really feel like doing those until the system table structure settles for pg_type. CHECKS when added, will also be copied to to the table attributes. FK Constraints (if I ever figure out how) will be done similarly. Both will lbe handled by MergeDomainAttributes() which is called shortly before MergeAttributes(). Rod Taylor
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.184 2002/03/06 06:09:25 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.185 2002/03/06 20:34:45 momjian Exp $
|
||||
*
|
||||
*
|
||||
* INTERFACE ROUTINES
|
||||
@@ -698,10 +698,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 domiains */
|
||||
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 +1589,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 +1623,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 +1846,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.65 2001/10/25 05:49:23 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.66 2002/03/06 20:34:46 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -178,8 +178,13 @@ TypeShellMakeWithOpenRelation(Relation pg_type_desc, char *typeName)
|
||||
values[i++] = ObjectIdGetDatum(InvalidOid); /* 14 */
|
||||
values[i++] = CharGetDatum('i'); /* 15 */
|
||||
values[i++] = CharGetDatum('p'); /* 16 */
|
||||
values[i++] = BoolGetDatum(false); /* 17 */
|
||||
values[i++] = Int32GetDatum(-1); /* 18 */
|
||||
values[i++] = DirectFunctionCall1(textin,
|
||||
CStringGetDatum(typeName)); /* 17 */
|
||||
CStringGetDatum(typeName)); /* 19 */
|
||||
values[i++] = DirectFunctionCall1(textin,
|
||||
CStringGetDatum(typeName)); /* 20 */
|
||||
|
||||
|
||||
/*
|
||||
* create a new type tuple with FormHeapTuple
|
||||
@@ -264,7 +269,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 +279,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 +295,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 +328,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 +410,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 +433,7 @@ TypeCreate(char *typeName,
|
||||
PointerGetDatum(argList),
|
||||
0);
|
||||
}
|
||||
|
||||
if (!OidIsValid(procOid))
|
||||
func_error("TypeCreate", procname, 1, argList, NULL);
|
||||
}
|
||||
@@ -428,6 +451,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 +487,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.83 2002/03/06 06:09:31 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/creatinh.c,v 1.84 2002/03/06 20:34:46 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);
|
||||
|
||||
/*
|
||||
* Merge domain attributes into the known columns before 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,88 @@ TruncateRelation(char *name)
|
||||
heap_truncate(name);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* MergeDomainAttributes
|
||||
* Returns a new schema with the constraints, types, and other
|
||||
* attributes of the domain resolved.
|
||||
*
|
||||
* Defaults are processed at execution time by taking the default of
|
||||
* the type (domain) if it is null. This does not need to be merged
|
||||
* here.
|
||||
*/
|
||||
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);
|
||||
|
||||
//typedef struct TypeName
|
||||
//{
|
||||
//NodeTag type;
|
||||
//char *name; /* name of the type */
|
||||
//bool timezone; /* timezone specified? */
|
||||
//bool setof; /* is a set? */
|
||||
//int32 typmod; /* type modifier */
|
||||
//List *arrayBounds; /* array bounds */
|
||||
//char *attrname; /* field name when using %TYPE */
|
||||
//} TypeName;
|
||||
|
||||
// ColumnDef
|
||||
// NodeTag type;
|
||||
// char *colname; /* name of column */
|
||||
// TypeName *typename; /* type of column */
|
||||
// bool is_not_null; /* NOT NULL constraint specified? */
|
||||
// Node *raw_default; /* default value (untransformed parse
|
||||
// * tree) */
|
||||
// char *cooked_default; /* nodeToString representation */
|
||||
// List *constraints; /* other constraints on column */
|
||||
|
||||
}
|
||||
|
||||
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.67 2002/03/06 06:09:32 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.68 2002/03/06 20:34:47 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,322 @@ 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 = DatumGetBool(heap_getattr(typeTup, Anum_pg_type_typbyval, pg_type_dsc, &isnull));
|
||||
Assert(!isnull);
|
||||
|
||||
/* Required Alignment */
|
||||
alignment = DatumGetChar(heap_getattr(typeTup, Anum_pg_type_typalign, pg_type_dsc, &isnull));
|
||||
Assert(!isnull);
|
||||
|
||||
/* Storage Length */
|
||||
internalLength = DatumGetInt16(heap_getattr(typeTup, Anum_pg_type_typlen, pg_type_dsc, &isnull));
|
||||
Assert(!isnull);
|
||||
|
||||
/* External Length (unused) */
|
||||
externalLength = DatumGetInt16(heap_getattr(typeTup, Anum_pg_type_typprtlen, pg_type_dsc, &isnull));
|
||||
Assert(!isnull);
|
||||
|
||||
/* Array element Delimiter */
|
||||
delimiter = DatumGetChar(heap_getattr(typeTup, Anum_pg_type_typdelim, pg_type_dsc, &isnull));
|
||||
Assert(!isnull);
|
||||
|
||||
/* 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 = DatumGetChar(heap_getattr(typeTup, Anum_pg_type_typstorage, pg_type_dsc, &isnull));
|
||||
Assert(!isnull);
|
||||
|
||||
/* Inherited default value */
|
||||
datum = heap_getattr(typeTup, Anum_pg_type_typdefault, pg_type_dsc, &isnull);
|
||||
if (!isnull) {
|
||||
defaultValue = 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 +807,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 +850,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 +910,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 +951,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 +982,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.66 2002/03/06 06:09:35 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/commands/Attic/remove.c,v 1.67 2002/03/06 20:34:47 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.165 2002/03/01 22:45:11 petere Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.166 2002/03/06 20:34:47 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -2227,6 +2227,19 @@ _copyLoadStmt(LoadStmt *from)
|
||||
return newnode;
|
||||
}
|
||||
|
||||
static CreateDomainStmt *
|
||||
_copyCreateDomainStmt(CreateDomainStmt *from)
|
||||
{
|
||||
CreateDomainStmt *newnode = makeNode(CreateDomainStmt);
|
||||
|
||||
if (from->domainname)
|
||||
newnode->domainname = pstrdup(from->domainname);
|
||||
if (from->typename)
|
||||
newnode->typename = from->typename;
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
||||
static CreatedbStmt *
|
||||
_copyCreatedbStmt(CreatedbStmt *from)
|
||||
{
|
||||
|
||||
@@ -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.113 2002/03/06 06:09:49 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.114 2002/03/06 20:34:48 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -1095,6 +1095,17 @@ _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;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalCreatedbStmt(CreatedbStmt *a, CreatedbStmt *b)
|
||||
{
|
||||
@@ -2011,6 +2022,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.46 2001/11/05 17:46:26 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.47 2002/03/06 20:34:49 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -355,7 +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;
|
||||
@@ -392,7 +391,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 +429,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.285 2002/03/06 06:09:53 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.286 2002/03/06 20:34:49 momjian Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@@ -135,7 +135,8 @@ static void doNegateFloat(Value *v);
|
||||
ClosePortalStmt, ClusterStmt, CommentStmt, ConstraintsSetStmt,
|
||||
CopyStmt, CreateAsStmt, CreateGroupStmt, CreatePLangStmt,
|
||||
CreateSchemaStmt, CreateSeqStmt, CreateStmt, CreateTrigStmt,
|
||||
CreateUserStmt, CreatedbStmt, CursorStmt, DefineStmt, DeleteStmt,
|
||||
CreateUserStmt, CreateDomainStmt, CreatedbStmt, CursorStmt,
|
||||
DefineStmt, DeleteStmt,
|
||||
DropGroupStmt, DropPLangStmt, DropSchemaStmt, DropStmt, DropTrigStmt,
|
||||
DropUserStmt, DropdbStmt, ExplainStmt, FetchStmt,
|
||||
GrantStmt, IndexStmt, InsertStmt, ListenStmt, LoadStmt, LockStmt,
|
||||
@@ -289,6 +290,8 @@ static void doNegateFloat(Value *v);
|
||||
%type <list> constraints_set_namelist
|
||||
%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 +346,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 +449,7 @@ stmt : AlterDatabaseSetStmt
|
||||
| CopyStmt
|
||||
| CreateStmt
|
||||
| CreateAsStmt
|
||||
| CreateDomainStmt
|
||||
| CreateSchemaStmt
|
||||
| CreateGroupStmt
|
||||
| CreateSeqStmt
|
||||
@@ -776,8 +780,11 @@ DropSchemaStmt: DROP SCHEMA UserId
|
||||
n->dbname = $3;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Set PG internal variable
|
||||
@@ -1461,7 +1468,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
|
||||
@@ -2043,7 +2053,16 @@ 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;
|
||||
}
|
||||
| ColLabel '=' def_arg
|
||||
{
|
||||
$$ = makeNode(DefElem);
|
||||
$$->defname = $1;
|
||||
@@ -2078,6 +2097,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;
|
||||
}
|
||||
;
|
||||
@@ -2110,7 +2138,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
|
||||
@@ -2196,6 +2224,7 @@ comment_type: DATABASE { $$ = DATABASE; }
|
||||
| RULE { $$ = RULE; }
|
||||
| SEQUENCE { $$ = SEQUENCE; }
|
||||
| TABLE { $$ = TABLE; }
|
||||
| DOMAIN_P { $$ = TYPE_P; }
|
||||
| TYPE_P { $$ = TYPE_P; }
|
||||
| VIEW { $$ = VIEW; }
|
||||
;
|
||||
@@ -3222,6 +3251,30 @@ AlterDatabaseSetStmt: ALTER DATABASE database_name VariableSetStmt
|
||||
}
|
||||
;
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* Manipulate a domain
|
||||
*
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
CreateDomainStmt: CREATE DOMAIN_P name opt_as Typename ColQualList opt_collate
|
||||
{
|
||||
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; }
|
||||
;
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
@@ -5879,6 +5932,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.101 2002/03/05 05:33:15 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.102 2002/03/06 20:34:50 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.64 2001/10/25 05:49:39 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.65 2002/03/06 20:34:51 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -38,6 +38,7 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
|
||||
{
|
||||
Node *result;
|
||||
|
||||
|
||||
if (targetTypeId == inputTypeId ||
|
||||
targetTypeId == InvalidOid ||
|
||||
node == NULL)
|
||||
@@ -605,3 +606,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.105 2001/11/12 20:05:24 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.106 2002/03/06 20:34:52 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;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.129 2002/03/05 05:33:19 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/tcop/utility.c,v 1.130 2002/03/06 20:34:52 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;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -742,6 +747,16 @@ ProcessUtility(Node *parsetree,
|
||||
DropProceduralLanguage((DropPLangStmt *) parsetree);
|
||||
break;
|
||||
|
||||
/*
|
||||
* ******************************** DOMAIN statements ****
|
||||
*
|
||||
*/
|
||||
case T_CreateDomainStmt:
|
||||
set_ps_display(commandTag = "CREATE DOMAIN");
|
||||
|
||||
DefineDomain((CreateDomainStmt *) parsetree);
|
||||
break;
|
||||
|
||||
/*
|
||||
* ******************************** USER statements ****
|
||||
*
|
||||
|
||||
@@ -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.24 2002/03/02 21:39:32 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/format_type.c,v 1.25 2002/03/06 20:34:53 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.60 2002/03/01 04:09:26 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.61 2002/03/06 20:34:54 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.154 2002/03/06 06:10:21 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/cache/relcache.c,v 1.155 2002/03/06 20:34:54 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