diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/access/common/tupdesc.c | 9 | ||||
-rw-r--r-- | src/backend/catalog/heap.c | 4 | ||||
-rw-r--r-- | src/backend/catalog/pg_type.c | 30 | ||||
-rw-r--r-- | src/backend/commands/tablecmds.c | 44 | ||||
-rw-r--r-- | src/backend/commands/typecmds.c | 104 | ||||
-rw-r--r-- | src/backend/nodes/copyfuncs.c | 5 | ||||
-rw-r--r-- | src/backend/nodes/equalfuncs.c | 5 | ||||
-rw-r--r-- | src/backend/nodes/makefuncs.c | 16 | ||||
-rw-r--r-- | src/backend/nodes/outfuncs.c | 5 | ||||
-rw-r--r-- | src/backend/parser/gram.y | 447 | ||||
-rw-r--r-- | src/backend/parser/parse_expr.c | 29 | ||||
-rw-r--r-- | src/backend/parser/parse_relation.c | 6 | ||||
-rw-r--r-- | src/backend/parser/parse_type.c | 77 | ||||
-rw-r--r-- | src/backend/utils/adt/arrayutils.c | 30 | ||||
-rw-r--r-- | src/backend/utils/adt/date.c | 89 | ||||
-rw-r--r-- | src/backend/utils/adt/format_type.c | 145 | ||||
-rw-r--r-- | src/backend/utils/adt/numeric.c | 65 | ||||
-rw-r--r-- | src/backend/utils/adt/timestamp.c | 244 | ||||
-rw-r--r-- | src/backend/utils/adt/varbit.c | 87 | ||||
-rw-r--r-- | src/backend/utils/adt/varchar.c | 89 | ||||
-rw-r--r-- | src/backend/utils/cache/lsyscache.c | 56 | ||||
-rw-r--r-- | src/backend/utils/misc/guc.c | 8 |
22 files changed, 1107 insertions, 487 deletions
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c index 6242afa20f3..be5665db79c 100644 --- a/src/backend/access/common/tupdesc.c +++ b/src/backend/access/common/tupdesc.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.118 2006/07/14 14:52:16 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.119 2006/12/30 21:21:52 tgl Exp $ * * NOTES * some of the executor utility code such as "ExecTypeFromTL" should be @@ -508,6 +508,7 @@ BuildDescForRelation(List *schema) AttrDefault *attrdef = NULL; TupleConstr *constr = (TupleConstr *) palloc0(sizeof(TupleConstr)); char *attname; + Oid atttypid; int32 atttypmod; int attdim; int ndef = 0; @@ -533,7 +534,8 @@ BuildDescForRelation(List *schema) attnum++; attname = entry->colname; - atttypmod = entry->typename->typmod; + atttypid = typenameTypeId(NULL, entry->typename); + atttypmod = typenameTypeMod(NULL, entry->typename, atttypid); attdim = list_length(entry->typename->arrayBounds); if (entry->typename->setof) @@ -543,8 +545,7 @@ BuildDescForRelation(List *schema) attname))); TupleDescInitEntry(desc, attnum, attname, - typenameTypeId(NULL, entry->typename), - atttypmod, attdim); + atttypid, atttypmod, attdim); /* Fill in additional stuff not handled by TupleDescInitEntry */ if (entry->is_not_null) diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index d6822c73c69..41ef41c7c17 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.314 2006/11/05 22:42:08 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.315 2006/12/30 21:21:52 tgl Exp $ * * * INTERFACE ROUTINES @@ -724,6 +724,8 @@ AddNewRelationType(const char *typeName, F_RECORD_OUT, /* output procedure */ F_RECORD_RECV, /* receive procedure */ F_RECORD_SEND, /* send procedure */ + InvalidOid, /* typmodin procedure - none */ + InvalidOid, /* typmodout procedure - none */ InvalidOid, /* analyze procedure - default */ InvalidOid, /* array element type - irrelevant */ InvalidOid, /* domain base type - irrelevant */ diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c index 32de0b90dc1..74517124fbd 100644 --- a/src/backend/catalog/pg_type.c +++ b/src/backend/catalog/pg_type.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_type.c,v 1.108 2006/10/04 00:29:50 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/pg_type.c,v 1.109 2006/12/30 21:21:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -94,6 +94,8 @@ TypeShellMake(const char *typeName, Oid typeNamespace) values[i++] = ObjectIdGetDatum(F_SHELL_OUT); /* typoutput */ values[i++] = ObjectIdGetDatum(InvalidOid); /* typreceive */ values[i++] = ObjectIdGetDatum(InvalidOid); /* typsend */ + values[i++] = ObjectIdGetDatum(InvalidOid); /* typmodin */ + values[i++] = ObjectIdGetDatum(InvalidOid); /* typmodout */ values[i++] = ObjectIdGetDatum(InvalidOid); /* typanalyze */ values[i++] = CharGetDatum('i'); /* typalign */ values[i++] = CharGetDatum('p'); /* typstorage */ @@ -132,6 +134,8 @@ TypeShellMake(const char *typeName, Oid typeNamespace) InvalidOid, InvalidOid, InvalidOid, + InvalidOid, + InvalidOid, NULL, false); @@ -164,6 +168,8 @@ TypeCreate(const char *typeName, Oid outputProcedure, Oid receiveProcedure, Oid sendProcedure, + Oid typmodinProcedure, + Oid typmodoutProcedure, Oid analyzeProcedure, Oid elementType, Oid baseType, @@ -243,6 +249,8 @@ TypeCreate(const char *typeName, values[i++] = ObjectIdGetDatum(outputProcedure); /* typoutput */ values[i++] = ObjectIdGetDatum(receiveProcedure); /* typreceive */ values[i++] = ObjectIdGetDatum(sendProcedure); /* typsend */ + values[i++] = ObjectIdGetDatum(typmodinProcedure); /* typmodin */ + values[i++] = ObjectIdGetDatum(typmodoutProcedure); /* typmodout */ values[i++] = ObjectIdGetDatum(analyzeProcedure); /* typanalyze */ values[i++] = CharGetDatum(alignment); /* typalign */ values[i++] = CharGetDatum(storage); /* typstorage */ @@ -341,6 +349,8 @@ TypeCreate(const char *typeName, outputProcedure, receiveProcedure, sendProcedure, + typmodinProcedure, + typmodoutProcedure, analyzeProcedure, elementType, baseType, @@ -374,6 +384,8 @@ GenerateTypeDependencies(Oid typeNamespace, Oid outputProcedure, Oid receiveProcedure, Oid sendProcedure, + Oid typmodinProcedure, + Oid typmodoutProcedure, Oid analyzeProcedure, Oid elementType, Oid baseType, @@ -436,6 +448,22 @@ GenerateTypeDependencies(Oid typeNamespace, recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } + if (OidIsValid(typmodinProcedure)) + { + referenced.classId = ProcedureRelationId; + referenced.objectId = typmodinProcedure; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } + + if (OidIsValid(typmodoutProcedure)) + { + referenced.classId = ProcedureRelationId; + referenced.objectId = typmodoutProcedure; + referenced.objectSubId = 0; + recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); + } + if (OidIsValid(analyzeProcedure)) { referenced.classId = ProcedureRelationId; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 2a1116f7580..c30aa69c555 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.207 2006/12/23 00:43:09 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.208 2006/12/30 21:21:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -889,6 +889,9 @@ MergeAttributes(List *schema, List *supers, bool istemp, exist_attno = findAttrByName(attributeName, inhSchema); if (exist_attno > 0) { + Oid defTypeId; + int32 deftypmod; + /* * Yes, try to merge the two column definitions. They must * have the same type and typmod. @@ -897,8 +900,10 @@ MergeAttributes(List *schema, List *supers, bool istemp, (errmsg("merging multiple inherited definitions of column \"%s\"", attributeName))); def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1); - if (typenameTypeId(NULL, def->typename) != attribute->atttypid || - def->typename->typmod != attribute->atttypmod) + defTypeId = typenameTypeId(NULL, def->typename); + deftypmod = typenameTypeMod(NULL, def->typename, defTypeId); + if (defTypeId != attribute->atttypid || + deftypmod != attribute->atttypmod) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("inherited column \"%s\" has a type conflict", @@ -1029,6 +1034,8 @@ MergeAttributes(List *schema, List *supers, bool istemp, if (exist_attno > 0) { ColumnDef *def; + Oid defTypeId, newTypeId; + int32 deftypmod, newtypmod; /* * Yes, try to merge the two column definitions. They must @@ -1038,8 +1045,11 @@ MergeAttributes(List *schema, List *supers, bool istemp, (errmsg("merging column \"%s\" with inherited definition", attributeName))); def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1); - if (typenameTypeId(NULL, def->typename) != typenameTypeId(NULL, newdef->typename) || - def->typename->typmod != newdef->typename->typmod) + defTypeId = typenameTypeId(NULL, def->typename); + deftypmod = typenameTypeMod(NULL, def->typename, defTypeId); + newTypeId = typenameTypeId(NULL, newdef->typename); + newtypmod = typenameTypeMod(NULL, newdef->typename, newTypeId); + if (defTypeId != newTypeId || deftypmod != newtypmod) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("column \"%s\" has a type conflict", @@ -3092,6 +3102,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, maxatts; HeapTuple typeTuple; Oid typeOid; + int32 typmod; Form_pg_type tform; Expr *defval; @@ -3110,10 +3121,14 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, if (HeapTupleIsValid(tuple)) { Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple); + Oid ctypeId; + int32 ctypmod; /* Okay if child matches by type */ - if (typenameTypeId(NULL, colDef->typename) != childatt->atttypid || - colDef->typename->typmod != childatt->atttypmod) + ctypeId = typenameTypeId(NULL, colDef->typename); + ctypmod = typenameTypeMod(NULL, colDef->typename, ctypeId); + if (ctypeId != childatt->atttypid || + ctypmod != childatt->atttypmod) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("child table \"%s\" has different type for column \"%s\"", @@ -3169,6 +3184,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, typeTuple = typenameType(NULL, colDef->typename); tform = (Form_pg_type) GETSTRUCT(typeTuple); typeOid = HeapTupleGetOid(typeTuple); + typmod = typenameTypeMod(NULL, colDef->typename, typeOid); /* make sure datatype is legal for a column */ CheckAttributeType(colDef->colname, typeOid); @@ -3186,7 +3202,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, attribute->attstattarget = -1; attribute->attlen = tform->typlen; attribute->attcacheoff = -1; - attribute->atttypmod = colDef->typename->typmod; + attribute->atttypmod = typmod; attribute->attnum = i; attribute->attbyval = tform->typbyval; attribute->attndims = list_length(colDef->typename->arrayBounds); @@ -3278,7 +3294,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel, (Node *) defval, basetype, typeOid, - colDef->typename->typmod, + typmod, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST); if (defval == NULL) /* should not happen */ @@ -4877,6 +4893,7 @@ ATPrepAlterColumnType(List **wqueue, Form_pg_attribute attTup; AttrNumber attnum; Oid targettype; + int32 targettypmod; Node *transform; NewColumnValue *newval; ParseState *pstate = make_parsestate(NULL); @@ -4907,6 +4924,7 @@ ATPrepAlterColumnType(List **wqueue, /* Look up the target type */ targettype = typenameTypeId(NULL, typename); + targettypmod = typenameTypeMod(NULL, typename, targettype); /* make sure datatype is legal for a column */ CheckAttributeType(colName, targettype); @@ -4958,7 +4976,7 @@ ATPrepAlterColumnType(List **wqueue, transform = coerce_to_target_type(pstate, transform, exprType(transform), - targettype, typename->typmod, + targettype, targettypmod, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST); if (transform == NULL) @@ -5004,6 +5022,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, HeapTuple typeTuple; Form_pg_type tform; Oid targettype; + int32 targettypmod; Node *defaultexpr; Relation attrelation; Relation depRel; @@ -5035,6 +5054,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, typeTuple = typenameType(NULL, typename); tform = (Form_pg_type) GETSTRUCT(typeTuple); targettype = HeapTupleGetOid(typeTuple); + targettypmod = typenameTypeMod(NULL, typename, targettype); /* * If there is a default expression for the column, get it and ensure we @@ -5055,7 +5075,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, defaultexpr = strip_implicit_coercions(defaultexpr); defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */ defaultexpr, exprType(defaultexpr), - targettype, typename->typmod, + targettype, targettypmod, COERCION_ASSIGNMENT, COERCE_IMPLICIT_CAST); if (defaultexpr == NULL) @@ -5272,7 +5292,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, * copy of the syscache entry, so okay to scribble on.) */ attTup->atttypid = targettype; - attTup->atttypmod = typename->typmod; + attTup->atttypmod = targettypmod; attTup->attndims = list_length(typename->arrayBounds); attTup->attlen = tform->typlen; attTup->attbyval = tform->typbyval; diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 53cca73a9dd..2b26f2dfa14 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.97 2006/10/04 00:29:51 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.98 2006/12/30 21:21:53 tgl Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -75,6 +75,8 @@ static Oid findTypeInputFunction(List *procname, Oid typeOid); static Oid findTypeOutputFunction(List *procname, Oid typeOid); static Oid findTypeReceiveFunction(List *procname, Oid typeOid); static Oid findTypeSendFunction(List *procname, Oid typeOid); +static Oid findTypeTypmodinFunction(List *procname); +static Oid findTypeTypmodoutFunction(List *procname); static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid); static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode); static void checkDomainOwner(HeapTuple tup, TypeName *typename); @@ -100,6 +102,8 @@ DefineType(List *names, List *parameters) List *outputName = NIL; List *receiveName = NIL; List *sendName = NIL; + List *typmodinName = NIL; + List *typmodoutName = NIL; List *analyzeName = NIL; char *defaultValue = NULL; bool byValue = false; @@ -110,6 +114,8 @@ DefineType(List *names, List *parameters) Oid outputOid; Oid receiveOid = InvalidOid; Oid sendOid = InvalidOid; + Oid typmodinOid = InvalidOid; + Oid typmodoutOid = InvalidOid; Oid analyzeOid = InvalidOid; char *shadow_type; ListCell *pl; @@ -182,6 +188,10 @@ DefineType(List *names, List *parameters) receiveName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "send") == 0) sendName = defGetQualifiedName(defel); + else if (pg_strcasecmp(defel->defname, "typmod_in") == 0) + typmodinName = defGetQualifiedName(defel); + else if (pg_strcasecmp(defel->defname, "typmod_out") == 0) + typmodoutName = defGetQualifiedName(defel); else if (pg_strcasecmp(defel->defname, "analyze") == 0 || pg_strcasecmp(defel->defname, "analyse") == 0) analyzeName = defGetQualifiedName(defel); @@ -268,6 +278,11 @@ DefineType(List *names, List *parameters) (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("type output function must be specified"))); + if (typmodinName == NIL && typmodoutName != NIL) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("type modifier output function is useless without a type modifier input function"))); + /* * Convert I/O proc names to OIDs */ @@ -336,6 +351,14 @@ DefineType(List *names, List *parameters) } /* + * Convert typmodin/out function proc names to OIDs. + */ + if (typmodinName) + typmodinOid = findTypeTypmodinFunction(typmodinName); + if (typmodoutName) + typmodoutOid = findTypeTypmodoutFunction(typmodoutName); + + /* * Convert analysis function proc name to an OID. If no analysis function * is specified, we'll use zero to select the built-in default algorithm. */ @@ -362,6 +385,12 @@ DefineType(List *names, List *parameters) if (sendOid && !pg_proc_ownercheck(sendOid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, NameListToString(sendName)); + if (typmodinOid && !pg_proc_ownercheck(typmodinOid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + NameListToString(typmodinName)); + if (typmodoutOid && !pg_proc_ownercheck(typmodoutOid, GetUserId())) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, + NameListToString(typmodoutName)); if (analyzeOid && !pg_proc_ownercheck(analyzeOid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, NameListToString(analyzeName)); @@ -381,6 +410,8 @@ DefineType(List *names, List *parameters) outputOid, /* output procedure */ receiveOid, /* receive procedure */ sendOid, /* send procedure */ + typmodinOid, /* typmodin procedure */ + typmodoutOid,/* typmodout procedure */ analyzeOid, /* analyze procedure */ elemType, /* element type ID */ InvalidOid, /* base type ID (only for domains) */ @@ -413,6 +444,8 @@ DefineType(List *names, List *parameters) F_ARRAY_OUT, /* output procedure */ F_ARRAY_RECV, /* receive procedure */ F_ARRAY_SEND, /* send procedure */ + typmodinOid, /* typmodin procedure */ + typmodoutOid, /* typmodout procedure */ InvalidOid, /* analyze procedure - default */ typoid, /* element type ID */ InvalidOid, /* base type ID */ @@ -552,6 +585,7 @@ DefineDomain(CreateDomainStmt *stmt) Oid basetypeoid; Oid domainoid; Form_pg_type baseType; + int32 basetypeMod; /* Convert list of names to a name and namespace */ domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname, @@ -581,9 +615,9 @@ DefineDomain(CreateDomainStmt *stmt) * Look up the base type. */ typeTup = typenameType(NULL, stmt->typename); - baseType = (Form_pg_type) GETSTRUCT(typeTup); basetypeoid = HeapTupleGetOid(typeTup); + basetypeMod = typenameTypeMod(NULL, stmt->typename, basetypeoid); /* * Base type must be a plain base type or another domain. Domains over @@ -621,6 +655,8 @@ DefineDomain(CreateDomainStmt *stmt) receiveProcedure = F_DOMAIN_RECV; sendProcedure = baseType->typsend; + /* Domains never accept typmods, so no typmodin/typmodout needed */ + /* Analysis function */ analyzeProcedure = baseType->typanalyze; @@ -681,7 +717,7 @@ DefineDomain(CreateDomainStmt *stmt) */ defaultExpr = cookDefault(pstate, constr->raw_expr, basetypeoid, - stmt->typename->typmod, + basetypeMod, domainName); /* @@ -768,6 +804,8 @@ DefineDomain(CreateDomainStmt *stmt) outputProcedure, /* output procedure */ receiveProcedure, /* receive procedure */ sendProcedure, /* send procedure */ + InvalidOid, /* typmodin procedure - none */ + InvalidOid, /* typmodout procedure - none */ analyzeProcedure, /* analyze procedure */ typelem, /* element type ID */ basetypeoid, /* base type ID */ @@ -776,7 +814,7 @@ DefineDomain(CreateDomainStmt *stmt) byValue, /* passed by value */ alignment, /* required alignment */ storage, /* TOAST strategy */ - stmt->typename->typmod, /* typeMod value */ + basetypeMod, /* typeMod value */ typNDims, /* Array dimensions for base type */ typNotNull); /* Type NOT NULL */ @@ -793,7 +831,7 @@ DefineDomain(CreateDomainStmt *stmt) { case CONSTR_CHECK: domainAddConstraint(domainoid, domainNamespace, - basetypeoid, stmt->typename->typmod, + basetypeoid, basetypeMod, constr, domainName); break; @@ -1068,6 +1106,60 @@ findTypeSendFunction(List *procname, Oid typeOid) } static Oid +findTypeTypmodinFunction(List *procname) +{ + Oid argList[1]; + Oid procOid; + + /* + * typmodin functions always take one int4[] argument and return int4. + */ + argList[0] = INT4ARRAYOID; + + procOid = LookupFuncName(procname, 1, argList, true); + if (!OidIsValid(procOid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("function %s does not exist", + func_signature_string(procname, 1, argList)))); + + if (get_func_rettype(procOid) != INT4OID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("typmod_in function %s must return type \"integer\"", + NameListToString(procname)))); + + return procOid; +} + +static Oid +findTypeTypmodoutFunction(List *procname) +{ + Oid argList[1]; + Oid procOid; + + /* + * typmodout functions always take one int4 argument and return cstring. + */ + argList[0] = INT4OID; + + procOid = LookupFuncName(procname, 1, argList, true); + if (!OidIsValid(procOid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_FUNCTION), + errmsg("function %s does not exist", + func_signature_string(procname, 1, argList)))); + + if (get_func_rettype(procOid) != CSTRINGOID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("typmod_out function %s must return type \"cstring\"", + NameListToString(procname)))); + + return procOid; +} + +static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid) { Oid argList[1]; @@ -1244,6 +1336,8 @@ AlterDomainDefault(List *names, Node *defaultRaw) typTup->typoutput, typTup->typreceive, typTup->typsend, + typTup->typmodin, + typTup->typmodout, typTup->typanalyze, typTup->typelem, typTup->typbasetype, diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index d46ed57d830..c8c9b907c9c 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.357 2006/12/24 00:29:18 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.358 2006/12/30 21:21:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1584,7 +1584,8 @@ _copyTypeName(TypeName *from) COPY_SCALAR_FIELD(timezone); COPY_SCALAR_FIELD(setof); COPY_SCALAR_FIELD(pct_type); - COPY_SCALAR_FIELD(typmod); + COPY_NODE_FIELD(typmods); + COPY_SCALAR_FIELD(typemod); COPY_NODE_FIELD(arrayBounds); COPY_SCALAR_FIELD(location); diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 29bff448c7f..57e61f0e2a2 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -18,7 +18,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.291 2006/12/24 00:29:18 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.292 2006/12/30 21:21:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1614,7 +1614,8 @@ _equalTypeName(TypeName *a, TypeName *b) COMPARE_SCALAR_FIELD(timezone); COMPARE_SCALAR_FIELD(setof); COMPARE_SCALAR_FIELD(pct_type); - COMPARE_SCALAR_FIELD(typmod); + COMPARE_NODE_FIELD(typmods); + COMPARE_SCALAR_FIELD(typemod); COMPARE_NODE_FIELD(arrayBounds); COMPARE_SCALAR_FIELD(location); diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c index 9f6aa22707b..277103e4a18 100644 --- a/src/backend/nodes/makefuncs.c +++ b/src/backend/nodes/makefuncs.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/makefuncs.c,v 1.52 2006/10/04 00:29:53 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/makefuncs.c,v 1.53 2006/12/30 21:21:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -262,12 +262,7 @@ makeRangeVar(char *schemaname, char *relname) TypeName * makeTypeName(char *typnam) { - TypeName *n = makeNode(TypeName); - - n->names = list_make1(makeString(typnam)); - n->typmod = -1; - n->location = -1; - return n; + return makeTypeNameFromNameList(list_make1(makeString(typnam))); } /* @@ -282,14 +277,15 @@ makeTypeNameFromNameList(List *names) TypeName *n = makeNode(TypeName); n->names = names; - n->typmod = -1; + n->typmods = NIL; + n->typemod = -1; n->location = -1; return n; } /* * makeTypeNameFromOid - - * build a TypeName node to represent a type already known by OID. + * build a TypeName node to represent a type already known by OID/typmod. */ TypeName * makeTypeNameFromOid(Oid typeid, int32 typmod) @@ -297,7 +293,7 @@ makeTypeNameFromOid(Oid typeid, int32 typmod) TypeName *n = makeNode(TypeName); n->typeid = typeid; - n->typmod = typmod; + n->typemod = typmod; n->location = -1; return n; } diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 4911d6ed404..2b21eae6a10 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.289 2006/12/24 00:29:18 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.290 2006/12/30 21:21:53 tgl Exp $ * * NOTES * Every node type that can appear in stored rules' parsetrees *must* @@ -1476,7 +1476,8 @@ _outTypeName(StringInfo str, TypeName *node) WRITE_BOOL_FIELD(timezone); WRITE_BOOL_FIELD(setof); WRITE_BOOL_FIELD(pct_type); - WRITE_INT_FIELD(typmod); + WRITE_NODE_FIELD(typmods); + WRITE_INT_FIELD(typemod); WRITE_NODE_FIELD(arrayBounds); WRITE_INT_FIELD(location); } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a1511870f28..c4820190f07 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.570 2006/12/24 00:29:18 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.571 2006/12/30 21:21:53 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -206,7 +206,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args) %type <str> relation_name copy_file_name database_name access_method_clause access_method attr_name - index_name name function_name file_name + index_name name file_name %type <list> func_name handler_name qual_Op qual_all_Op subquery_Op opt_class opt_validator @@ -242,7 +242,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args) group_clause TriggerFuncArgs select_limit opt_select_limit opclass_item_list transaction_mode_list_or_empty - TableFuncElementList + TableFuncElementList opt_type_modifiers prep_type_clause prep_type_list execute_param_clause using_clause returning_clause @@ -319,20 +319,19 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args) %type <str> character %type <str> extract_arg %type <str> opt_charset -%type <ival> opt_numeric opt_decimal %type <boolean> opt_varying opt_timezone %type <ival> Iconst SignedIconst %type <str> Sconst comment_text %type <str> RoleId opt_granted_by opt_boolean ColId_or_Sconst %type <list> var_list var_list_or_default -%type <str> ColId ColLabel var_name type_name param_name +%type <str> ColId ColLabel var_name type_function_name param_name %type <node> var_value zone_value -%type <keyword> unreserved_keyword func_name_keyword +%type <keyword> unreserved_keyword type_func_name_keyword %type <keyword> col_name_keyword reserved_keyword -%type <node> TableConstraint TableLikeClause +%type <node> TableConstraint TableLikeClause %type <list> TableLikeOptionList %type <ival> TableLikeOption %type <list> ColQualList @@ -1180,35 +1179,20 @@ zone_value: ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("time zone interval must be HOUR or HOUR TO MINUTE"))); - n->typename->typmod = INTERVAL_TYPMOD(INTERVAL_FULL_PRECISION, $3); + n->typename->typmods = list_make1(makeIntConst($3)); } $$ = (Node *)n; } | ConstInterval '(' Iconst ')' Sconst opt_interval { A_Const *n = (A_Const *) makeStringConst($5, $1); - if ($3 < 0) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("INTERVAL(%d) precision must not be negative", - $3))); - if ($3 > MAX_INTERVAL_PRECISION) - { - ereport(WARNING, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("INTERVAL(%d) precision reduced to maximum allowed, %d", - $3, MAX_INTERVAL_PRECISION))); - $3 = MAX_INTERVAL_PRECISION; - } - if (($6 != INTERVAL_FULL_RANGE) && (($6 & ~(INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE))) != 0)) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("time zone interval must be HOUR or HOUR TO MINUTE"))); - - n->typename->typmod = INTERVAL_TYPMOD($3, $6); - + n->typename->typmods = list_make2(makeIntConst($6), + makeIntConst($3)); $$ = (Node *)n; } | NumericOnly { $$ = makeAConst($1); } @@ -2823,7 +2807,7 @@ DefineStmt: n->definition = $4; $$ = (Node *)n; } - | CREATE TYPE_P any_name + | CREATE TYPE_P any_name { /* Shell type (identified by lack of definition) */ DefineStmt *n = makeNode(DefineStmt); @@ -2889,7 +2873,6 @@ def_elem: ColLabel '=' def_arg /* Note: any simple identifier will be returned as a type name! */ def_arg: func_type { $$ = (Node *)$1; } - | func_name_keyword { $$ = (Node *)makeString(pstrdup($1)); } | reserved_keyword { $$ = (Node *)makeString(pstrdup($1)); } | qual_all_Op { $$ = (Node *)$1; } | NumericOnly { $$ = (Node *)$1; } @@ -3047,7 +3030,7 @@ ReassignOwnedStmt: * * QUERY: * - * DROP itemtype [ IF EXISTS ] itemname [, itemname ...] + * DROP itemtype [ IF EXISTS ] itemname [, itemname ...] * [ RESTRICT | CASCADE ] * *****************************************************************************/ @@ -3872,7 +3855,7 @@ arg_class: IN_P { $$ = FUNC_PARAM_IN; } /* * Ideally param_name should be ColId, but that causes too many conflicts. */ -param_name: function_name +param_name: type_function_name ; func_return: @@ -3888,23 +3871,20 @@ func_return: /* * We would like to make the %TYPE productions here be ColId attrs etc, - * but that causes reduce/reduce conflicts. type_name is next best choice. + * but that causes reduce/reduce conflicts. type_function_name + * is next best choice. */ func_type: Typename { $$ = $1; } - | type_name attrs '%' TYPE_P + | type_function_name attrs '%' TYPE_P { - $$ = makeNode(TypeName); - $$->names = lcons(makeString($1), $2); + $$ = makeTypeNameFromNameList(lcons(makeString($1), $2)); $$->pct_type = true; - $$->typmod = -1; $$->location = @1; } - | SETOF type_name attrs '%' TYPE_P + | SETOF type_function_name attrs '%' TYPE_P { - $$ = makeNode(TypeName); - $$->names = lcons(makeString($2), $3); + $$ = makeTypeNameFromNameList(lcons(makeString($2), $3)); $$->pct_type = true; - $$->typmod = -1; $$->setof = TRUE; $$->location = @2; } @@ -5552,7 +5532,7 @@ multiple_set_clause: res_col->val = res_val; } - + $$ = $2; } ; @@ -6363,14 +6343,6 @@ opt_array_bounds: { $$ = NIL; } ; -/* - * XXX ideally, the production for a qualified typename should be ColId attrs - * (there's no obvious reason why the first name should need to be restricted) - * and should be an alternative of GenericType (so that it can be used to - * specify a type for a literal in AExprConst). However doing either causes - * reduce/reduce conflicts that I haven't been able to find a workaround - * for. FIXME later. - */ SimpleTypename: GenericType { $$ = $1; } | Numeric { $$ = $1; } @@ -6381,32 +6353,13 @@ SimpleTypename: { $$ = $1; if ($2 != INTERVAL_FULL_RANGE) - $$->typmod = INTERVAL_TYPMOD(INTERVAL_FULL_PRECISION, $2); + $$->typmods = list_make1(makeIntConst($2)); } | ConstInterval '(' Iconst ')' opt_interval { $$ = $1; - if ($3 < 0) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("INTERVAL(%d) precision must not be negative", - $3))); - if ($3 > MAX_INTERVAL_PRECISION) - { - ereport(WARNING, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("INTERVAL(%d) precision reduced to maximum allowed, %d", - $3, MAX_INTERVAL_PRECISION))); - $3 = MAX_INTERVAL_PRECISION; - } - $$->typmod = INTERVAL_TYPMOD($3, $5); - } - | type_name attrs - { - $$ = makeNode(TypeName); - $$->names = lcons(makeString($1), $2); - $$->typmod = -1; - $$->location = @1; + $$->typmods = list_make2(makeIntConst($5), + makeIntConst($3)); } ; @@ -6417,80 +6370,112 @@ SimpleTypename: * where there is an obvious better choice to make. * Note that ConstInterval is not included here since it must * be pushed up higher in the rules to accomodate the postfix - * options (e.g. INTERVAL '1' YEAR). + * options (e.g. INTERVAL '1' YEAR). Likewise, we have to handle + * the generic-type-name case in AExprConst to avoid premature + * reduce/reduce conflicts against function names. */ ConstTypename: - GenericType { $$ = $1; } - | Numeric { $$ = $1; } + Numeric { $$ = $1; } | ConstBit { $$ = $1; } | ConstCharacter { $$ = $1; } | ConstDatetime { $$ = $1; } ; +/* + * GenericType covers all type names that don't have special syntax mandated + * by the standard, including qualified names. We also allow type modifiers. + * To avoid parsing conflicts against function invocations, the modifiers + * have to be shown as expr_list here, but parse analysis will only accept + * integer constants for them. + */ GenericType: - type_name + type_function_name opt_type_modifiers { $$ = makeTypeName($1); + $$->typmods = $2; + $$->location = @1; + } + | type_function_name attrs opt_type_modifiers + { + $$ = makeTypeNameFromNameList(lcons(makeString($1), $2)); + $$->typmods = $3; $$->location = @1; } ; -/* SQL92 numeric data types - * Check FLOAT() precision limits assuming IEEE floating types. - * - thomas 1997-09-18 - * Provide real DECIMAL() and NUMERIC() implementations now - Jan 1998-12-30 +opt_type_modifiers: '(' expr_list ')' { $$ = $2; } + | /* EMPTY */ { $$ = NIL; } + ; + +/* + * SQL92 numeric data types */ Numeric: INT_P { $$ = SystemTypeName("int4"); + $$->location = @1; } | INTEGER { $$ = SystemTypeName("int4"); + $$->location = @1; } | SMALLINT { $$ = SystemTypeName("int2"); + $$->location = @1; } | BIGINT { $$ = SystemTypeName("int8"); + $$->location = @1; } | REAL { $$ = SystemTypeName("float4"); + $$->location = @1; } | FLOAT_P opt_float { $$ = $2; + $$->location = @1; } | DOUBLE_P PRECISION { $$ = SystemTypeName("float8"); + $$->location = @1; } - | DECIMAL_P opt_decimal + | DECIMAL_P opt_type_modifiers { $$ = SystemTypeName("numeric"); - $$->typmod = $2; + $$->typmods = $2; + $$->location = @1; } - | DEC opt_decimal + | DEC opt_type_modifiers { $$ = SystemTypeName("numeric"); - $$->typmod = $2; + $$->typmods = $2; + $$->location = @1; } - | NUMERIC opt_numeric + | NUMERIC opt_type_modifiers { $$ = SystemTypeName("numeric"); - $$->typmod = $2; + $$->typmods = $2; + $$->location = @1; } | BOOLEAN_P { $$ = SystemTypeName("bool"); + $$->location = @1; } ; opt_float: '(' Iconst ')' { + /* + * Check FLOAT() precision limits assuming IEEE floating + * types - thomas 1997-09-18 + */ if ($2 < 1) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), @@ -6510,73 +6495,6 @@ opt_float: '(' Iconst ')' } ; -opt_numeric: - '(' Iconst ',' Iconst ')' - { - if ($2 < 1 || $2 > NUMERIC_MAX_PRECISION) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("NUMERIC precision %d must be between 1 and %d", - $2, NUMERIC_MAX_PRECISION))); - if ($4 < 0 || $4 > $2) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("NUMERIC scale %d must be between 0 and precision %d", - $4, $2))); - - $$ = (($2 << 16) | $4) + VARHDRSZ; - } - | '(' Iconst ')' - { - if ($2 < 1 || $2 > NUMERIC_MAX_PRECISION) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("NUMERIC precision %d must be between 1 and %d", - $2, NUMERIC_MAX_PRECISION))); - - $$ = ($2 << 16) + VARHDRSZ; - } - | /*EMPTY*/ - { - /* Insert "-1" meaning "no limit" */ - $$ = -1; - } - ; - -opt_decimal: - '(' Iconst ',' Iconst ')' - { - if ($2 < 1 || $2 > NUMERIC_MAX_PRECISION) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("DECIMAL precision %d must be between 1 and %d", - $2, NUMERIC_MAX_PRECISION))); - if ($4 < 0 || $4 > $2) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("DECIMAL scale %d must be between 0 and precision %d", - $4, $2))); - - $$ = (($2 << 16) | $4) + VARHDRSZ; - } - | '(' Iconst ')' - { - if ($2 < 1 || $2 > NUMERIC_MAX_PRECISION) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("DECIMAL precision %d must be between 1 and %d", - $2, NUMERIC_MAX_PRECISION))); - - $$ = ($2 << 16) + VARHDRSZ; - } - | /*EMPTY*/ - { - /* Insert "-1" meaning "no limit" */ - $$ = -1; - } - ; - - /* * SQL92 bit-field data types * The following implements BIT() and BIT VARYING(). @@ -6600,28 +6518,19 @@ ConstBit: BitWithLength | BitWithoutLength { $$ = $1; - $$->typmod = -1; + $$->typmods = NIL; } ; BitWithLength: - BIT opt_varying '(' Iconst ')' + BIT opt_varying '(' expr_list ')' { char *typname; typname = $2 ? "varbit" : "bit"; $$ = SystemTypeName(typname); - if ($4 < 1) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("length for type %s must be at least 1", - typname))); - else if ($4 > (MaxAttrSize * BITS_PER_BYTE)) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("length for type %s cannot exceed %d", - typname, MaxAttrSize * BITS_PER_BYTE))); - $$->typmod = $4; + $$->typmods = $4; + $$->location = @1; } ; @@ -6632,13 +6541,13 @@ BitWithoutLength: if ($2) { $$ = SystemTypeName("varbit"); - $$->typmod = -1; } else { $$ = SystemTypeName("bit"); - $$->typmod = 1; + $$->typmods = list_make1(makeIntConst(1)); } + $$->location = @1; } ; @@ -6670,7 +6579,7 @@ ConstCharacter: CharacterWithLength * was not specified. */ $$ = $1; - $$->typmod = -1; + $$->typmods = NIL; } ; @@ -6688,24 +6597,8 @@ CharacterWithLength: character '(' Iconst ')' opt_charset } $$ = SystemTypeName($1); - - if ($3 < 1) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("length for type %s must be at least 1", - $1))); - else if ($3 > MaxAttrSize) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("length for type %s cannot exceed %d", - $1, MaxAttrSize))); - - /* we actually implement these like a varlen, so - * the first 4 bytes is the length. (the difference - * between these and "text" is that we blank-pad and - * truncate where necessary) - */ - $$->typmod = VARHDRSZ + $3; + $$->typmods = list_make1(makeIntConst($3)); + $$->location = @1; } ; @@ -6726,9 +6619,9 @@ CharacterWithoutLength: character opt_charset /* char defaults to char(1), varchar to no limit */ if (strcmp($1, "bpchar") == 0) - $$->typmod = VARHDRSZ + 1; - else - $$->typmod = -1; + $$->typmods = list_make1(makeIntConst(1)); + + $$->location = @1; } ; @@ -6756,6 +6649,9 @@ opt_charset: | /*EMPTY*/ { $$ = NULL; } ; +/* + * SQL92 date/time types + */ ConstDatetime: TIMESTAMP '(' Iconst ')' opt_timezone { @@ -6767,21 +6663,8 @@ ConstDatetime: * - thomas 2001-09-06 */ $$->timezone = $5; - if ($3 < 0) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("TIMESTAMP(%d)%s precision must not be negative", - $3, ($5 ? " WITH TIME ZONE": "")))); - if ($3 > MAX_TIMESTAMP_PRECISION) - { - ereport(WARNING, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("TIMESTAMP(%d)%s precision reduced to maximum allowed, %d", - $3, ($5 ? " WITH TIME ZONE": ""), - MAX_TIMESTAMP_PRECISION))); - $3 = MAX_TIMESTAMP_PRECISION; - } - $$->typmod = $3; + $$->typmods = list_make1(makeIntConst($3)); + $$->location = @1; } | TIMESTAMP opt_timezone { @@ -6793,6 +6676,7 @@ ConstDatetime: * - thomas 2001-09-06 */ $$->timezone = $2; + $$->location = @1; } | TIME '(' Iconst ')' opt_timezone { @@ -6800,21 +6684,8 @@ ConstDatetime: $$ = SystemTypeName("timetz"); else $$ = SystemTypeName("time"); - if ($3 < 0) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("TIME(%d)%s precision must not be negative", - $3, ($5 ? " WITH TIME ZONE": "")))); - if ($3 > MAX_TIME_PRECISION) - { - ereport(WARNING, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("TIME(%d)%s precision reduced to maximum allowed, %d", - $3, ($5 ? " WITH TIME ZONE": ""), - MAX_TIME_PRECISION))); - $3 = MAX_TIME_PRECISION; - } - $$->typmod = $3; + $$->typmods = list_make1(makeIntConst($3)); + $$->location = @1; } | TIME opt_timezone { @@ -6822,11 +6693,16 @@ ConstDatetime: $$ = SystemTypeName("timetz"); else $$ = SystemTypeName("time"); + $$->location = @1; } ; ConstInterval: - INTERVAL { $$ = SystemTypeName("interval"); } + INTERVAL + { + $$ = SystemTypeName("interval"); + $$->location = @1; + } ; opt_timezone: @@ -7520,20 +7396,7 @@ func_expr: func_name '(' ')' s->val.val.str = "now"; s->typename = SystemTypeName("text"); d = SystemTypeName("timetz"); - if ($3 < 0) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("CURRENT_TIME(%d) precision must not be negative", - $3))); - if ($3 > MAX_TIME_PRECISION) - { - ereport(WARNING, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("CURRENT_TIME(%d) precision reduced to maximum allowed, %d", - $3, MAX_TIME_PRECISION))); - $3 = MAX_TIME_PRECISION; - } - d->typmod = $3; + d->typmods = list_make1(makeIntConst($3)); $$ = (Node *)makeTypeCast((Node *)s, d); } @@ -7565,20 +7428,7 @@ func_expr: func_name '(' ')' s->typename = SystemTypeName("text"); d = SystemTypeName("timestamptz"); - if ($3 < 0) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("CURRENT_TIMESTAMP(%d) precision must not be negative", - $3))); - if ($3 > MAX_TIMESTAMP_PRECISION) - { - ereport(WARNING, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("CURRENT_TIMESTAMP(%d) precision reduced to maximum allowed, %d", - $3, MAX_TIMESTAMP_PRECISION))); - $3 = MAX_TIMESTAMP_PRECISION; - } - d->typmod = $3; + d->typmods = list_make1(makeIntConst($3)); $$ = (Node *)makeTypeCast((Node *)s, d); } @@ -7612,20 +7462,7 @@ func_expr: func_name '(' ')' s->val.val.str = "now"; s->typename = SystemTypeName("text"); d = SystemTypeName("time"); - if ($3 < 0) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("LOCALTIME(%d) precision must not be negative", - $3))); - if ($3 > MAX_TIME_PRECISION) - { - ereport(WARNING, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("LOCALTIME(%d) precision reduced to maximum allowed, %d", - $3, MAX_TIME_PRECISION))); - $3 = MAX_TIME_PRECISION; - } - d->typmod = $3; + d->typmods = list_make1(makeIntConst($3)); $$ = (Node *)makeTypeCast((Node *)s, d); } @@ -7660,20 +7497,7 @@ func_expr: func_name '(' ')' s->typename = SystemTypeName("text"); d = SystemTypeName("timestamp"); - if ($3 < 0) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("LOCALTIMESTAMP(%d) precision must not be negative", - $3))); - if ($3 > MAX_TIMESTAMP_PRECISION) - { - ereport(WARNING, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("LOCALTIMESTAMP(%d) precision reduced to maximum allowed, %d", - $3, MAX_TIMESTAMP_PRECISION))); - $3 = MAX_TIMESTAMP_PRECISION; - } - d->typmod = $3; + d->typmods = list_make1(makeIntConst($3)); $$ = (Node *)makeTypeCast((Node *)s, d); } @@ -7880,7 +7704,7 @@ func_expr: func_name '(' ')' $$ = (Node *)v; } | XMLCONCAT '(' expr_list ')' - { + { $$ = makeXmlExpr(IS_XMLCONCAT, NULL, NIL, $3); } | XMLELEMENT '(' NAME_P ColLabel ')' @@ -7985,7 +7809,7 @@ xml_attribute_el: a_expr AS ColLabel $$ = makeNode(ResTarget); $$->name = NULL; $$->indirection = NULL; - $$->val = (Node *) $1; + $$->val = (Node *) $1; $$->location = @1; } ; @@ -8486,12 +8310,12 @@ file_name: Sconst { $$ = $1; }; /* * The production for a qualified func_name has to exactly match the * production for a qualified columnref, because we cannot tell which we - * are parsing until we see what comes after it ('(' for a func_name, + * are parsing until we see what comes after it ('(' or Sconst for a func_name, * anything else for a columnref). Therefore we allow 'indirection' which * may contain subscripts, and reject that case in the C code. (If we * ever implement SQL99-like methods, such syntax may actually become legal!) */ -func_name: function_name +func_name: type_function_name { $$ = list_make1(makeString($1)); } | relation_name indirection { $$ = check_func_name(lcons(makeString($1), $2)); } @@ -8541,6 +8365,27 @@ AexprConst: Iconst n->val.val.str = $1; $$ = (Node *)n; } + | func_name Sconst + { + /* generic type 'literal' syntax */ + A_Const *n = makeNode(A_Const); + n->typename = makeTypeNameFromNameList($1); + n->typename->location = @1; + n->val.type = T_String; + n->val.val.str = $2; + $$ = (Node *)n; + } + | func_name '(' expr_list ')' Sconst + { + /* generic syntax with a type modifier */ + A_Const *n = makeNode(A_Const); + n->typename = makeTypeNameFromNameList($1); + n->typename->typmods = $3; + n->typename->location = @1; + n->val.type = T_String; + n->val.val.str = $5; + $$ = (Node *)n; + } | ConstTypename Sconst { A_Const *n = makeNode(A_Const); @@ -8557,7 +8402,7 @@ AexprConst: Iconst n->val.val.str = $2; /* precision is not specified, but fields may be... */ if ($3 != INTERVAL_FULL_RANGE) - n->typename->typmod = INTERVAL_TYPMOD(INTERVAL_FULL_PRECISION, $3); + n->typename->typmods = list_make1(makeIntConst($3)); $$ = (Node *)n; } | ConstInterval '(' Iconst ')' Sconst opt_interval @@ -8566,21 +8411,8 @@ AexprConst: Iconst n->typename = $1; n->val.type = T_String; n->val.val.str = $5; - /* precision specified, and fields may be... */ - if ($3 < 0) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("INTERVAL(%d) precision must not be negative", - $3))); - if ($3 > MAX_INTERVAL_PRECISION) - { - ereport(WARNING, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("INTERVAL(%d) precision reduced to maximum allowed, %d", - $3, MAX_INTERVAL_PRECISION))); - $3 = MAX_INTERVAL_PRECISION; - } - n->typename->typmod = INTERVAL_TYPMOD($3, $6); + n->typename->typmods = list_make2(makeIntConst($6), + makeIntConst($3)); $$ = (Node *)n; } | TRUE_P @@ -8625,18 +8457,11 @@ ColId: IDENT { $$ = $1; } | col_name_keyword { $$ = pstrdup($1); } ; -/* Type identifier --- names that can be type names. - */ -type_name: IDENT { $$ = $1; } - | unreserved_keyword { $$ = pstrdup($1); } - ; - -/* Function identifier --- names that can be function names. +/* Type/function identifier --- names that can be type or function names. */ -function_name: - IDENT { $$ = $1; } +type_function_name: IDENT { $$ = $1; } | unreserved_keyword { $$ = pstrdup($1); } - | func_name_keyword { $$ = pstrdup($1); } + | type_func_name_keyword { $$ = pstrdup($1); } ; /* Column label --- allowed labels in "AS" clauses. @@ -8645,7 +8470,7 @@ function_name: ColLabel: IDENT { $$ = $1; } | unreserved_keyword { $$ = pstrdup($1); } | col_name_keyword { $$ = pstrdup($1); } - | func_name_keyword { $$ = pstrdup($1); } + | type_func_name_keyword { $$ = pstrdup($1); } | reserved_keyword { $$ = pstrdup($1); } ; @@ -8940,7 +8765,7 @@ col_name_keyword: | XMLSERIALIZE ; -/* Function identifier --- keywords that can be function names. +/* Type/function identifier --- keywords that can be type or function names. * * Most of these are keywords that are used as operators in expressions; * in general such keywords can't be column names because they would be @@ -8950,7 +8775,7 @@ col_name_keyword: * productions in a_expr to support the goofy SQL9x argument syntax. * - thomas 2000-11-28 */ -func_name_keyword: +type_func_name_keyword: AUTHORIZATION | BETWEEN | BINARY @@ -9383,12 +9208,8 @@ SystemFuncName(char *name) TypeName * SystemTypeName(char *name) { - TypeName *n = makeNode(TypeName); - - n->names = list_make2(makeString("pg_catalog"), makeString(name)); - n->typmod = -1; - n->location = -1; - return n; + return makeTypeNameFromNameList(list_make2(makeString("pg_catalog"), + makeString(name))); } /* parser_init() diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 7dbbb9a33a8..383013c9a10 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.202 2006/12/24 00:29:19 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.203 2006/12/30 21:21:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1810,29 +1810,6 @@ exprTypmod(Node *expr) { case T_Var: return ((Var *) expr)->vartypmod; - case T_Const: - { - /* Be smart about string constants... */ - Const *con = (Const *) expr; - - switch (con->consttype) - { - case BPCHAROID: - if (!con->constisnull) - { - int32 len = VARSIZE(DatumGetPointer(con->constvalue)) - VARHDRSZ; - - /* if multi-byte, take len and find # characters */ - if (pg_database_encoding_max_length() > 1) - len = pg_mbstrlen_with_len(VARDATA(DatumGetPointer(con->constvalue)), len); - return len + VARHDRSZ; - } - break; - default: - break; - } - } - break; case T_Param: return ((Param *) expr)->paramtypmod; case T_FuncExpr: @@ -2024,14 +2001,16 @@ typecast_expression(ParseState *pstate, Node *expr, TypeName *typename) { Oid inputType = exprType(expr); Oid targetType; + int32 targetTypmod; targetType = typenameTypeId(pstate, typename); + targetTypmod = typenameTypeMod(pstate, typename, targetType); if (inputType == InvalidOid) return expr; /* do nothing if NULL input */ expr = coerce_to_target_type(pstate, expr, inputType, - targetType, typename->typmod, + targetType, targetTypmod, COERCION_EXPLICIT, COERCE_EXPLICIT_CAST); if (expr == NULL) diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c index 75d5a50702a..f6d4fcae4aa 100644 --- a/src/backend/parser/parse_relation.c +++ b/src/backend/parser/parse_relation.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.125 2006/10/04 00:29:56 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.126 2006/12/30 21:21:54 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -901,9 +901,9 @@ addRangeTableEntryForFunction(ParseState *pstate, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("column \"%s\" cannot be declared SETOF", attrname))); - eref->colnames = lappend(eref->colnames, makeString(attrname)); attrtype = typenameTypeId(pstate, n->typename); - attrtypmod = n->typename->typmod; + attrtypmod = typenameTypeMod(pstate, n->typename, attrtype); + eref->colnames = lappend(eref->colnames, makeString(attrname)); rte->funccoltypes = lappend_oid(rte->funccoltypes, attrtype); rte->funccoltypmods = lappend_int(rte->funccoltypmods, attrtypmod); } diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c index 93c7db6b52a..6aeabb57498 100644 --- a/src/backend/parser/parse_type.c +++ b/src/backend/parser/parse_type.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.85 2006/10/04 00:29:56 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.86 2006/12/30 21:21:54 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -20,6 +20,7 @@ #include "nodes/makefuncs.h" #include "parser/parser.h" #include "parser/parse_type.h" +#include "utils/array.h" #include "utils/builtins.h" #include "utils/lsyscache.h" #include "utils/syscache.h" @@ -245,10 +246,82 @@ typenameTypeId(ParseState *pstate, const TypeName *typename) errmsg("type \"%s\" is only a shell", TypeNameToString(typename)), parser_errposition(pstate, typename->location))); + return typoid; } /* + * typenameTypeMod - given a TypeName, return the internal typmod value + * + * This will throw an error if the TypeName includes type modifiers that are + * illegal for the data type. + * + * The actual type OID represented by the TypeName must already have been + * determined (usually by typenameTypeId()), and is passed as typeId. + * + * pstate is only used for error location info, and may be NULL. + */ +int32 +typenameTypeMod(ParseState *pstate, const TypeName *typename, + Oid typeId) +{ + int32 result; + Oid typmodin; + Datum *datums; + int n; + ListCell *l; + ArrayType *arrtypmod; + + Assert(OidIsValid(typeId)); + + /* Return prespecified typmod if no typmod expressions */ + if (typename->typmods == NIL) + return typename->typemod; + + /* Else, type had better accept typmods */ + typmodin = get_typmodin(typeId); + + if (typmodin == InvalidOid) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("type modifier is not allowed for type \"%s\"", + TypeNameToString(typename)), + parser_errposition(pstate, typename->location))); + + /* + * Convert the list of (raw grammar output) expressions to an integer + * array. Currently, we only allow simple integer constants, though + * possibly this could be extended. + */ + datums = (Datum *) palloc(list_length(typename->typmods) * sizeof(Datum)); + n = 0; + foreach(l, typename->typmods) + { + A_Const *ac = (A_Const *) lfirst(l); + + if (!IsA(ac, A_Const) || + !IsA(&ac->val, Integer)) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("type modifiers must be integer constants"), + parser_errposition(pstate, typename->location))); + datums[n++] = Int32GetDatum(ac->val.val.ival); + } + + /* hardwired knowledge about int4's representation details here */ + arrtypmod = construct_array(datums, n, INT4OID, + sizeof(int4), true, 'i'); + + result = DatumGetInt32(OidFunctionCall1(typmodin, + PointerGetDatum(arrtypmod))); + + pfree(datums); + pfree(arrtypmod); + + return result; +} + +/* * typenameType - given a TypeName, return a Type structure * * This is equivalent to typenameTypeId + syscache fetch of Type tuple. @@ -490,7 +563,7 @@ parseTypeString(const char *str, Oid *type_id, int32 *typmod) goto fail; *type_id = typenameTypeId(NULL, typename); - *typmod = typename->typmod; + *typmod = typenameTypeMod(NULL, typename, *type_id); pfree(buf.data); diff --git a/src/backend/utils/adt/arrayutils.c b/src/backend/utils/adt/arrayutils.c index 2a732ad63bb..2913d0a1d8b 100644 --- a/src/backend/utils/adt/arrayutils.c +++ b/src/backend/utils/adt/arrayutils.c @@ -8,13 +8,14 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/arrayutils.c,v 1.21 2006/03/05 15:58:41 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/arrayutils.c,v 1.22 2006/12/30 21:21:54 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" +#include "catalog/pg_type.h" #include "utils/array.h" #include "utils/memutils.h" @@ -188,3 +189,30 @@ mda_next_tuple(int n, int *curr, const int *span) return -1; } + +/* + * ArrayGetTypmods: verify that argument is a 1-D integer array, + * return its length and a pointer to the first contained integer. + */ +int32 * +ArrayGetTypmods(ArrayType *arr, int *n) +{ + if (ARR_ELEMTYPE(arr) != INT4OID) + ereport(ERROR, + (errcode(ERRCODE_ARRAY_ELEMENT_ERROR), + errmsg("typmod array must be type integer[]"))); + + if (ARR_NDIM(arr) != 1) + ereport(ERROR, + (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), + errmsg("typmod array must be one-dimensional"))); + + if (ARR_HASNULL(arr)) + ereport(ERROR, + (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), + errmsg("typmod array must not contain nulls"))); + + *n = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr)); + + return (int32 *) ARR_DATA_PTR(arr); +} diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c index 9efc7125b19..e2781dec485 100644 --- a/src/backend/utils/adt/date.c +++ b/src/backend/utils/adt/date.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.125 2006/07/14 14:52:23 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.126 2006/12/30 21:21:54 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -24,6 +24,7 @@ #include "libpq/pqformat.h" #include "miscadmin.h" #include "parser/scansup.h" +#include "utils/array.h" #include "utils/builtins.h" #include "utils/date.h" #include "utils/nabstime.h" @@ -43,6 +44,60 @@ static int tm2time(struct pg_tm * tm, fsec_t fsec, TimeADT *result); static int tm2timetz(struct pg_tm * tm, fsec_t fsec, int tz, TimeTzADT *result); static void AdjustTimeForTypmod(TimeADT *time, int32 typmod); + +/* common code for timetypmodin and timetztypmodin */ +static int32 +anytime_typmodin(bool istz, ArrayType *ta) +{ + int32 typmod; + int32 *tl; + int n; + + tl = ArrayGetTypmods(ta, &n); + + /* + * we're not too tense about good error message here because grammar + * shouldn't allow wrong number of modifiers for TIME + */ + if (n != 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid type modifier"))); + + if (*tl < 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("TIME(%d)%s precision must not be negative", + *tl, (istz ? " WITH TIME ZONE" : "")))); + if (*tl > MAX_TIME_PRECISION) + { + ereport(WARNING, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("TIME(%d)%s precision reduced to maximum allowed, %d", + *tl, (istz ? " WITH TIME ZONE" : "" ), + MAX_TIME_PRECISION))); + typmod = MAX_TIME_PRECISION; + } else + typmod = *tl; + + return typmod; +} + +/* common code for timetypmodout and timetztypmodout */ +static char * +anytime_typmodout(bool istz, int32 typmod) +{ + char *res = (char *) palloc(64); + const char *tz = istz ? " with time zone" : " without time zone"; + + if (typmod >= 0) + snprintf(res, 64, "(%d)%s", (int) typmod, tz); + else + snprintf(res, 64, "%s", tz); + return res; +} + + /***************************************************************************** * Date ADT *****************************************************************************/ @@ -1029,6 +1084,22 @@ time_send(PG_FUNCTION_ARGS) PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); } +Datum +timetypmodin(PG_FUNCTION_ARGS) +{ + ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0); + + PG_RETURN_INT32(anytime_typmodin(false, ta)); +} + +Datum +timetypmodout(PG_FUNCTION_ARGS) +{ + int32 typmod = PG_GETARG_INT32(0); + + PG_RETURN_CSTRING(anytime_typmodout(false, typmod)); +} + /* time_scale() * Adjust time type for specified scale factor. @@ -1830,6 +1901,22 @@ timetz_send(PG_FUNCTION_ARGS) PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); } +Datum +timetztypmodin(PG_FUNCTION_ARGS) +{ + ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0); + + PG_RETURN_INT32(anytime_typmodin(true, ta)); +} + +Datum +timetztypmodout(PG_FUNCTION_ARGS) +{ + int32 typmod = PG_GETARG_INT32(0); + + PG_RETURN_CSTRING(anytime_typmodout(true, typmod)); +} + /* timetz2tm() * Convert TIME WITH TIME ZONE data type to POSIX time structure. diff --git a/src/backend/utils/adt/format_type.c b/src/backend/utils/adt/format_type.c index 22d9bc156f6..d2b6323f0f2 100644 --- a/src/backend/utils/adt/format_type.c +++ b/src/backend/utils/adt/format_type.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/format_type.c,v 1.44 2006/07/14 14:52:24 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/format_type.c,v 1.45 2006/12/30 21:21:54 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -20,7 +20,6 @@ #include "catalog/namespace.h" #include "catalog/pg_type.h" #include "utils/builtins.h" -#include "utils/datetime.h" #include "utils/lsyscache.h" #include "utils/numeric.h" #include "utils/syscache.h" @@ -31,6 +30,7 @@ static char *format_type_internal(Oid type_oid, int32 typemod, bool typemod_given, bool allow_invalid); +static char *printTypmod(const char *typname, int32 typmod, Oid typmodout); static char * psnprintf(size_t len, const char *fmt,...) /* This lets gcc check the format string for consistency. */ @@ -186,8 +186,7 @@ format_type_internal(Oid type_oid, int32 typemod, { case BITOID: if (with_typemod) - buf = psnprintf(5 + MAX_INT32_LEN + 1, "bit(%d)", - (int) typemod); + buf = printTypmod("bit", typemod, typeform->typmodout); else if (typemod_given) { /* @@ -206,8 +205,7 @@ format_type_internal(Oid type_oid, int32 typemod, case BPCHAROID: if (with_typemod) - buf = psnprintf(11 + MAX_INT32_LEN + 1, "character(%d)", - (int) (typemod - VARHDRSZ)); + buf = printTypmod("character", typemod, typeform->typmodout); else if (typemod_given) { /* @@ -242,136 +240,56 @@ format_type_internal(Oid type_oid, int32 typemod, case NUMERICOID: if (with_typemod) - buf = psnprintf(10 + 2 * MAX_INT32_LEN + 1, "numeric(%d,%d)", - ((typemod - VARHDRSZ) >> 16) & 0xffff, - (typemod - VARHDRSZ) & 0xffff); + buf = printTypmod("numeric", typemod, typeform->typmodout); else buf = pstrdup("numeric"); break; case INTERVALOID: if (with_typemod) - { - int fields = INTERVAL_RANGE(typemod); - int precision = INTERVAL_PRECISION(typemod); - const char *fieldstr; - - switch (fields) - { - case INTERVAL_MASK(YEAR): - fieldstr = " year"; - break; - case INTERVAL_MASK(MONTH): - fieldstr = " month"; - break; - case INTERVAL_MASK(DAY): - fieldstr = " day"; - break; - case INTERVAL_MASK(HOUR): - fieldstr = " hour"; - break; - case INTERVAL_MASK(MINUTE): - fieldstr = " minute"; - break; - case INTERVAL_MASK(SECOND): - fieldstr = " second"; - break; - case INTERVAL_MASK(YEAR) - | INTERVAL_MASK(MONTH): - fieldstr = " year to month"; - break; - case INTERVAL_MASK(DAY) - | INTERVAL_MASK(HOUR): - fieldstr = " day to hour"; - break; - case INTERVAL_MASK(DAY) - | INTERVAL_MASK(HOUR) - | INTERVAL_MASK(MINUTE): - fieldstr = " day to minute"; - break; - case INTERVAL_MASK(DAY) - | INTERVAL_MASK(HOUR) - | INTERVAL_MASK(MINUTE) - | INTERVAL_MASK(SECOND): - fieldstr = " day to second"; - break; - case INTERVAL_MASK(HOUR) - | INTERVAL_MASK(MINUTE): - fieldstr = " hour to minute"; - break; - case INTERVAL_MASK(HOUR) - | INTERVAL_MASK(MINUTE) - | INTERVAL_MASK(SECOND): - fieldstr = " hour to second"; - break; - case INTERVAL_MASK(MINUTE) - | INTERVAL_MASK(SECOND): - fieldstr = " minute to second"; - break; - case INTERVAL_FULL_RANGE: - fieldstr = ""; - break; - default: - elog(ERROR, "invalid INTERVAL typmod: 0x%x", typemod); - fieldstr = ""; - break; - } - if (precision != INTERVAL_FULL_PRECISION) - buf = psnprintf(100, "interval(%d)%s", - precision, fieldstr); - else - buf = psnprintf(100, "interval%s", - fieldstr); - } + buf = printTypmod("interval", typemod, typeform->typmodout); else buf = pstrdup("interval"); break; case TIMEOID: if (with_typemod) - buf = psnprintf(50, "time(%d) without time zone", - typemod); + buf = printTypmod("time", typemod, typeform->typmodout); else buf = pstrdup("time without time zone"); break; case TIMETZOID: if (with_typemod) - buf = psnprintf(50, "time(%d) with time zone", - typemod); + buf = printTypmod("time", typemod, typeform->typmodout); else buf = pstrdup("time with time zone"); break; case TIMESTAMPOID: if (with_typemod) - buf = psnprintf(50, "timestamp(%d) without time zone", - typemod); + buf = printTypmod("timestamp", typemod, typeform->typmodout); else buf = pstrdup("timestamp without time zone"); break; case TIMESTAMPTZOID: if (with_typemod) - buf = psnprintf(50, "timestamp(%d) with time zone", - typemod); + buf = printTypmod("timestamp", typemod, typeform->typmodout); else buf = pstrdup("timestamp with time zone"); break; case VARBITOID: if (with_typemod) - buf = psnprintf(13 + MAX_INT32_LEN + 1, "bit varying(%d)", - (int) typemod); + buf = printTypmod("bit varying", typemod, typeform->typmodout); else buf = pstrdup("bit varying"); break; case VARCHAROID: if (with_typemod) - buf = psnprintf(19 + MAX_INT32_LEN + 1, - "character varying(%d)", - (int) (typemod - VARHDRSZ)); + buf = printTypmod("character varying", typemod, typeform->typmodout); else buf = pstrdup("character varying"); break; @@ -396,6 +314,9 @@ format_type_internal(Oid type_oid, int32 typemod, typname = NameStr(typeform->typname); buf = quote_qualified_identifier(nspname, typname); + + if (with_typemod) + buf = printTypmod(buf, typemod, typeform->typmodout); } if (is_array) @@ -408,6 +329,38 @@ format_type_internal(Oid type_oid, int32 typemod, /* + * Add typmod decoration to the basic type name + */ +static char * +printTypmod(const char *typname, int32 typmod, Oid typmodout) +{ + char *res; + + /* Shouldn't be called if typmod is -1 */ + Assert(typmod >= 0); + + if (typmodout == InvalidOid) + { + /* Default behavior: just print the integer typmod with parens */ + res = psnprintf(strlen(typname) + MAX_INT32_LEN + 3, "%s(%d)", + typname, (int) typmod); + } + else + { + /* Use the type-specific typmodout procedure */ + char *tmstr; + + tmstr = DatumGetCString(OidFunctionCall1(typmodout, + Int32GetDatum(typmod))); + res = psnprintf(strlen(typname) + strlen(tmstr) + 1, "%s%s", + typname, tmstr); + } + + return res; +} + + +/* * type_maximum_size --- determine maximum width of a variable-width column * * If the max width is indeterminate, return -1. In particular, we return @@ -417,7 +370,9 @@ format_type_internal(Oid type_oid, int32 typemod, * * This may appear unrelated to format_type(), but in fact the two routines * share knowledge of the encoding of typmod for different types, so it's - * convenient to keep them together. + * convenient to keep them together. (XXX now that most of this knowledge + * has been pushed out of format_type into the typmodout functions, it's + * interesting to wonder if it's worth trying to factor this code too...) */ int32 type_maximum_size(Oid type_oid, int32 typemod) diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c index 35b0221b85d..11dd881011f 100644 --- a/src/backend/utils/adt/numeric.c +++ b/src/backend/utils/adt/numeric.c @@ -14,7 +14,7 @@ * Copyright (c) 1998-2006, PostgreSQL Global Development Group * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/numeric.c,v 1.96 2006/10/04 00:29:59 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/numeric.c,v 1.97 2006/12/30 21:21:54 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -470,7 +470,7 @@ numeric_send(PG_FUNCTION_ARGS) * scale of the attribute have to be applied on the value. */ Datum -numeric (PG_FUNCTION_ARGS) +numeric(PG_FUNCTION_ARGS) { Numeric num = PG_GETARG_NUMERIC(0); int32 typmod = PG_GETARG_INT32(1); @@ -537,6 +537,67 @@ numeric (PG_FUNCTION_ARGS) PG_RETURN_NUMERIC(new); } +Datum +numerictypmodin(PG_FUNCTION_ARGS) +{ + ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0); + int32 *tl; + int n; + int32 typmod; + + tl = ArrayGetTypmods(ta, &n); + + if (n == 2) + { + if (tl[0] < 1 || tl[0] > NUMERIC_MAX_PRECISION) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("NUMERIC precision %d must be between 1 and %d", + tl[0], NUMERIC_MAX_PRECISION))); + if (tl[1] < 0 || tl[1] > tl[0]) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("NUMERIC scale %d must be between 0 and precision %d", + tl[1], tl[0]))); + typmod = ((tl[0] << 16) | tl[1]) + VARHDRSZ; + } + else if (n == 1) + { + if (tl[0] < 1 || tl[0] > NUMERIC_MAX_PRECISION) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("NUMERIC precision %d must be between 1 and %d", + tl[0], NUMERIC_MAX_PRECISION))); + /* scale defaults to zero */ + typmod = (tl[0] << 16) + VARHDRSZ; + } + else + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid NUMERIC type modifier"))); + typmod = 0; /* keep compiler quiet */ + } + + PG_RETURN_INT32(typmod); +} + +Datum +numerictypmodout(PG_FUNCTION_ARGS) +{ + int32 typmod = PG_GETARG_INT32(0); + char *res = (char *) palloc(64); + + if (typmod >= 0) + snprintf(res, 64, "(%d,%d)", + ((typmod - VARHDRSZ) >> 16) & 0xffff, + (typmod - VARHDRSZ) & 0xffff); + else + *res = '\0'; + + PG_RETURN_CSTRING(res); +} + /* ---------------------------------------------------------------------- * diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c index f94413e3f32..f9b0bb2c992 100644 --- a/src/backend/utils/adt/timestamp.c +++ b/src/backend/utils/adt/timestamp.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.169 2006/11/11 01:14:19 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.170 2006/12/30 21:21:54 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -56,6 +56,60 @@ static void AdjustIntervalForTypmod(Interval *interval, int32 typmod); static TimestampTz timestamp2timestamptz(Timestamp timestamp); +/* common code for timestamptypmodin and timestamptztypmodin */ +static int32 +anytimestamp_typmodin(bool istz, ArrayType *ta) +{ + int32 typmod; + int32 *tl; + int n; + + tl = ArrayGetTypmods(ta, &n); + + /* + * we're not too tense about good error message here because grammar + * shouldn't allow wrong number of modifiers for TIMESTAMP + */ + if (n != 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid type modifier"))); + + if (*tl < 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("TIMESTAMP(%d)%s precision must not be negative", + *tl, (istz ? " WITH TIME ZONE" : "")))); + if (*tl > MAX_TIMESTAMP_PRECISION) + { + ereport(WARNING, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("TIMESTAMP(%d)%s precision reduced to maximum allowed, %d", + *tl, (istz ? " WITH TIME ZONE" : ""), + MAX_TIMESTAMP_PRECISION))); + typmod = MAX_TIMESTAMP_PRECISION; + } else + typmod = *tl; + + return typmod; +} + +/* common code for timestamptypmodout and timestamptztypmodout */ +static char * +anytimestamp_typmodout(bool istz, int32 typmod) +{ + char *res = (char *) palloc(64); + const char *tz = istz ? " with time zone" : " without time zone"; + + if (typmod >= 0) + snprintf(res, 64, "(%d)%s", (int) typmod, tz); + else + snprintf(res, 64, "%s", tz); + + return res; +} + + /***************************************************************************** * USER I/O ROUTINES * *****************************************************************************/ @@ -215,6 +269,22 @@ timestamp_send(PG_FUNCTION_ARGS) PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); } +Datum +timestamptypmodin(PG_FUNCTION_ARGS) +{ + ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0); + + PG_RETURN_INT32(anytimestamp_typmodin(false, ta)); +} + +Datum +timestamptypmodout(PG_FUNCTION_ARGS) +{ + int32 typmod = PG_GETARG_INT32(0); + + PG_RETURN_CSTRING(anytimestamp_typmodout(false, typmod)); +} + /* timestamp_scale() * Adjust time type for specified scale factor. @@ -461,6 +531,22 @@ timestamptz_send(PG_FUNCTION_ARGS) PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); } +Datum +timestamptztypmodin(PG_FUNCTION_ARGS) +{ + ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0); + + PG_RETURN_INT32(anytimestamp_typmodin(true, ta)); +} + +Datum +timestamptztypmodout(PG_FUNCTION_ARGS) +{ + int32 typmod = PG_GETARG_INT32(0); + + PG_RETURN_CSTRING(anytimestamp_typmodout(true, typmod)); +} + /* timestamptz_scale() * Adjust time type for specified scale factor. @@ -625,6 +711,162 @@ interval_send(PG_FUNCTION_ARGS) PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); } +Datum +intervaltypmodin(PG_FUNCTION_ARGS) +{ + ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0); + int32 *tl; + int n; + int32 typmod; + + tl = ArrayGetTypmods(ta, &n); + + /* + * tl[0] - opt_interval + * tl[1] - Iconst (optional) + * + * Note we must validate tl[0] even though it's normally guaranteed + * correct by the grammar --- consider SELECT 'foo'::"interval"(1000). + */ + if (n > 0) + { + switch (tl[0]) + { + case INTERVAL_MASK(YEAR): + case INTERVAL_MASK(MONTH): + case INTERVAL_MASK(DAY): + case INTERVAL_MASK(HOUR): + case INTERVAL_MASK(MINUTE): + case INTERVAL_MASK(SECOND): + case INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH): + case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR): + case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE): + case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND): + case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE): + case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND): + case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND): + case INTERVAL_FULL_RANGE: + /* all OK */ + break; + default: + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid INTERVAL type modifier"))); + } + } + + if (n == 1) + { + if (tl[0] != INTERVAL_FULL_RANGE) + typmod = INTERVAL_TYPMOD(INTERVAL_FULL_PRECISION, tl[0]); + else + typmod = -1; + } + else if (n == 2) + { + if (tl[1] < 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("INTERVAL(%d) precision must not be negative", + tl[1]))); + if (tl[1] > MAX_INTERVAL_PRECISION) + { + ereport(WARNING, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("INTERVAL(%d) precision reduced to maximum allowed, %d", + tl[1], MAX_INTERVAL_PRECISION))); + typmod = INTERVAL_TYPMOD(MAX_INTERVAL_PRECISION, tl[0]); + } + else + typmod = INTERVAL_TYPMOD(tl[1], tl[0]); + } + else + { + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid INTERVAL type modifier"))); + typmod = 0; /* keep compiler quiet */ + } + + PG_RETURN_INT32(typmod); +} + +Datum +intervaltypmodout(PG_FUNCTION_ARGS) +{ + int32 typmod = PG_GETARG_INT32(0); + char *res = (char *) palloc(64); + int fields; + int precision; + const char *fieldstr; + + if (typmod < 0) + { + *res = '\0'; + PG_RETURN_CSTRING(res); + } + + fields = INTERVAL_RANGE(typmod); + precision = INTERVAL_PRECISION(typmod); + + switch (fields) + { + case INTERVAL_MASK(YEAR): + fieldstr = " year"; + break; + case INTERVAL_MASK(MONTH): + fieldstr = " month"; + break; + case INTERVAL_MASK(DAY): + fieldstr = " day"; + break; + case INTERVAL_MASK(HOUR): + fieldstr = " hour"; + break; + case INTERVAL_MASK(MINUTE): + fieldstr = " minute"; + break; + case INTERVAL_MASK(SECOND): + fieldstr = " second"; + break; + case INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH): + fieldstr = " year to month"; + break; + case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR): + fieldstr = " day to hour"; + break; + case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE): + fieldstr = " day to minute"; + break; + case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND): + fieldstr = " day to second"; + break; + case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE): + fieldstr = " hour to minute"; + break; + case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND): + fieldstr = " hour to second"; + break; + case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND): + fieldstr = " minute to second"; + break; + case INTERVAL_FULL_RANGE: + fieldstr = ""; + break; + default: + elog(ERROR, "invalid INTERVAL typmod: 0x%x", typmod); + fieldstr = ""; + break; + } + + if (precision != INTERVAL_FULL_PRECISION) + snprintf(res, 64, "(%d)%s", precision, fieldstr); + else + snprintf(res, 64, "%s", fieldstr); + + PG_RETURN_CSTRING(res); +} + /* interval_scale() * Adjust interval type for specified fields. diff --git a/src/backend/utils/adt/varbit.c b/src/backend/utils/adt/varbit.c index e0a67d340ef..4a810b955da 100644 --- a/src/backend/utils/adt/varbit.c +++ b/src/backend/utils/adt/varbit.c @@ -9,19 +9,71 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/varbit.c,v 1.50 2006/07/14 14:52:24 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/varbit.c,v 1.51 2006/12/30 21:21:54 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" +#include "access/htup.h" #include "libpq/pqformat.h" +#include "utils/array.h" #include "utils/varbit.h" #define HEXDIG(z) ((z)<10 ? ((z)+'0') : ((z)-10+'A')) +/* common code for bittypmodin and varbittypmodin */ +static int32 +anybit_typmodin(ArrayType *ta, const char *typename) +{ + int32 typmod; + int32 *tl; + int n; + + tl = ArrayGetTypmods(ta, &n); + + /* + * we're not too tense about good error message here because grammar + * shouldn't allow wrong number of modifiers for BIT + */ + if (n != 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid type modifier"))); + + if (*tl < 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("length for type %s must be at least 1", + typename))); + if (*tl > (MaxAttrSize * BITS_PER_BYTE)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("length for type %s cannot exceed %d", + typename, MaxAttrSize * BITS_PER_BYTE))); + + typmod = *tl; + + return typmod; +} + +/* common code for bittypmodout and varbittypmodout */ +static char * +anybit_typmodout(int32 typmod) +{ + char *res = (char *) palloc(64); + + if (typmod >= 0) + snprintf(res, 64, "(%d)", typmod); + else + *res = '\0'; + + return res; +} + + /*---------- * attypmod -- contains the length of the bit string in bits, or for * varying bits the maximum length. @@ -325,6 +377,23 @@ bit(PG_FUNCTION_ARGS) PG_RETURN_VARBIT_P(result); } +Datum +bittypmodin(PG_FUNCTION_ARGS) +{ + ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0); + + PG_RETURN_INT32(anybit_typmodin(ta, "bit")); +} + +Datum +bittypmodout(PG_FUNCTION_ARGS) +{ + int32 typmod = PG_GETARG_INT32(0); + + PG_RETURN_CSTRING(anybit_typmodout(typmod)); +} + + /* * varbit_in - * converts a string to the internal representation of a bitstring. @@ -603,6 +672,22 @@ varbit(PG_FUNCTION_ARGS) PG_RETURN_VARBIT_P(result); } +Datum +varbittypmodin(PG_FUNCTION_ARGS) +{ + ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0); + + PG_RETURN_INT32(anybit_typmodin(ta, "varbit")); +} + +Datum +varbittypmodout(PG_FUNCTION_ARGS) +{ + int32 typmod = PG_GETARG_INT32(0); + + PG_RETURN_CSTRING(anybit_typmodout(typmod)); +} + /* * Comparison operators diff --git a/src/backend/utils/adt/varchar.c b/src/backend/utils/adt/varchar.c index 937cf96ebef..9cc2f5e34e3 100644 --- a/src/backend/utils/adt/varchar.c +++ b/src/backend/utils/adt/varchar.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/varchar.c,v 1.119 2006/10/04 00:30:00 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/varchar.c,v 1.120 2006/12/30 21:21:54 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,10 +17,65 @@ #include "access/hash.h" #include "libpq/pqformat.h" +#include "utils/array.h" #include "utils/builtins.h" #include "mb/pg_wchar.h" +/* common code for bpchartypmodin and varchartypmodin */ +static int32 +anychar_typmodin(ArrayType *ta, const char *typename) +{ + int32 typmod; + int32 *tl; + int n; + + tl = ArrayGetTypmods(ta, &n); + + /* + * we're not too tense about good error message here because grammar + * shouldn't allow wrong number of modifiers for CHAR + */ + if (n != 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("invalid type modifier"))); + + if (*tl < 1) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("length for type %s must be at least 1", typename))); + if (*tl > MaxAttrSize) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("length for type %s cannot exceed %d", + typename, MaxAttrSize))); + + /* + * For largely historical reasons, the typmod is VARHDRSZ plus the + * number of characters; there is enough client-side code that knows + * about that that we'd better not change it. + */ + typmod = VARHDRSZ + *tl; + + return typmod; +} + +/* common code for bpchartypmodout and varchartypmodout */ +static char * +anychar_typmodout(int32 typmod) +{ + char *res = (char *) palloc(64); + + if (typmod > VARHDRSZ) + snprintf(res, 64, "(%d)", (int) (typmod - VARHDRSZ)); + else + *res = '\0'; + + return res; +} + + /* * CHAR() and VARCHAR() types are part of the ANSI SQL standard. CHAR() * is for blank-padded string whose length is specified in CREATE TABLE. @@ -359,6 +414,22 @@ name_bpchar(PG_FUNCTION_ARGS) PG_RETURN_BPCHAR_P(result); } +Datum +bpchartypmodin(PG_FUNCTION_ARGS) +{ + ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0); + + PG_RETURN_INT32(anychar_typmodin(ta, "char")); +} + +Datum +bpchartypmodout(PG_FUNCTION_ARGS) +{ + int32 typmod = PG_GETARG_INT32(0); + + PG_RETURN_CSTRING(anychar_typmodout(typmod)); +} + /***************************************************************************** * varchar - varchar(n) @@ -536,6 +607,22 @@ varchar(PG_FUNCTION_ARGS) PG_RETURN_VARCHAR_P(result); } +Datum +varchartypmodin(PG_FUNCTION_ARGS) +{ + ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0); + + PG_RETURN_INT32(anychar_typmodin(ta, "varchar")); +} + +Datum +varchartypmodout(PG_FUNCTION_ARGS) +{ + int32 typmod = PG_GETARG_INT32(0); + + PG_RETURN_CSTRING(anychar_typmodout(typmod)); +} + /***************************************************************************** * Exported functions diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index 824ef4a1efe..8c4cbef66cb 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.139 2006/12/23 00:43:11 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.140 2006/12/30 21:21:54 tgl Exp $ * * NOTES * Eventually, the index information should go through here, too. @@ -2016,6 +2016,60 @@ getTypeBinaryOutputInfo(Oid type, Oid *typSend, bool *typIsVarlena) ReleaseSysCache(typeTuple); } +/* + * get_typmodin + * + * Given the type OID, return the type's typmodin procedure, if any. + */ +Oid +get_typmodin(Oid typid) +{ + HeapTuple tp; + + tp = SearchSysCache(TYPEOID, + ObjectIdGetDatum(typid), + 0, 0, 0); + if (HeapTupleIsValid(tp)) + { + Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp); + Oid result; + + result = typtup->typmodin; + ReleaseSysCache(tp); + return result; + } + else + return InvalidOid; +} + +#ifdef NOT_USED +/* + * get_typmodout + * + * Given the type OID, return the type's typmodout procedure, if any. + */ +Oid +get_typmodout(Oid typid) +{ + HeapTuple tp; + + tp = SearchSysCache(TYPEOID, + ObjectIdGetDatum(typid), + 0, 0, 0); + if (HeapTupleIsValid(tp)) + { + Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp); + Oid result; + + result = typtup->typmodout; + ReleaseSysCache(tp); + return result; + } + else + return InvalidOid; +} +#endif /* NOT_USED */ + /* ---------- STATISTICS CACHE ---------- */ diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 6c6ff1fb5a3..377c6d87c3a 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -10,7 +10,7 @@ * Written by Peter Eisentraut <peter_e@gmx.net>. * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.363 2006/12/23 00:52:40 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.364 2006/12/30 21:21:54 tgl Exp $ * *-------------------------------------------------------------------- */ @@ -45,6 +45,7 @@ #include "parser/gramparse.h" #include "parser/parse_expr.h" #include "parser/parse_relation.h" +#include "parser/parse_type.h" #include "parser/scansup.h" #include "pgstat.h" #include "postmaster/autovacuum.h" @@ -4523,14 +4524,17 @@ flatten_set_variable_args(const char *name, List *args) * to interval and back to normalize the value and account * for any typmod. */ + int32 typmod; Datum interval; char *intervalout; + typmod = typenameTypeMod(NULL, arg->typename, INTERVALOID); + interval = DirectFunctionCall3(interval_in, CStringGetDatum(val), ObjectIdGetDatum(InvalidOid), - Int32GetDatum(arg->typename->typmod)); + Int32GetDatum(typmod)); intervalout = DatumGetCString(DirectFunctionCall1(interval_out, |