mirror of
https://github.com/postgres/postgres.git
synced 2025-10-22 14:32:25 +03:00
Remove collation information from TypeName, where it does not belong.
The initial collations patch treated a COLLATE spec as part of a TypeName, following what can only be described as brain fade on the part of the SQL committee. It's a lot more reasonable to treat COLLATE as a syntactically separate object, so that it can be added in only the productions where it actually belongs, rather than needing to reject it in a boatload of places where it doesn't belong (something the original patch mostly failed to do). In addition this change lets us meet the spec's requirement to allow COLLATE anywhere in the clauses of a ColumnDef, and it avoids unfriendly behavior for constructs such as "foo::type COLLATE collation". To do this, pull collation information out of TypeName and put it in ColumnDef instead, thus reverting most of the collation-related changes in parse_type.c's API. I made one additional structural change, which was to use a ColumnDef as an intermediate node in AT_AlterColumnType AlterTableCmd nodes. This provides enough room to get rid of the "transform" wart in AlterTableCmd too, since the ColumnDef can carry the USING expression easily enough. Also fix some other minor bugs that have crept in in the same areas, like failure to copy recently-added fields of ColumnDef in copyfuncs.c. While at it, document the formerly secret ability to specify a collation in ALTER TABLE ALTER COLUMN TYPE, ALTER TYPE ADD ATTRIBUTE, and ALTER TYPE ALTER ATTRIBUTE TYPE; and correct some misstatements about what the default collation selection will be when COLLATE is omitted. BTW, the three-parameter form of format_type() should go away too, since it just contributes to the confusion in this area; but I'll do that in a separate patch.
This commit is contained in:
@@ -132,6 +132,9 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args,
|
||||
static List *mergeTableFuncParameters(List *func_args, List *columns);
|
||||
static TypeName *TableFuncTypeName(List *columns);
|
||||
static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_t yyscanner);
|
||||
static void SplitColQualList(List *qualList,
|
||||
List **constraintList, CollateClause **collClause,
|
||||
core_yyscan_t yyscanner);
|
||||
|
||||
%}
|
||||
|
||||
@@ -221,7 +224,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
|
||||
%type <node> alter_column_default opclass_item opclass_drop alter_using
|
||||
%type <ival> add_drop opt_asc_desc opt_nulls_order
|
||||
|
||||
%type <node> alter_table_cmd alter_type_cmd
|
||||
%type <node> alter_table_cmd alter_type_cmd opt_collate_clause
|
||||
%type <list> alter_table_cmds alter_type_cmds
|
||||
|
||||
%type <dbehavior> opt_drop_behavior
|
||||
@@ -400,8 +403,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
|
||||
%type <list> copy_generic_opt_list copy_generic_opt_arg_list
|
||||
%type <list> copy_options
|
||||
|
||||
%type <typnam> Typename SimpleTypename SimpleTypenameWithoutCollation
|
||||
ConstTypename
|
||||
%type <typnam> Typename SimpleTypename ConstTypename
|
||||
GenericType Numeric opt_float
|
||||
Character ConstCharacter
|
||||
CharacterWithLength CharacterWithoutLength
|
||||
@@ -619,6 +621,7 @@ static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_
|
||||
%left '^'
|
||||
/* Unary Operators */
|
||||
%left AT ZONE /* sets precedence for AT TIME ZONE */
|
||||
%left COLLATE
|
||||
%right UMINUS
|
||||
%left '[' ']'
|
||||
%left '(' ')'
|
||||
@@ -1746,13 +1749,17 @@ alter_table_cmd:
|
||||
* ALTER TABLE <name> ALTER [COLUMN] <colname> [SET DATA] TYPE <typename>
|
||||
* [ USING <expression> ]
|
||||
*/
|
||||
| ALTER opt_column ColId opt_set_data TYPE_P Typename alter_using
|
||||
| ALTER opt_column ColId opt_set_data TYPE_P Typename opt_collate_clause alter_using
|
||||
{
|
||||
AlterTableCmd *n = makeNode(AlterTableCmd);
|
||||
ColumnDef *def = makeNode(ColumnDef);
|
||||
n->subtype = AT_AlterColumnType;
|
||||
n->name = $3;
|
||||
n->def = (Node *) $6;
|
||||
n->transform = $7;
|
||||
n->def = (Node *) def;
|
||||
/* We only use these three fields of the ColumnDef node */
|
||||
def->typeName = $6;
|
||||
def->collClause = (CollateClause *) $7;
|
||||
def->raw_default = $8;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
/* ALTER TABLE <name> ADD CONSTRAINT ... */
|
||||
@@ -1981,6 +1988,19 @@ opt_drop_behavior:
|
||||
| /* EMPTY */ { $$ = DROP_RESTRICT; /* default */ }
|
||||
;
|
||||
|
||||
opt_collate_clause:
|
||||
COLLATE any_name
|
||||
{
|
||||
CollateClause *n = makeNode(CollateClause);
|
||||
n->arg = NULL;
|
||||
n->collnames = $2;
|
||||
n->collOid = InvalidOid;
|
||||
n->location = @1;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
| /* EMPTY */ { $$ = NULL; }
|
||||
;
|
||||
|
||||
alter_using:
|
||||
USING a_expr { $$ = $2; }
|
||||
| /* EMPTY */ { $$ = NULL; }
|
||||
@@ -2077,13 +2097,18 @@ alter_type_cmd:
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
/* ALTER TYPE <name> ALTER ATTRIBUTE <attname> [SET DATA] TYPE <typename> [RESTRICT|CASCADE] */
|
||||
| ALTER ATTRIBUTE ColId opt_set_data TYPE_P Typename opt_drop_behavior
|
||||
| ALTER ATTRIBUTE ColId opt_set_data TYPE_P Typename opt_collate_clause opt_drop_behavior
|
||||
{
|
||||
AlterTableCmd *n = makeNode(AlterTableCmd);
|
||||
ColumnDef *def = makeNode(ColumnDef);
|
||||
n->subtype = AT_AlterColumnType;
|
||||
n->name = $3;
|
||||
n->def = (Node *) $6;
|
||||
n->behavior = $7;
|
||||
n->def = (Node *) def;
|
||||
n->behavior = $8;
|
||||
/* We only use these three fields of the ColumnDef node */
|
||||
def->typeName = $6;
|
||||
def->collClause = (CollateClause *) $7;
|
||||
def->raw_default = NULL;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
;
|
||||
@@ -2454,8 +2479,16 @@ columnDef: ColId Typename ColQualList
|
||||
ColumnDef *n = makeNode(ColumnDef);
|
||||
n->colname = $1;
|
||||
n->typeName = $2;
|
||||
n->constraints = $3;
|
||||
n->inhcount = 0;
|
||||
n->is_local = true;
|
||||
n->is_not_null = false;
|
||||
n->is_from_type = false;
|
||||
n->storage = 0;
|
||||
n->raw_default = NULL;
|
||||
n->cooked_default = NULL;
|
||||
n->collOid = InvalidOid;
|
||||
SplitColQualList($3, &n->constraints, &n->collClause,
|
||||
yyscanner);
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
;
|
||||
@@ -2464,8 +2497,17 @@ columnOptions: ColId WITH OPTIONS ColQualList
|
||||
{
|
||||
ColumnDef *n = makeNode(ColumnDef);
|
||||
n->colname = $1;
|
||||
n->constraints = $4;
|
||||
n->typeName = NULL;
|
||||
n->inhcount = 0;
|
||||
n->is_local = true;
|
||||
n->is_not_null = false;
|
||||
n->is_from_type = false;
|
||||
n->storage = 0;
|
||||
n->raw_default = NULL;
|
||||
n->cooked_default = NULL;
|
||||
n->collOid = InvalidOid;
|
||||
SplitColQualList($4, &n->constraints, &n->collClause,
|
||||
yyscanner);
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
;
|
||||
@@ -2486,6 +2528,20 @@ ColConstraint:
|
||||
}
|
||||
| ColConstraintElem { $$ = $1; }
|
||||
| ConstraintAttr { $$ = $1; }
|
||||
| COLLATE any_name
|
||||
{
|
||||
/*
|
||||
* Note: the CollateClause is momentarily included in
|
||||
* the list built by ColQualList, but we split it out
|
||||
* again in SplitColQualList.
|
||||
*/
|
||||
CollateClause *n = makeNode(CollateClause);
|
||||
n->arg = NULL;
|
||||
n->collnames = $2;
|
||||
n->collOid = InvalidOid;
|
||||
n->location = @1;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
;
|
||||
|
||||
/* DEFAULT NULL is already the default for Postgres.
|
||||
@@ -2973,8 +3029,12 @@ CreateAsElement:
|
||||
n->inhcount = 0;
|
||||
n->is_local = true;
|
||||
n->is_not_null = false;
|
||||
n->is_from_type = false;
|
||||
n->storage = 0;
|
||||
n->raw_default = NULL;
|
||||
n->cooked_default = NULL;
|
||||
n->collClause = NULL;
|
||||
n->collOid = InvalidOid;
|
||||
n->constraints = NIL;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
@@ -6577,7 +6637,7 @@ opt_column: COLUMN { $$ = COLUMN; }
|
||||
| /*EMPTY*/ { $$ = 0; }
|
||||
;
|
||||
|
||||
opt_set_data: SET DATA_P { $$ = 1; }
|
||||
opt_set_data: SET DATA_P { $$ = 1; }
|
||||
| /*EMPTY*/ { $$ = 0; }
|
||||
;
|
||||
|
||||
@@ -7443,7 +7503,8 @@ CreateDomainStmt:
|
||||
CreateDomainStmt *n = makeNode(CreateDomainStmt);
|
||||
n->domainname = $3;
|
||||
n->typeName = $5;
|
||||
n->constraints = $6;
|
||||
SplitColQualList($6, &n->constraints, &n->collClause,
|
||||
yyscanner);
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
;
|
||||
@@ -9084,13 +9145,21 @@ TableFuncElementList:
|
||||
}
|
||||
;
|
||||
|
||||
TableFuncElement: ColId Typename
|
||||
TableFuncElement: ColId Typename opt_collate_clause
|
||||
{
|
||||
ColumnDef *n = makeNode(ColumnDef);
|
||||
n->colname = $1;
|
||||
n->typeName = $2;
|
||||
n->constraints = NIL;
|
||||
n->inhcount = 0;
|
||||
n->is_local = true;
|
||||
n->is_not_null = false;
|
||||
n->is_from_type = false;
|
||||
n->storage = 0;
|
||||
n->raw_default = NULL;
|
||||
n->cooked_default = NULL;
|
||||
n->collClause = (CollateClause *) $3;
|
||||
n->collOid = InvalidOid;
|
||||
n->constraints = NIL;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
;
|
||||
@@ -9151,13 +9220,6 @@ opt_array_bounds:
|
||||
;
|
||||
|
||||
SimpleTypename:
|
||||
SimpleTypenameWithoutCollation opt_collate
|
||||
{
|
||||
$$ = $1;
|
||||
$$->collnames = $2;
|
||||
}
|
||||
|
||||
SimpleTypenameWithoutCollation:
|
||||
GenericType { $$ = $1; }
|
||||
| Numeric { $$ = $1; }
|
||||
| Bit { $$ = $1; }
|
||||
@@ -9625,6 +9687,14 @@ interval_second:
|
||||
a_expr: c_expr { $$ = $1; }
|
||||
| a_expr TYPECAST Typename
|
||||
{ $$ = makeTypeCast($1, $3, @2); }
|
||||
| a_expr COLLATE any_name
|
||||
{
|
||||
CollateClause *n = makeNode(CollateClause);
|
||||
n->arg = (Expr *) $1;
|
||||
n->collnames = $3;
|
||||
n->location = @2;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
| a_expr AT TIME ZONE a_expr
|
||||
{
|
||||
FuncCall *n = makeNode(FuncCall);
|
||||
@@ -10193,14 +10263,6 @@ c_expr: columnref { $$ = $1; }
|
||||
r->location = @1;
|
||||
$$ = (Node *)r;
|
||||
}
|
||||
| c_expr COLLATE any_name
|
||||
{
|
||||
CollateClause *n = makeNode(CollateClause);
|
||||
n->arg = (Expr *) $1;
|
||||
n->collnames = $3;
|
||||
n->location = @2;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
;
|
||||
|
||||
/*
|
||||
@@ -12678,15 +12740,6 @@ makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args,
|
||||
return (Node *) x;
|
||||
}
|
||||
|
||||
/* parser_init()
|
||||
* Initialize to parse one query string
|
||||
*/
|
||||
void
|
||||
parser_init(base_yy_extra_type *yyext)
|
||||
{
|
||||
yyext->parsetree = NIL; /* in case grammar forgets to set it */
|
||||
}
|
||||
|
||||
/*
|
||||
* Merge the input and output parameters of a table function.
|
||||
*/
|
||||
@@ -12774,6 +12827,57 @@ makeRangeVarFromAnyName(List *names, int position, core_yyscan_t yyscanner)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Separate Constraint nodes from COLLATE clauses in a ColQualList */
|
||||
static void
|
||||
SplitColQualList(List *qualList,
|
||||
List **constraintList, CollateClause **collClause,
|
||||
core_yyscan_t yyscanner)
|
||||
{
|
||||
ListCell *cell;
|
||||
ListCell *prev;
|
||||
ListCell *next;
|
||||
|
||||
*collClause = NULL;
|
||||
prev = NULL;
|
||||
for (cell = list_head(qualList); cell; cell = next)
|
||||
{
|
||||
Node *n = (Node *) lfirst(cell);
|
||||
|
||||
next = lnext(cell);
|
||||
if (IsA(n, Constraint))
|
||||
{
|
||||
/* keep it in list */
|
||||
prev = cell;
|
||||
continue;
|
||||
}
|
||||
if (IsA(n, CollateClause))
|
||||
{
|
||||
CollateClause *c = (CollateClause *) n;
|
||||
|
||||
if (*collClause)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("multiple COLLATE clauses not allowed"),
|
||||
parser_errposition(c->location)));
|
||||
*collClause = c;
|
||||
}
|
||||
else
|
||||
elog(ERROR, "unexpected node type %d", (int) n->type);
|
||||
/* remove non-Constraint nodes from qualList */
|
||||
qualList = list_delete_cell(qualList, cell, prev);
|
||||
}
|
||||
*constraintList = qualList;
|
||||
}
|
||||
|
||||
/* parser_init()
|
||||
* Initialize to parse one query string
|
||||
*/
|
||||
void
|
||||
parser_init(base_yy_extra_type *yyext)
|
||||
{
|
||||
yyext->parsetree = NIL; /* in case grammar forgets to set it */
|
||||
}
|
||||
|
||||
/*
|
||||
* Must undefine this stuff before including scan.c, since it has different
|
||||
* definitions for these macros.
|
||||
|
@@ -147,12 +147,6 @@ transformExpr(ParseState *pstate, Node *expr)
|
||||
{
|
||||
TypeCast *tc = (TypeCast *) expr;
|
||||
|
||||
if (tc->typeName->collnames)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("COLLATE clause not allowed in cast target"),
|
||||
parser_errposition(pstate, tc->typeName->location)));
|
||||
|
||||
/*
|
||||
* If the subject of the typecast is an ARRAY[] construct and
|
||||
* the target type is an array type, we invoke
|
||||
@@ -2116,13 +2110,16 @@ transformCollateClause(ParseState *pstate, CollateClause *c)
|
||||
newc->arg = (Expr *) transformExpr(pstate, (Node *) c->arg);
|
||||
|
||||
argtype = exprType((Node *) newc->arg);
|
||||
/* The unknown type is not collatable, but coerce_type() takes
|
||||
* care of it separately, so we'll let it go here. */
|
||||
/*
|
||||
* The unknown type is not collatable, but coerce_type() takes
|
||||
* care of it separately, so we'll let it go here.
|
||||
*/
|
||||
if (!type_is_collatable(argtype) && argtype != UNKNOWNOID)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
errmsg("collations are not supported by type %s",
|
||||
format_type_be(argtype))));
|
||||
format_type_be(argtype)),
|
||||
parser_errposition(pstate, c->location)));
|
||||
|
||||
newc->collOid = LookupCollation(pstate, c->collnames, c->location);
|
||||
newc->collnames = c->collnames;
|
||||
|
@@ -1313,7 +1313,7 @@ FuncNameAsType(List *funcname)
|
||||
Oid result;
|
||||
Type typtup;
|
||||
|
||||
typtup = LookupTypeName(NULL, makeTypeNameFromNameList(funcname), NULL, NULL);
|
||||
typtup = LookupTypeName(NULL, makeTypeNameFromNameList(funcname), NULL);
|
||||
if (typtup == NULL)
|
||||
return InvalidOid;
|
||||
|
||||
@@ -1500,7 +1500,7 @@ LookupTypeNameOid(const TypeName *typename)
|
||||
Oid result;
|
||||
Type typtup;
|
||||
|
||||
typtup = LookupTypeName(NULL, typename, NULL, NULL);
|
||||
typtup = LookupTypeName(NULL, typename, NULL);
|
||||
if (typtup == NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
|
@@ -1169,7 +1169,8 @@ addRangeTableEntryForFunction(ParseState *pstate,
|
||||
errmsg("column \"%s\" cannot be declared SETOF",
|
||||
attrname),
|
||||
parser_errposition(pstate, n->typeName->location)));
|
||||
typenameTypeIdModColl(pstate, n->typeName, &attrtype, &attrtypmod, &attrcollation);
|
||||
typenameTypeIdAndMod(pstate, n->typeName, &attrtype, &attrtypmod);
|
||||
attrcollation = GetColumnDefCollation(pstate, n, attrtype);
|
||||
eref->colnames = lappend(eref->colnames, makeString(attrname));
|
||||
rte->funccoltypes = lappend_oid(rte->funccoltypes, attrtype);
|
||||
rte->funccoltypmods = lappend_int(rte->funccoltypmods, attrtypmod);
|
||||
|
@@ -29,8 +29,6 @@
|
||||
|
||||
static int32 typenameTypeMod(ParseState *pstate, const TypeName *typeName,
|
||||
Type typ);
|
||||
static Oid typenameCollation(ParseState *pstate, const TypeName *typeName,
|
||||
Type typ);
|
||||
|
||||
|
||||
/*
|
||||
@@ -38,8 +36,7 @@ static Oid typenameCollation(ParseState *pstate, const TypeName *typeName,
|
||||
* Given a TypeName object, lookup the pg_type syscache entry of the type.
|
||||
* Returns NULL if no such type can be found. If the type is found,
|
||||
* the typmod value represented in the TypeName struct is computed and
|
||||
* stored into *typmod_p, and the collation is looked up and stored into
|
||||
* *colloid_p.
|
||||
* stored into *typmod_p.
|
||||
*
|
||||
* NB: on success, the caller must ReleaseSysCache the type tuple when done
|
||||
* with it.
|
||||
@@ -54,18 +51,15 @@ static Oid typenameCollation(ParseState *pstate, const TypeName *typeName,
|
||||
* found but is a shell, and there is typmod decoration, an error will be
|
||||
* thrown --- this is intentional.
|
||||
*
|
||||
* colloid_p can also be null.
|
||||
*
|
||||
* pstate is only used for error location info, and may be NULL.
|
||||
*/
|
||||
Type
|
||||
LookupTypeName(ParseState *pstate, const TypeName *typeName,
|
||||
int32 *typmod_p, Oid *collid_p)
|
||||
int32 *typmod_p)
|
||||
{
|
||||
Oid typoid;
|
||||
HeapTuple tup;
|
||||
int32 typmod;
|
||||
Oid collid;
|
||||
|
||||
if (typeName->names == NIL)
|
||||
{
|
||||
@@ -180,28 +174,22 @@ LookupTypeName(ParseState *pstate, const TypeName *typeName,
|
||||
if (typmod_p)
|
||||
*typmod_p = typmod;
|
||||
|
||||
collid = typenameCollation(pstate, typeName, (Type) tup);
|
||||
|
||||
if (collid_p)
|
||||
*collid_p = collid;
|
||||
|
||||
return (Type) tup;
|
||||
}
|
||||
|
||||
/*
|
||||
* typenameType - given a TypeName, return a Type structure, typmod, and
|
||||
* collation
|
||||
* typenameType - given a TypeName, return a Type structure and typmod
|
||||
*
|
||||
* This is equivalent to LookupTypeName, except that this will report
|
||||
* a suitable error message if the type cannot be found or is not defined.
|
||||
* Callers of this can therefore assume the result is a fully valid type.
|
||||
*/
|
||||
Type
|
||||
typenameType(ParseState *pstate, const TypeName *typeName, int32 *typmod_p, Oid *collid_p)
|
||||
typenameType(ParseState *pstate, const TypeName *typeName, int32 *typmod_p)
|
||||
{
|
||||
Type tup;
|
||||
|
||||
tup = LookupTypeName(pstate, typeName, typmod_p, collid_p);
|
||||
tup = LookupTypeName(pstate, typeName, typmod_p);
|
||||
if (tup == NULL)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
||||
@@ -229,7 +217,7 @@ typenameTypeId(ParseState *pstate, const TypeName *typeName)
|
||||
Oid typoid;
|
||||
Type tup;
|
||||
|
||||
tup = typenameType(pstate, typeName, NULL, NULL);
|
||||
tup = typenameType(pstate, typeName, NULL);
|
||||
typoid = HeapTupleGetOid(tup);
|
||||
ReleaseSysCache(tup);
|
||||
|
||||
@@ -248,25 +236,7 @@ typenameTypeIdAndMod(ParseState *pstate, const TypeName *typeName,
|
||||
{
|
||||
Type tup;
|
||||
|
||||
tup = typenameType(pstate, typeName, typmod_p, NULL);
|
||||
*typeid_p = HeapTupleGetOid(tup);
|
||||
ReleaseSysCache(tup);
|
||||
}
|
||||
|
||||
/*
|
||||
* typenameTypeIdModColl - given a TypeName, return the type's OID,
|
||||
* typmod, and collation
|
||||
*
|
||||
* This is equivalent to typenameType, but we only hand back the type OID,
|
||||
* typmod, and collation, not the syscache entry.
|
||||
*/
|
||||
void
|
||||
typenameTypeIdModColl(ParseState *pstate, const TypeName *typeName,
|
||||
Oid *typeid_p, int32 *typmod_p, Oid *collid_p)
|
||||
{
|
||||
Type tup;
|
||||
|
||||
tup = typenameType(pstate, typeName, typmod_p, collid_p);
|
||||
tup = typenameType(pstate, typeName, typmod_p);
|
||||
*typeid_p = HeapTupleGetOid(tup);
|
||||
ReleaseSysCache(tup);
|
||||
}
|
||||
@@ -380,62 +350,6 @@ typenameTypeMod(ParseState *pstate, const TypeName *typeName, Type typ)
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* typenameCollation - given a TypeName, return the collation OID
|
||||
*
|
||||
* This will throw an error if the TypeName includes a collation but
|
||||
* the data type does not support collations.
|
||||
*
|
||||
* The actual type OID represented by the TypeName must already have been
|
||||
* looked up, and is passed as "typ".
|
||||
*
|
||||
* pstate is only used for error location info, and may be NULL.
|
||||
*/
|
||||
static Oid
|
||||
typenameCollation(ParseState *pstate, const TypeName *typeName, Type typ)
|
||||
{
|
||||
Oid typcollation = ((Form_pg_type) GETSTRUCT(typ))->typcollation;
|
||||
|
||||
/* return prespecified collation OID if no collation name specified */
|
||||
if (typeName->collnames == NIL)
|
||||
{
|
||||
if (typeName->collOid == InvalidOid)
|
||||
return typcollation;
|
||||
else
|
||||
return typeName->collOid;
|
||||
}
|
||||
|
||||
if (!OidIsValid(typcollation))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_SYNTAX_ERROR),
|
||||
errmsg("collations are not supported by type %s",
|
||||
format_type_be(HeapTupleGetOid(typ))),
|
||||
parser_errposition(pstate, typeName->location)));
|
||||
|
||||
return LookupCollation(pstate, typeName->collnames, typeName->location);
|
||||
}
|
||||
|
||||
/*
|
||||
* LookupCollation
|
||||
*
|
||||
* Look up collation by name, return OID, with support for error
|
||||
* location.
|
||||
*/
|
||||
Oid
|
||||
LookupCollation(ParseState *pstate, List *collnames, int location)
|
||||
{
|
||||
Oid colloid;
|
||||
ParseCallbackState pcbstate;
|
||||
|
||||
setup_parser_errposition_callback(&pcbstate, pstate, location);
|
||||
|
||||
colloid = get_collation_oid(collnames, false);
|
||||
|
||||
cancel_parser_errposition_callback(&pcbstate);
|
||||
|
||||
return colloid;
|
||||
}
|
||||
|
||||
/*
|
||||
* appendTypeNameToBuffer
|
||||
* Append a string representing the name of a TypeName to a StringInfo.
|
||||
@@ -516,6 +430,72 @@ TypeNameListToString(List *typenames)
|
||||
return string.data;
|
||||
}
|
||||
|
||||
/*
|
||||
* LookupCollation
|
||||
*
|
||||
* Look up collation by name, return OID, with support for error location.
|
||||
*/
|
||||
Oid
|
||||
LookupCollation(ParseState *pstate, List *collnames, int location)
|
||||
{
|
||||
Oid colloid;
|
||||
ParseCallbackState pcbstate;
|
||||
|
||||
if (pstate)
|
||||
setup_parser_errposition_callback(&pcbstate, pstate, location);
|
||||
|
||||
colloid = get_collation_oid(collnames, false);
|
||||
|
||||
if (pstate)
|
||||
cancel_parser_errposition_callback(&pcbstate);
|
||||
|
||||
return colloid;
|
||||
}
|
||||
|
||||
/*
|
||||
* GetColumnDefCollation
|
||||
*
|
||||
* Get the collation to be used for a column being defined, given the
|
||||
* ColumnDef node and the previously-determined column type OID.
|
||||
*
|
||||
* pstate is only used for error location purposes, and can be NULL.
|
||||
*/
|
||||
Oid
|
||||
GetColumnDefCollation(ParseState *pstate, ColumnDef *coldef, Oid typeOid)
|
||||
{
|
||||
Oid result;
|
||||
Oid typcollation = get_typcollation(typeOid);
|
||||
int location = -1;
|
||||
|
||||
if (coldef->collClause)
|
||||
{
|
||||
/* We have a raw COLLATE clause, so look up the collation */
|
||||
location = coldef->collClause->location;
|
||||
result = LookupCollation(pstate, coldef->collClause->collnames,
|
||||
location);
|
||||
}
|
||||
else if (OidIsValid(coldef->collOid))
|
||||
{
|
||||
/* Precooked collation spec, use that */
|
||||
result = coldef->collOid;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Use the type's default collation if any */
|
||||
result = typcollation;
|
||||
}
|
||||
|
||||
/* Complain if COLLATE is applied to an uncollatable type */
|
||||
if (OidIsValid(result) && !OidIsValid(typcollation))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
errmsg("collations are not supported by type %s",
|
||||
format_type_be(typeOid)),
|
||||
parser_errposition(pstate, location)));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* return a Type structure, given a type id */
|
||||
/* NB: caller must ReleaseSysCache the type tuple when done with it */
|
||||
Type
|
||||
|
@@ -627,13 +627,16 @@ transformInhRelation(CreateStmtContext *cxt, InhRelation *inhRelation)
|
||||
def = makeNode(ColumnDef);
|
||||
def->colname = pstrdup(attributeName);
|
||||
def->typeName = makeTypeNameFromOid(attribute->atttypid,
|
||||
attribute->atttypmod,
|
||||
attribute->attcollation);
|
||||
attribute->atttypmod);
|
||||
def->inhcount = 0;
|
||||
def->is_local = true;
|
||||
def->is_not_null = attribute->attnotnull;
|
||||
def->is_from_type = false;
|
||||
def->storage = 0;
|
||||
def->raw_default = NULL;
|
||||
def->cooked_default = NULL;
|
||||
def->collClause = NULL;
|
||||
def->collOid = attribute->attcollation;
|
||||
def->constraints = NIL;
|
||||
|
||||
/*
|
||||
@@ -822,7 +825,7 @@ transformOfType(CreateStmtContext *cxt, TypeName *ofTypename)
|
||||
|
||||
AssertArg(ofTypename);
|
||||
|
||||
tuple = typenameType(NULL, ofTypename, NULL, NULL);
|
||||
tuple = typenameType(NULL, ofTypename, NULL);
|
||||
typ = (Form_pg_type) GETSTRUCT(tuple);
|
||||
ofTypeId = HeapTupleGetOid(tuple);
|
||||
ofTypename->typeOid = ofTypeId; /* cached for later */
|
||||
@@ -837,16 +840,24 @@ transformOfType(CreateStmtContext *cxt, TypeName *ofTypename)
|
||||
for (i = 0; i < tupdesc->natts; i++)
|
||||
{
|
||||
Form_pg_attribute attr = tupdesc->attrs[i];
|
||||
ColumnDef *n = makeNode(ColumnDef);
|
||||
ColumnDef *n;
|
||||
|
||||
if (attr->attisdropped)
|
||||
continue;
|
||||
|
||||
n = makeNode(ColumnDef);
|
||||
n->colname = pstrdup(NameStr(attr->attname));
|
||||
n->typeName = makeTypeNameFromOid(attr->atttypid, attr->atttypmod, attr->attcollation);
|
||||
n->constraints = NULL;
|
||||
n->typeName = makeTypeNameFromOid(attr->atttypid, attr->atttypmod);
|
||||
n->inhcount = 0;
|
||||
n->is_local = true;
|
||||
n->is_not_null = false;
|
||||
n->is_from_type = true;
|
||||
n->storage = 0;
|
||||
n->raw_default = NULL;
|
||||
n->cooked_default = NULL;
|
||||
n->collClause = NULL;
|
||||
n->collOid = attr->attcollation;
|
||||
n->constraints = NIL;
|
||||
cxt->columns = lappend(cxt->columns, n);
|
||||
}
|
||||
DecrTupleDescRefCount(tupdesc);
|
||||
@@ -2445,9 +2456,28 @@ static void
|
||||
transformColumnType(CreateStmtContext *cxt, ColumnDef *column)
|
||||
{
|
||||
/*
|
||||
* All we really need to do here is verify that the type is valid.
|
||||
* All we really need to do here is verify that the type is valid,
|
||||
* including any collation spec that might be present.
|
||||
*/
|
||||
Type ctype = typenameType(cxt->pstate, column->typeName, NULL, NULL);
|
||||
Type ctype = typenameType(cxt->pstate, column->typeName, NULL);
|
||||
|
||||
if (column->collClause)
|
||||
{
|
||||
Form_pg_type typtup = (Form_pg_type) GETSTRUCT(ctype);
|
||||
Oid collOid;
|
||||
|
||||
collOid = LookupCollation(cxt->pstate,
|
||||
column->collClause->collnames,
|
||||
column->collClause->location);
|
||||
/* Complain if COLLATE is applied to an uncollatable type */
|
||||
if (!OidIsValid(typtup->typcollation))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
errmsg("collations are not supported by type %s",
|
||||
format_type_be(HeapTupleGetOid(ctype))),
|
||||
parser_errposition(cxt->pstate,
|
||||
column->collClause->location)));
|
||||
}
|
||||
|
||||
ReleaseSysCache(ctype);
|
||||
}
|
||||
|
Reference in New Issue
Block a user