Per-column collation, proof of concept
Here is a proof of concept for per-column collation support.
Here is how it works: When creating a table, an optional COLLATE clause
can specify a collation name, which is stored (by OID) in pg_attribute.
This becomes part of the type information and is propagated through the
expression parse analysis, like typmod. When an operator or function
call is parsed (transformed), the collations of the arguments are
unified, using some rules (like type analysis, but different in detail).
The collations of the function/operator arguments come either from Var
nodes which in turn got them from pg_attribute, or from other
function and operator calls, or you can override them with explicit
COLLATE clauses (not yet implemented, but will work a bit like
RelabelType). At the end, each function or operator call gets one
collation to use.
The function call itself can then look up the collation using the
fcinfo->flinfo->fn_expr field. (Works for operator calls, but doesn't
work for sort operations, needs more thought.)
A collation is in this implementation defined as an lc_collate string
and an lc_ctype string. The implementation of functions interested in
that information, such as comparison operators, or upper and lower
functions, will take the collation OID that is passed in, look up the
locale string, and use the xlocale.h interface (newlocale(),
strcoll_l()) to compute the result.
(Note that the xlocale stuff is only 10 or so lines in this patch. It
should be feasible to allow other appropriate locale libraries to be
used.)
Loose ends:
- Support function calls (currently only operator calls) (easy)
- Implementation of sort clauses
- Indexing support/integration
- Domain support (should be straightforward)
- Make all expression node types deal with collation information
appropriately
- Explicit COLLATE clause on expressions
- Caching and not leaking memory of locale lookups
- I have typcollatable to mark which types can accept collation
information, but perhaps there should also be proicareaboutcollation
to skip collation resolution when none of the functions in the
expression tree care.
You can start by reading the collate.sql regression test file to see
what it can do. Btw., regression tests only work with "make check
MULTIBYTE=UTF8". And it (probably) only works with glibc for now.
Attachments:
collate.patchtext/x-patch; charset=UTF-8; name=collate.patchDownload
diff --git a/contrib/btree_gist/btree_utils_var.c b/contrib/btree_gist/btree_utils_var.c
index a703289..2d8801d 100644
--- a/contrib/btree_gist/btree_utils_var.c
+++ b/contrib/btree_gist/btree_utils_var.c
@@ -156,7 +156,7 @@ gbt_bytea_pf_match(const bytea *pf, const bytea *query, const gbtree_vinfo *tinf
if (tinfo->eml > 1)
{
- out = (varstr_cmp(q, nlen, n, nlen) == 0);
+ out = (varstr_cmp(q, nlen, n, nlen, InvalidOid) == 0);
}
else
{
diff --git a/contrib/citext/citext.c b/contrib/citext/citext.c
index 006d0e4..bf7c978 100644
--- a/contrib/citext/citext.c
+++ b/contrib/citext/citext.c
@@ -18,7 +18,7 @@ PG_MODULE_MAGIC;
* ====================
*/
-static int32 citextcmp(text *left, text *right);
+static int32 citextcmp(text *left, text *right, Oid collid);
extern Datum citext_cmp(PG_FUNCTION_ARGS);
extern Datum citext_hash(PG_FUNCTION_ARGS);
extern Datum citext_eq(PG_FUNCTION_ARGS);
@@ -42,7 +42,7 @@ extern Datum citext_larger(PG_FUNCTION_ARGS);
* Returns int32 negative, zero, or positive.
*/
static int32
-citextcmp(text *left, text *right)
+citextcmp(text *left, text *right, Oid collid)
{
char *lcstr,
*rcstr;
@@ -52,7 +52,8 @@ citextcmp(text *left, text *right)
rcstr = str_tolower(VARDATA_ANY(right), VARSIZE_ANY_EXHDR(right));
result = varstr_cmp(lcstr, strlen(lcstr),
- rcstr, strlen(rcstr));
+ rcstr, strlen(rcstr),
+ collid);
pfree(lcstr);
pfree(rcstr);
@@ -75,7 +76,7 @@ citext_cmp(PG_FUNCTION_ARGS)
text *right = PG_GETARG_TEXT_PP(1);
int32 result;
- result = citextcmp(left, right);
+ result = citextcmp(left, right, PG_GET_COLLATION());
PG_FREE_IF_COPY(left, 0);
PG_FREE_IF_COPY(right, 1);
@@ -177,7 +178,7 @@ citext_lt(PG_FUNCTION_ARGS)
text *right = PG_GETARG_TEXT_PP(1);
bool result;
- result = citextcmp(left, right) < 0;
+ result = citextcmp(left, right, PG_GET_COLLATION()) < 0;
PG_FREE_IF_COPY(left, 0);
PG_FREE_IF_COPY(right, 1);
@@ -194,7 +195,7 @@ citext_le(PG_FUNCTION_ARGS)
text *right = PG_GETARG_TEXT_PP(1);
bool result;
- result = citextcmp(left, right) <= 0;
+ result = citextcmp(left, right, PG_GET_COLLATION()) <= 0;
PG_FREE_IF_COPY(left, 0);
PG_FREE_IF_COPY(right, 1);
@@ -211,7 +212,7 @@ citext_gt(PG_FUNCTION_ARGS)
text *right = PG_GETARG_TEXT_PP(1);
bool result;
- result = citextcmp(left, right) > 0;
+ result = citextcmp(left, right, PG_GET_COLLATION()) > 0;
PG_FREE_IF_COPY(left, 0);
PG_FREE_IF_COPY(right, 1);
@@ -228,7 +229,7 @@ citext_ge(PG_FUNCTION_ARGS)
text *right = PG_GETARG_TEXT_PP(1);
bool result;
- result = citextcmp(left, right) >= 0;
+ result = citextcmp(left, right, PG_GET_COLLATION()) >= 0;
PG_FREE_IF_COPY(left, 0);
PG_FREE_IF_COPY(right, 1);
@@ -251,7 +252,7 @@ citext_smaller(PG_FUNCTION_ARGS)
text *right = PG_GETARG_TEXT_PP(1);
text *result;
- result = citextcmp(left, right) < 0 ? left : right;
+ result = citextcmp(left, right, PG_GET_COLLATION()) < 0 ? left : right;
PG_RETURN_TEXT_P(result);
}
@@ -264,6 +265,6 @@ citext_larger(PG_FUNCTION_ARGS)
text *right = PG_GETARG_TEXT_PP(1);
text *result;
- result = citextcmp(left, right) > 0 ? left : right;
+ result = citextcmp(left, right, PG_GET_COLLATION()) > 0 ? left : right;
PG_RETURN_TEXT_P(result);
}
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index 9a8611f..2b290f8 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -476,6 +476,7 @@ TupleDescInitEntry(TupleDesc desc,
att->attisdropped = false;
att->attislocal = true;
att->attinhcount = 0;
+ att->attcollation = InvalidOid;
/* attacl and attoptions are not present in tupledescs */
tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(oidtypeid));
@@ -514,6 +515,7 @@ BuildDescForRelation(List *schema)
Oid atttypid;
int32 atttypmod;
int attdim;
+ Oid colloid;
/*
* allocate a new tuple descriptor
@@ -536,7 +538,7 @@ BuildDescForRelation(List *schema)
attnum++;
attname = entry->colname;
- atttypid = typenameTypeId(NULL, entry->typeName, &atttypmod);
+ atttypid = typenameTypeId(NULL, entry->typeName, &atttypmod, &colloid);
attdim = list_length(entry->typeName->arrayBounds);
if (entry->typeName->setof)
@@ -557,6 +559,7 @@ BuildDescForRelation(List *schema)
has_not_null |= entry->is_not_null;
desc->attrs[attnum - 1]->attislocal = entry->is_local;
desc->attrs[attnum - 1]->attinhcount = entry->inhcount;
+ desc->attrs[attnum - 1]->attcollation = colloid;
}
if (has_not_null)
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 82f761d..32ed66d 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -15,7 +15,7 @@ OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \
pg_inherits.o pg_largeobject.o pg_namespace.o pg_operator.o pg_proc.o \
pg_db_role_setting.o pg_shdepend.o pg_type.o storage.o toasting.o
-BKIFILES = postgres.bki postgres.description postgres.shdescription
+BKIFILES = postgres.bki postgres.description postgres.shdescription locales.txt
include $(top_srcdir)/src/backend/common.mk
@@ -37,7 +37,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
pg_ts_config.h pg_ts_config_map.h pg_ts_dict.h \
pg_ts_parser.h pg_ts_template.h \
pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
- pg_default_acl.h \
+ pg_default_acl.h pg_collation.h \
toasting.h indexing.h \
)
@@ -57,11 +57,15 @@ schemapg.h: postgres.bki ;
postgres.bki: genbki.pl Catalog.pm $(POSTGRES_BKI_SRCS)
$(PERL) -I $(catalogdir) $< $(pg_includes) --set-version=$(MAJORVERSION) $(POSTGRES_BKI_SRCS)
+locales.txt:
+ for loc in $$(locale -a); do cm=$$(LC_CTYPE="$$loc" locale charmap); echo "$$loc $$cm"; done >$@
+
.PHONY: install-data
install-data: $(BKIFILES) installdirs
$(INSTALL_DATA) $(call vpathsearch,postgres.bki) '$(DESTDIR)$(datadir)/postgres.bki'
$(INSTALL_DATA) $(call vpathsearch,postgres.description) '$(DESTDIR)$(datadir)/postgres.description'
$(INSTALL_DATA) $(call vpathsearch,postgres.shdescription) '$(DESTDIR)$(datadir)/postgres.shdescription'
+ $(INSTALL_DATA) $(call vpathsearch,locales.txt) '$(DESTDIR)$(datadir)/locales.txt'
$(INSTALL_DATA) $(srcdir)/system_views.sql '$(DESTDIR)$(datadir)/system_views.sql'
$(INSTALL_DATA) $(srcdir)/information_schema.sql '$(DESTDIR)$(datadir)/information_schema.sql'
$(INSTALL_DATA) $(srcdir)/sql_features.txt '$(DESTDIR)$(datadir)/sql_features.txt'
@@ -76,6 +80,7 @@ uninstall-data:
# postgres.bki, postgres.description, postgres.shdescription, and schemapg.h
# are in the distribution tarball, so they are not cleaned here.
clean:
+ rm -f locales.txt
maintainer-clean: clean
rm -f $(BKIFILES)
diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl
index 31aabda..5e57946 100644
--- a/src/backend/catalog/genbki.pl
+++ b/src/backend/catalog/genbki.pl
@@ -367,6 +367,7 @@ sub emit_pgattr_row
attisdropped => 'f',
attislocal => 't',
attinhcount => '0',
+ attcollation => '0',
attacl => '_null_',
attoptions => '_null_'
);
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index d848ef0..b0350ed 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -536,6 +536,7 @@ InsertPgAttributeTuple(Relation pg_attribute_rel,
values[Anum_pg_attribute_attisdropped - 1] = BoolGetDatum(new_attribute->attisdropped);
values[Anum_pg_attribute_attislocal - 1] = BoolGetDatum(new_attribute->attislocal);
values[Anum_pg_attribute_attinhcount - 1] = Int32GetDatum(new_attribute->attinhcount);
+ values[Anum_pg_attribute_attcollation - 1] = ObjectIdGetDatum(new_attribute->attcollation);
/* start out with empty permissions and empty options */
nulls[Anum_pg_attribute_attacl - 1] = true;
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 5581346..ba1a720 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -2765,6 +2765,53 @@ PopOverrideSearchPath(void)
/*
+ * FindCollationByName - find a collation by possibly qualified name
+ */
+Oid
+FindCollationByName(List *name)
+{
+ char *schemaname;
+ char *collation_name;
+ Oid namespaceId;
+ Oid colloid;
+ ListCell *l;
+
+ /* deconstruct the name list */
+ DeconstructQualifiedName(name, &schemaname, &collation_name);
+
+ if (schemaname)
+ {
+ /* use exact schema given */
+ namespaceId = LookupExplicitNamespace(schemaname);
+ return GetSysCacheOid2(COLLNAMENSP,
+ PointerGetDatum(collation_name),
+ ObjectIdGetDatum(namespaceId));
+ }
+ else
+ {
+ /* search for it in search path */
+ recomputeNamespacePath();
+
+ foreach(l, activeSearchPath)
+ {
+ namespaceId = lfirst_oid(l);
+
+ if (namespaceId == myTempNamespace)
+ continue; /* do not look in temp namespace */
+
+ colloid = GetSysCacheOid2(COLLNAMENSP,
+ PointerGetDatum(collation_name),
+ ObjectIdGetDatum(namespaceId));
+ if (OidIsValid(colloid))
+ return colloid;
+ }
+ }
+
+ /* Not found in path */
+ return InvalidOid;
+}
+
+/*
* FindConversionByName - find a conversion by possibly qualified name
*/
Oid
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 76f9e06..5532b3c 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -109,6 +109,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
values[i++] = CharGetDatum('i'); /* typalign */
values[i++] = CharGetDatum('p'); /* typstorage */
values[i++] = BoolGetDatum(false); /* typnotnull */
+ values[i++] = BoolGetDatum(false); /* typcollatable */
values[i++] = ObjectIdGetDatum(InvalidOid); /* typbasetype */
values[i++] = Int32GetDatum(-1); /* typtypmod */
values[i++] = Int32GetDatum(0); /* typndims */
@@ -339,6 +340,7 @@ TypeCreate(Oid newTypeOid,
values[i++] = CharGetDatum(alignment); /* typalign */
values[i++] = CharGetDatum(storage); /* typstorage */
values[i++] = BoolGetDatum(typeNotNull); /* typnotnull */
+ values[i++] = BoolGetDatum(false); /* typcollatable */ // TODO: add to CREATE TYPE
values[i++] = ObjectIdGetDatum(baseType); /* typbasetype */
values[i++] = Int32GetDatum(typeMod); /* typtypmod */
values[i++] = Int32GetDatum(typNDims); /* typndims */
diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c
index 4964fb3..d7ba02e 100644
--- a/src/backend/commands/aggregatecmds.c
+++ b/src/backend/commands/aggregatecmds.c
@@ -142,7 +142,7 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
{
numArgs = 1;
aggArgTypes = (Oid *) palloc(sizeof(Oid));
- aggArgTypes[0] = typenameTypeId(NULL, baseType, NULL);
+ aggArgTypes[0] = typenameTypeId(NULL, baseType, NULL, NULL);
}
}
else
@@ -164,7 +164,7 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
{
TypeName *curTypeName = (TypeName *) lfirst(lc);
- aggArgTypes[i++] = typenameTypeId(NULL, curTypeName, NULL);
+ aggArgTypes[i++] = typenameTypeId(NULL, curTypeName, NULL, NULL);
}
}
@@ -179,7 +179,7 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
* worse) by connecting up incompatible internal-using functions in an
* aggregate.
*/
- transTypeId = typenameTypeId(NULL, transType, NULL);
+ transTypeId = typenameTypeId(NULL, transType, NULL, NULL);
if (get_typtype(transTypeId) == TYPTYPE_PSEUDO &&
!IsPolymorphicType(transTypeId))
{
diff --git a/src/backend/commands/comment.c b/src/backend/commands/comment.c
index b613cbf..e0e2c91 100644
--- a/src/backend/commands/comment.c
+++ b/src/backend/commands/comment.c
@@ -945,7 +945,7 @@ CommentType(List *typename, char *comment)
/* Find the type's oid */
- oid = typenameTypeId(NULL, tname, NULL);
+ oid = typenameTypeId(NULL, tname, NULL, NULL);
/* Check object security */
@@ -1457,8 +1457,8 @@ CommentCast(List *qualname, List *arguments, char *comment)
targettype = (TypeName *) linitial(arguments);
Assert(IsA(targettype, TypeName));
- sourcetypeid = typenameTypeId(NULL, sourcetype, NULL);
- targettypeid = typenameTypeId(NULL, targettype, NULL);
+ sourcetypeid = typenameTypeId(NULL, sourcetype, NULL, NULL);
+ targettypeid = typenameTypeId(NULL, targettype, NULL, NULL);
tuple = SearchSysCache2(CASTSOURCETARGET,
ObjectIdGetDatum(sourcetypeid),
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 9a584ed..8de06ee 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -86,7 +86,7 @@ compute_return_type(TypeName *returnType, Oid languageOid,
Oid rettype;
Type typtup;
- typtup = LookupTypeName(NULL, returnType, NULL);
+ typtup = LookupTypeName(NULL, returnType, NULL, NULL);
if (typtup)
{
@@ -206,7 +206,7 @@ examine_parameter_list(List *parameters, Oid languageOid,
Oid toid;
Type typtup;
- typtup = LookupTypeName(NULL, t, NULL);
+ typtup = LookupTypeName(NULL, t, NULL, NULL);
if (typtup)
{
if (!((Form_pg_type) GETSTRUCT(typtup))->typisdefined)
@@ -1496,8 +1496,8 @@ CreateCast(CreateCastStmt *stmt)
ObjectAddress myself,
referenced;
- sourcetypeid = typenameTypeId(NULL, stmt->sourcetype, NULL);
- targettypeid = typenameTypeId(NULL, stmt->targettype, NULL);
+ sourcetypeid = typenameTypeId(NULL, stmt->sourcetype, NULL, NULL);
+ targettypeid = typenameTypeId(NULL, stmt->targettype, NULL, NULL);
sourcetyptype = get_typtype(sourcetypeid);
targettyptype = get_typtype(targettypeid);
@@ -1763,8 +1763,8 @@ DropCast(DropCastStmt *stmt)
ObjectAddress object;
/* when dropping a cast, the types must exist even if you use IF EXISTS */
- sourcetypeid = typenameTypeId(NULL, stmt->sourcetype, NULL);
- targettypeid = typenameTypeId(NULL, stmt->targettype, NULL);
+ sourcetypeid = typenameTypeId(NULL, stmt->sourcetype, NULL, NULL);
+ targettypeid = typenameTypeId(NULL, stmt->targettype, NULL, NULL);
tuple = SearchSysCache2(CASTSOURCETARGET,
ObjectIdGetDatum(sourcetypeid),
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 55d447e..47bf116 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -320,7 +320,7 @@ DefineOpClass(CreateOpClassStmt *stmt)
errmsg("must be superuser to create an operator class")));
/* Look up the datatype */
- typeoid = typenameTypeId(NULL, stmt->datatype, NULL);
+ typeoid = typenameTypeId(NULL, stmt->datatype, NULL, NULL);
#ifdef NOT_USED
/* XXX this is unnecessary given the superuser check above */
@@ -474,7 +474,7 @@ DefineOpClass(CreateOpClassStmt *stmt)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("storage type specified more than once")));
- storageoid = typenameTypeId(NULL, item->storedtype, NULL);
+ storageoid = typenameTypeId(NULL, item->storedtype, NULL, NULL);
#ifdef NOT_USED
/* XXX this is unnecessary given the superuser check above */
@@ -1021,12 +1021,12 @@ processTypesSpec(List *args, Oid *lefttype, Oid *righttype)
Assert(args != NIL);
typeName = (TypeName *) linitial(args);
- *lefttype = typenameTypeId(NULL, typeName, NULL);
+ *lefttype = typenameTypeId(NULL, typeName, NULL, NULL);
if (list_length(args) > 1)
{
typeName = (TypeName *) lsecond(args);
- *righttype = typenameTypeId(NULL, typeName, NULL);
+ *righttype = typenameTypeId(NULL, typeName, NULL, NULL);
}
else
*righttype = *lefttype;
diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c
index 5db1d0d..a0e0360 100644
--- a/src/backend/commands/operatorcmds.c
+++ b/src/backend/commands/operatorcmds.c
@@ -167,9 +167,9 @@ DefineOperator(List *names, List *parameters)
/* Transform type names to type OIDs */
if (typeName1)
- typeId1 = typenameTypeId(NULL, typeName1, NULL);
+ typeId1 = typenameTypeId(NULL, typeName1, NULL, NULL);
if (typeName2)
- typeId2 = typenameTypeId(NULL, typeName2, NULL);
+ typeId2 = typenameTypeId(NULL, typeName2, NULL, NULL);
if (!OidIsValid(typeId1) && !OidIsValid(typeId2))
ereport(ERROR,
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index e765382..33ec289 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -90,7 +90,7 @@ PrepareQuery(PrepareStmt *stmt, const char *queryString)
foreach(l, stmt->argtypes)
{
TypeName *tn = lfirst(l);
- Oid toid = typenameTypeId(pstate, tn, NULL);
+ Oid toid = typenameTypeId(pstate, tn, NULL, NULL);
argtypes[i++] = toid;
}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 5f6fe41..0cbb92f 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -451,7 +451,7 @@ DefineRelation(CreateStmt *stmt, char relkind)
(void) heap_reloptions(relkind, reloptions, true);
if (stmt->ofTypename)
- ofTypeId = typenameTypeId(NULL, stmt->ofTypename, NULL);
+ ofTypeId = typenameTypeId(NULL, stmt->ofTypename, NULL, NULL);
else
ofTypeId = InvalidOid;
@@ -1367,6 +1367,7 @@ MergeAttributes(List *schema, List *supers, bool istemp,
{
Oid defTypeId;
int32 deftypmod;
+ Oid defCollId;
/*
* Yes, try to merge the two column definitions. They must
@@ -1376,9 +1377,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);
- defTypeId = typenameTypeId(NULL, def->typeName, &deftypmod);
+ defTypeId = typenameTypeId(NULL, def->typeName, &deftypmod, &defCollId);
if (defTypeId != attribute->atttypid ||
- deftypmod != attribute->atttypmod)
+ deftypmod != attribute->atttypmod ||
+ defCollId != attribute->attcollation /*TODO: separate msg for collation*/)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("inherited column \"%s\" has a type conflict",
@@ -1539,6 +1541,8 @@ MergeAttributes(List *schema, List *supers, bool istemp,
newTypeId;
int32 deftypmod,
newtypmod;
+ Oid defcollid,
+ newcollid;
/*
* Yes, try to merge the two column definitions. They must
@@ -1548,9 +1552,9 @@ MergeAttributes(List *schema, List *supers, bool istemp,
(errmsg("merging column \"%s\" with inherited definition",
attributeName)));
def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1);
- defTypeId = typenameTypeId(NULL, def->typeName, &deftypmod);
- newTypeId = typenameTypeId(NULL, newdef->typeName, &newtypmod);
- if (defTypeId != newTypeId || deftypmod != newtypmod)
+ defTypeId = typenameTypeId(NULL, def->typeName, &deftypmod, &defcollid);
+ newTypeId = typenameTypeId(NULL, newdef->typeName, &newtypmod, &newcollid);
+ if (defTypeId != newTypeId || deftypmod != newtypmod || defcollid != newcollid /*TODO*/)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("column \"%s\" has a type conflict",
@@ -3613,6 +3617,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
HeapTuple typeTuple;
Oid typeOid;
int32 typmod;
+ Oid collOid;
Form_pg_type tform;
Expr *defval;
@@ -3638,11 +3643,13 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
Oid ctypeId;
int32 ctypmod;
+ Oid ccollid;
/* Child column must match by type */
- ctypeId = typenameTypeId(NULL, colDef->typeName, &ctypmod);
+ ctypeId = typenameTypeId(NULL, colDef->typeName, &ctypmod, &ccollid);
if (ctypeId != childatt->atttypid ||
- ctypmod != childatt->atttypmod)
+ ctypmod != childatt->atttypmod ||
+ ccollid != childatt->attcollation /*TODO*/)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("child table \"%s\" has different type for column \"%s\"",
@@ -3704,7 +3711,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
MaxHeapAttributeNumber)));
}
- typeTuple = typenameType(NULL, colDef->typeName, &typmod);
+ typeTuple = typenameType(NULL, colDef->typeName, &typmod, &collOid);
tform = (Form_pg_type) GETSTRUCT(typeTuple);
typeOid = HeapTupleGetOid(typeTuple);
@@ -3729,6 +3736,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
attribute.attisdropped = false;
attribute.attislocal = colDef->is_local;
attribute.attinhcount = colDef->inhcount;
+ attribute.attcollation = collOid;
/* attribute.attacl is handled by InsertPgAttributeTuple */
ReleaseSysCache(typeTuple);
@@ -5784,6 +5792,7 @@ ATPrepAlterColumnType(List **wqueue,
AttrNumber attnum;
Oid targettype;
int32 targettypmod;
+ Oid targetcollid;
Node *transform;
NewColumnValue *newval;
ParseState *pstate = make_parsestate(NULL);
@@ -5813,7 +5822,8 @@ ATPrepAlterColumnType(List **wqueue,
colName)));
/* Look up the target type */
- targettype = typenameTypeId(NULL, typeName, &targettypmod);
+ targettype = typenameTypeId(NULL, typeName, &targettypmod, &targetcollid);
+ // TODO: check that type accepts collations if targetcollid is set
/* make sure datatype is legal for a column */
CheckAttributeType(colName, targettype, false);
@@ -5917,6 +5927,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
Form_pg_type tform;
Oid targettype;
int32 targettypmod;
+ Oid targetcollid;
Node *defaultexpr;
Relation attrelation;
Relation depRel;
@@ -5945,7 +5956,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
colName)));
/* Look up the target type (should not fail, since prep found it) */
- typeTuple = typenameType(NULL, typeName, &targettypmod);
+ typeTuple = typenameType(NULL, typeName, &targettypmod, &targetcollid);
tform = (Form_pg_type) GETSTRUCT(typeTuple);
targettype = HeapTupleGetOid(typeTuple);
@@ -6202,6 +6213,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
*/
attTup->atttypid = targettype;
attTup->atttypmod = targettypmod;
+ attTup->attcollation = targetcollid;
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 1e14dca..a245985 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -285,7 +285,7 @@ DefineType(List *names, List *parameters)
Type likeType;
Form_pg_type likeForm;
- likeType = typenameType(NULL, defGetTypeName(likeTypeEl), NULL);
+ likeType = typenameType(NULL, defGetTypeName(likeTypeEl), NULL, NULL);
likeForm = (Form_pg_type) GETSTRUCT(likeType);
internalLength = likeForm->typlen;
byValue = likeForm->typbyval;
@@ -332,7 +332,7 @@ DefineType(List *names, List *parameters)
}
if (elemTypeEl)
{
- elemType = typenameTypeId(NULL, defGetTypeName(elemTypeEl), NULL);
+ elemType = typenameTypeId(NULL, defGetTypeName(elemTypeEl), NULL, NULL);
/* disallow arrays of pseudotypes */
if (get_typtype(elemType) == TYPTYPE_PSEUDO)
ereport(ERROR,
@@ -640,7 +640,7 @@ RemoveTypes(DropStmt *drop)
typename = makeTypeNameFromNameList(names);
/* Use LookupTypeName here so that shell types can be removed. */
- tup = LookupTypeName(NULL, typename, NULL);
+ tup = LookupTypeName(NULL, typename, NULL, NULL);
if (tup == NULL)
{
if (!drop->missing_ok)
@@ -798,7 +798,7 @@ DefineDomain(CreateDomainStmt *stmt)
/*
* Look up the base type.
*/
- typeTup = typenameType(NULL, stmt->typeName, &basetypeMod);
+ typeTup = typenameType(NULL, stmt->typeName, &basetypeMod, NULL); /* TODO: collation support for domains */
baseType = (Form_pg_type) GETSTRUCT(typeTup);
basetypeoid = HeapTupleGetOid(typeTup);
@@ -1572,7 +1572,7 @@ AlterDomainDefault(List *names, Node *defaultRaw)
/* Make a TypeName so we can use standard type lookup machinery */
typename = makeTypeNameFromNameList(names);
- domainoid = typenameTypeId(NULL, typename, NULL);
+ domainoid = typenameTypeId(NULL, typename, NULL, NULL);
/* Look up the domain in the type table */
rel = heap_open(TypeRelationId, RowExclusiveLock);
@@ -1698,7 +1698,7 @@ AlterDomainNotNull(List *names, bool notNull)
/* Make a TypeName so we can use standard type lookup machinery */
typename = makeTypeNameFromNameList(names);
- domainoid = typenameTypeId(NULL, typename, NULL);
+ domainoid = typenameTypeId(NULL, typename, NULL, NULL);
/* Look up the domain in the type table */
typrel = heap_open(TypeRelationId, RowExclusiveLock);
@@ -1798,7 +1798,7 @@ AlterDomainDropConstraint(List *names, const char *constrName,
/* Make a TypeName so we can use standard type lookup machinery */
typename = makeTypeNameFromNameList(names);
- domainoid = typenameTypeId(NULL, typename, NULL);
+ domainoid = typenameTypeId(NULL, typename, NULL, NULL);
/* Look up the domain in the type table */
rel = heap_open(TypeRelationId, RowExclusiveLock);
@@ -1871,7 +1871,7 @@ AlterDomainAddConstraint(List *names, Node *newConstraint)
/* Make a TypeName so we can use standard type lookup machinery */
typename = makeTypeNameFromNameList(names);
- domainoid = typenameTypeId(NULL, typename, NULL);
+ domainoid = typenameTypeId(NULL, typename, NULL, NULL);
/* Look up the domain in the type table */
typrel = heap_open(TypeRelationId, RowExclusiveLock);
@@ -2492,7 +2492,7 @@ RenameType(List *names, const char *newTypeName)
/* Make a TypeName so we can use standard type lookup machinery */
typename = makeTypeNameFromNameList(names);
- typeOid = typenameTypeId(NULL, typename, NULL);
+ typeOid = typenameTypeId(NULL, typename, NULL, NULL);
/* Look up the type in the type table */
rel = heap_open(TypeRelationId, RowExclusiveLock);
@@ -2565,7 +2565,7 @@ AlterTypeOwner(List *names, Oid newOwnerId)
typename = makeTypeNameFromNameList(names);
/* Use LookupTypeName here so that shell types can be processed */
- tup = LookupTypeName(NULL, typename, NULL);
+ tup = LookupTypeName(NULL, typename, NULL, NULL);
if (tup == NULL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -2721,7 +2721,7 @@ AlterTypeNamespace(List *names, const char *newschema)
/* Make a TypeName so we can use standard type lookup machinery */
typename = makeTypeNameFromNameList(names);
- typeOid = typenameTypeId(NULL, typename, NULL);
+ typeOid = typenameTypeId(NULL, typename, NULL, NULL);
/* check permissions on type */
if (!pg_type_ownercheck(typeOid, GetUserId()))
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 93dcef5..0213cdd 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -975,6 +975,7 @@ _copyVar(Var *from)
COPY_SCALAR_FIELD(varlevelsup);
COPY_SCALAR_FIELD(varnoold);
COPY_SCALAR_FIELD(varoattno);
+ COPY_SCALAR_FIELD(varcollid);
COPY_LOCATION_FIELD(location);
return newnode;
@@ -1105,6 +1106,7 @@ _copyFuncExpr(FuncExpr *from)
COPY_SCALAR_FIELD(funcretset);
COPY_SCALAR_FIELD(funcformat);
COPY_NODE_FIELD(args);
+ COPY_SCALAR_FIELD(collid);
COPY_LOCATION_FIELD(location);
return newnode;
@@ -1139,6 +1141,7 @@ _copyOpExpr(OpExpr *from)
COPY_SCALAR_FIELD(opresulttype);
COPY_SCALAR_FIELD(opretset);
COPY_NODE_FIELD(args);
+ COPY_SCALAR_FIELD(collid);
COPY_LOCATION_FIELD(location);
return newnode;
@@ -2070,6 +2073,8 @@ _copyTypeName(TypeName *from)
COPY_NODE_FIELD(typmods);
COPY_SCALAR_FIELD(typemod);
COPY_NODE_FIELD(arrayBounds);
+ COPY_NODE_FIELD(collnames);
+ COPY_SCALAR_FIELD(collOid);
COPY_LOCATION_FIELD(location);
return newnode;
@@ -2141,6 +2146,19 @@ _copyTypeCast(TypeCast *from)
return newnode;
}
+static CollateClause *
+_copyCollateClause(CollateClause *from)
+{
+ CollateClause *newnode = makeNode(CollateClause);
+
+ COPY_NODE_FIELD(arg);
+ COPY_NODE_FIELD(collnames);
+ COPY_SCALAR_FIELD(collOid);
+ COPY_LOCATION_FIELD(location);
+
+ return newnode;
+}
+
static IndexElem *
_copyIndexElem(IndexElem *from)
{
@@ -4204,6 +4222,9 @@ copyObject(void *from)
case T_TypeCast:
retval = _copyTypeCast(from);
break;
+ case T_CollateClause:
+ retval = _copyCollateClause(from);
+ break;
case T_SortBy:
retval = _copySortBy(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 5d83727..1b986fc 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -140,6 +140,7 @@ _equalVar(Var *a, Var *b)
COMPARE_SCALAR_FIELD(varlevelsup);
COMPARE_SCALAR_FIELD(varnoold);
COMPARE_SCALAR_FIELD(varoattno);
+ COMPARE_SCALAR_FIELD(varcollid);
COMPARE_LOCATION_FIELD(location);
return true;
@@ -237,6 +238,7 @@ _equalFuncExpr(FuncExpr *a, FuncExpr *b)
return false;
COMPARE_NODE_FIELD(args);
+ COMPARE_SCALAR_FIELD(collid);
COMPARE_LOCATION_FIELD(location);
return true;
@@ -272,6 +274,7 @@ _equalOpExpr(OpExpr *a, OpExpr *b)
COMPARE_SCALAR_FIELD(opresulttype);
COMPARE_SCALAR_FIELD(opretset);
COMPARE_NODE_FIELD(args);
+ COMPARE_SCALAR_FIELD(collid);
COMPARE_LOCATION_FIELD(location);
return true;
@@ -2023,6 +2026,8 @@ _equalTypeName(TypeName *a, TypeName *b)
COMPARE_NODE_FIELD(typmods);
COMPARE_SCALAR_FIELD(typemod);
COMPARE_NODE_FIELD(arrayBounds);
+ COMPARE_NODE_FIELD(collnames);
+ COMPARE_SCALAR_FIELD(collOid);
COMPARE_LOCATION_FIELD(location);
return true;
@@ -2039,6 +2044,17 @@ _equalTypeCast(TypeCast *a, TypeCast *b)
}
static bool
+_equalCollateClause(CollateClause *a, CollateClause *b)
+{
+ COMPARE_NODE_FIELD(arg);
+ COMPARE_NODE_FIELD(collnames);
+ COMPARE_SCALAR_FIELD(collOid);
+ COMPARE_LOCATION_FIELD(location);
+
+ return true;
+}
+
+static bool
_equalSortBy(SortBy *a, SortBy *b)
{
COMPARE_NODE_FIELD(node);
@@ -2871,6 +2887,9 @@ equal(void *a, void *b)
case T_TypeCast:
retval = _equalTypeCast(a, b);
break;
+ case T_CollateClause:
+ retval = _equalCollateClause(a, b);
+ break;
case T_SortBy:
retval = _equalSortBy(a, b);
break;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 9cf0cae..a41fc41 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -460,6 +460,39 @@ exprTypmod(Node *expr)
}
/*
+ * exprColl -
+ * returns the Oid of the collation of the expression's result.
+ */
+Oid
+exprColl(Node *expr)
+{
+ Oid coll;
+
+ if (!expr)
+ return InvalidOid;
+
+ switch (nodeTag(expr))
+ {
+ case T_Var:
+ coll = ((Var *) expr)->varcollid;
+ break;
+ case T_FuncExpr:
+ coll = ((FuncExpr *) expr)->collid;
+ break;
+ case T_OpExpr:
+ coll = ((OpExpr *) expr)->collid;
+ break;
+ case T_DistinctExpr:
+ coll = ((DistinctExpr *) expr)->collid;
+ break;
+ default:
+ coll = InvalidOid; // TODO
+ break;
+ }
+ return coll;
+}
+
+/*
* exprIsLengthCoercion
* Detect whether an expression tree is an application of a datatype's
* typmod-coercion function. Optionally extract the result's typmod.
@@ -908,6 +941,9 @@ exprLocation(Node *expr)
loc = leftmostLoc(loc, tc->location);
}
break;
+ case T_CollateClause:
+ loc = ((CollateClause *) expr)->location;
+ break;
case T_SortBy:
/* just use argument's location (ignore operator, if any) */
loc = exprLocation(((SortBy *) expr)->node);
@@ -2471,6 +2507,8 @@ bool
return true;
}
break;
+ case T_CollateClause:
+ return walker(((CollateClause *) node)->arg, context);
case T_SortBy:
return walker(((SortBy *) node)->node, context);
case T_WindowDef:
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 79baf4f..74ff920 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -841,6 +841,7 @@ _outVar(StringInfo str, Var *node)
WRITE_UINT_FIELD(varlevelsup);
WRITE_UINT_FIELD(varnoold);
WRITE_INT_FIELD(varoattno);
+ WRITE_OID_FIELD(varcollid);
WRITE_LOCATION_FIELD(location);
}
@@ -928,6 +929,7 @@ _outFuncExpr(StringInfo str, FuncExpr *node)
WRITE_BOOL_FIELD(funcretset);
WRITE_ENUM_FIELD(funcformat, CoercionForm);
WRITE_NODE_FIELD(args);
+ WRITE_OID_FIELD(collid);
WRITE_LOCATION_FIELD(location);
}
@@ -952,6 +954,7 @@ _outOpExpr(StringInfo str, OpExpr *node)
WRITE_OID_FIELD(opresulttype);
WRITE_BOOL_FIELD(opretset);
WRITE_NODE_FIELD(args);
+ WRITE_OID_FIELD(collid);
WRITE_LOCATION_FIELD(location);
}
@@ -1937,6 +1940,8 @@ _outTypeName(StringInfo str, TypeName *node)
WRITE_NODE_FIELD(typmods);
WRITE_INT_FIELD(typemod);
WRITE_NODE_FIELD(arrayBounds);
+ WRITE_NODE_FIELD(collnames);
+ WRITE_OID_FIELD(collOid);
WRITE_LOCATION_FIELD(location);
}
@@ -1951,6 +1956,17 @@ _outTypeCast(StringInfo str, TypeCast *node)
}
static void
+_outCollateClause(StringInfo str, CollateClause *node)
+{
+ WRITE_NODE_TYPE("COLLATE");
+
+ WRITE_NODE_FIELD(arg);
+ WRITE_NODE_FIELD(collnames);
+ WRITE_OID_FIELD(collOid);
+ WRITE_LOCATION_FIELD(location);
+}
+
+static void
_outIndexElem(StringInfo str, IndexElem *node)
{
WRITE_NODE_TYPE("INDEXELEM");
@@ -2823,6 +2839,9 @@ _outNode(StringInfo str, void *obj)
case T_TypeCast:
_outTypeCast(str, obj);
break;
+ case T_CollateClause:
+ _outCollateClause(str, obj);
+ break;
case T_IndexElem:
_outIndexElem(str, obj);
break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index bc6e2a6..d520392 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -407,6 +407,7 @@ _readVar(void)
READ_UINT_FIELD(varlevelsup);
READ_UINT_FIELD(varnoold);
READ_INT_FIELD(varoattno);
+ READ_OID_FIELD(varcollid);
READ_LOCATION_FIELD(location);
READ_DONE();
@@ -524,6 +525,7 @@ _readFuncExpr(void)
READ_BOOL_FIELD(funcretset);
READ_ENUM_FIELD(funcformat, CoercionForm);
READ_NODE_FIELD(args);
+ READ_OID_FIELD(collid);
READ_LOCATION_FIELD(location);
READ_DONE();
@@ -569,6 +571,7 @@ _readOpExpr(void)
READ_OID_FIELD(opresulttype);
READ_BOOL_FIELD(opretset);
READ_NODE_FIELD(args);
+ READ_OID_FIELD(collid);
READ_LOCATION_FIELD(location);
READ_DONE();
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index e525ba6..5e3d50d 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -2173,6 +2173,7 @@ eval_const_expressions_mutator(Node *node,
newexpr->funcretset = expr->funcretset;
newexpr->funcformat = expr->funcformat;
newexpr->args = args;
+ newexpr->collid = expr->collid;
newexpr->location = expr->location;
return (Node *) newexpr;
}
@@ -2233,6 +2234,7 @@ eval_const_expressions_mutator(Node *node,
newexpr->opresulttype = expr->opresulttype;
newexpr->opretset = expr->opretset;
newexpr->args = args;
+ newexpr->collid = expr->collid;
newexpr->location = expr->location;
return (Node *) newexpr;
}
@@ -2325,6 +2327,7 @@ eval_const_expressions_mutator(Node *node,
newexpr->opresulttype = expr->opresulttype;
newexpr->opretset = expr->opretset;
newexpr->args = args;
+ newexpr->collid = expr->collid;
newexpr->location = expr->location;
return (Node *) newexpr;
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 3f6eeeb..ee1d786 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -388,7 +388,8 @@ static TypeName *TableFuncTypeName(List *columns);
%type <list> copy_generic_opt_list copy_generic_opt_arg_list
%type <list> copy_options
-%type <typnam> Typename SimpleTypename ConstTypename
+%type <typnam> Typename SimpleTypename SimpleTypenameWithoutCollation
+ ConstTypename
GenericType Numeric opt_float
Character ConstCharacter
CharacterWithLength CharacterWithoutLength
@@ -8258,6 +8259,14 @@ opt_array_bounds:
;
SimpleTypename:
+ SimpleTypenameWithoutCollation { $$ = $1; }
+ | SimpleTypenameWithoutCollation COLLATE any_name
+ {
+ $$ = $1;
+ $$->collnames = $3;
+ }
+
+SimpleTypenameWithoutCollation:
GenericType { $$ = $1; }
| Numeric { $$ = $1; }
| Bit { $$ = $1; }
@@ -9293,6 +9302,13 @@ c_expr: columnref { $$ = $1; }
r->location = @1;
$$ = (Node *)r;
}
+ | '(' a_expr ')' COLLATE any_name // TODO: this is not the right expression syntax
+ {
+ CollateClause *n = makeNode(CollateClause);
+ n->arg = $2;
+ n->collnames = $5;
+ $$ = (Node *)n;
+ }
;
/*
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 888b526..714c71f 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -69,6 +69,7 @@ static Node *transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte,
static Node *transformIndirection(ParseState *pstate, Node *basenode,
List *indirection);
static Node *transformTypeCast(ParseState *pstate, TypeCast *tc);
+static Node *transformCollateClause(ParseState *pstate, CollateClause *c);
static Node *make_row_comparison_op(ParseState *pstate, List *opname,
List *largs, List *rargs, int location);
static Node *make_row_distinct_op(ParseState *pstate, List *opname,
@@ -164,7 +165,8 @@ transformExpr(ParseState *pstate, Node *expr)
int32 targetTypmod;
targetType = typenameTypeId(pstate, tc->typeName,
- &targetTypmod);
+ &targetTypmod,
+ NULL); /* TODO: support or prohibit collations in type casts */
elementType = get_element_type(targetType);
if (OidIsValid(elementType))
{
@@ -191,6 +193,10 @@ transformExpr(ParseState *pstate, Node *expr)
break;
}
+ case T_CollateClause:
+ result = transformCollateClause(pstate, (CollateClause *) expr);
+ break;
+
case T_A_Expr:
{
A_Expr *a = (A_Expr *) expr;
@@ -1037,7 +1043,7 @@ transformAExprOf(ParseState *pstate, A_Expr *a)
ltype = exprType(lexpr);
foreach(telem, (List *) a->rexpr)
{
- rtype = typenameTypeId(pstate, lfirst(telem), NULL);
+ rtype = typenameTypeId(pstate, lfirst(telem), NULL, NULL);
matched = (rtype == ltype);
if (matched)
break;
@@ -1977,7 +1983,7 @@ transformXmlSerialize(ParseState *pstate, XmlSerialize *xs)
XMLOID,
"XMLSERIALIZE"));
- targetType = typenameTypeId(pstate, xs->typeName, &targetTypmod);
+ targetType = typenameTypeId(pstate, xs->typeName, &targetTypmod, NULL);
xexpr->xmloption = xs->xmloption;
xexpr->location = xs->location;
@@ -2212,7 +2218,7 @@ transformTypeCast(ParseState *pstate, TypeCast *tc)
int32 targetTypmod;
int location;
- targetType = typenameTypeId(pstate, tc->typeName, &targetTypmod);
+ targetType = typenameTypeId(pstate, tc->typeName, &targetTypmod, NULL); /* TODO: support or prohibit collations in type casts */
if (inputType == InvalidOid)
return expr; /* do nothing if NULL input */
@@ -2243,6 +2249,25 @@ transformTypeCast(ParseState *pstate, TypeCast *tc)
}
/*
+ * Handle an explicit COLLATE clause.
+ *
+ * Transform the argument, and look up the collation name.
+ */
+static Node *
+transformCollateClause(ParseState *pstate, CollateClause *c)
+{
+ CollateClause *newc;
+
+ newc = makeNode(CollateClause);
+ newc->arg = transformExpr(pstate, c->arg);
+ // TODO: check that input type accepts collations
+ newc->collOid = LookupCollation(pstate, c->collnames, c->location);
+ newc->collnames = c->collnames;
+
+ return (Node *) newc;
+}
+
+/*
* Transform a "row compare-op row" construct
*
* The inputs are lists of already-transformed expressions.
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 9580be9..25fdf95 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -1270,7 +1270,7 @@ FuncNameAsType(List *funcname)
Oid result;
Type typtup;
- typtup = LookupTypeName(NULL, makeTypeNameFromNameList(funcname), NULL);
+ typtup = LookupTypeName(NULL, makeTypeNameFromNameList(funcname), NULL, NULL);
if (typtup == NULL)
return InvalidOid;
@@ -1456,7 +1456,7 @@ LookupTypeNameOid(const TypeName *typename)
Oid result;
Type typtup;
- typtup = LookupTypeName(NULL, typename, NULL);
+ typtup = LookupTypeName(NULL, typename, NULL, NULL);
if (typtup == NULL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index a838b1e..cbc8f80 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -188,10 +188,12 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno, int location)
sublevels_up;
Oid vartypeid;
int32 type_mod;
+ Oid varcollid;
vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
- get_rte_attribute_type(rte, attrno, &vartypeid, &type_mod);
+ get_rte_attribute_type(rte, attrno, &vartypeid, &type_mod, &varcollid);
result = makeVar(vnum, attrno, vartypeid, type_mod, sublevels_up);
+ result->varcollid = varcollid;
result->location = location;
return result;
}
diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c
index 5798136..cb287cf 100644
--- a/src/backend/parser/parse_oper.c
+++ b/src/backend/parser/parse_oper.c
@@ -148,12 +148,12 @@ LookupOperNameTypeNames(ParseState *pstate, List *opername,
if (oprleft == NULL)
leftoid = InvalidOid;
else
- leftoid = typenameTypeId(pstate, oprleft, NULL);
+ leftoid = typenameTypeId(pstate, oprleft, NULL, NULL);
if (oprright == NULL)
rightoid = InvalidOid;
else
- rightoid = typenameTypeId(pstate, oprright, NULL);
+ rightoid = typenameTypeId(pstate, oprright, NULL, NULL);
return LookupOperName(pstate, opername, leftoid, rightoid,
noError, location);
@@ -779,6 +779,9 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
List *args;
Oid rettype;
OpExpr *result;
+ Oid lcollid,
+ rcollid;
+ Oid opcollid;
/* Select the operator */
if (rtree == NULL)
@@ -786,6 +789,8 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
/* right operator */
ltypeId = exprType(ltree);
rtypeId = InvalidOid;
+ lcollid = exprColl(ltree);
+ rcollid = InvalidOid;
tup = right_oper(pstate, opname, ltypeId, false, location);
}
else if (ltree == NULL)
@@ -793,6 +798,8 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
/* left operator */
rtypeId = exprType(rtree);
ltypeId = InvalidOid;
+ lcollid = InvalidOid;
+ rcollid = exprColl(rtree);
tup = left_oper(pstate, opname, rtypeId, false, location);
}
else
@@ -800,6 +807,8 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
/* otherwise, binary operator */
ltypeId = exprType(ltree);
rtypeId = exprType(rtree);
+ lcollid = exprColl(ltree);
+ rcollid = exprColl(rtree);
tup = oper(pstate, opname, ltypeId, rtypeId, false, location);
}
@@ -824,6 +833,7 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
actual_arg_types[0] = ltypeId;
declared_arg_types[0] = opform->oprleft;
nargs = 1;
+ opcollid = lcollid;
}
else if (ltree == NULL)
{
@@ -832,6 +842,7 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
actual_arg_types[0] = rtypeId;
declared_arg_types[0] = opform->oprright;
nargs = 1;
+ opcollid = rcollid;
}
else
{
@@ -842,6 +853,18 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
declared_arg_types[0] = opform->oprleft;
declared_arg_types[1] = opform->oprright;
nargs = 2;
+ // TODO: collation derivation logic per SQL standard goes somewhere here
+ if (lcollid == rcollid)
+ opcollid = lcollid;
+ else if (lcollid == InvalidOid)
+ opcollid = rcollid;
+ else if (rcollid == InvalidOid)
+ opcollid = lcollid;
+ else
+ {
+ elog(ERROR, "collation mismatch: left %u != right %u", lcollid, rcollid);
+ opcollid = InvalidOid;
+ }
}
/*
@@ -865,6 +888,7 @@ make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
result->opresulttype = rettype;
result->opretset = get_func_retset(opform->oprcode);
result->args = args;
+ result->collid = opcollid;
result->location = location;
ReleaseSysCache(tup);
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 38c7e91..a189b5b 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -1165,7 +1165,7 @@ addRangeTableEntryForFunction(ParseState *pstate,
errmsg("column \"%s\" cannot be declared SETOF",
attrname),
parser_errposition(pstate, n->typeName->location)));
- attrtype = typenameTypeId(pstate, n->typeName, &attrtypmod);
+ attrtype = typenameTypeId(pstate, n->typeName, &attrtypmod, NULL /*TODO*/);
eref->colnames = lappend(eref->colnames, makeString(attrname));
rte->funccoltypes = lappend_oid(rte->funccoltypes, attrtype);
rte->funccoltypmods = lappend_int(rte->funccoltypmods, attrtypmod);
@@ -1968,7 +1968,7 @@ get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum)
*/
void
get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
- Oid *vartype, int32 *vartypmod)
+ Oid *vartype, int32 *vartypmod, Oid *varcollid)
{
switch (rte->rtekind)
{
@@ -1998,6 +1998,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
get_rel_name(rte->relid))));
*vartype = att_tup->atttypid;
*vartypmod = att_tup->atttypmod;
+ *varcollid = att_tup->attcollation;
ReleaseSysCache(tp);
}
break;
@@ -2012,6 +2013,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
rte->eref->aliasname, attnum);
*vartype = exprType((Node *) te->expr);
*vartypmod = exprTypmod((Node *) te->expr);
+ *varcollid = exprColl((Node *) te->expr);
}
break;
case RTE_FUNCTION:
@@ -2053,17 +2055,20 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
rte->eref->aliasname)));
*vartype = att_tup->atttypid;
*vartypmod = att_tup->atttypmod;
+ *varcollid = att_tup->attcollation;
}
else if (functypclass == TYPEFUNC_SCALAR)
{
/* Base data type, i.e. scalar */
*vartype = funcrettype;
*vartypmod = -1;
+ *varcollid = InvalidOid; // XXX?
}
else if (functypclass == TYPEFUNC_RECORD)
{
*vartype = list_nth_oid(rte->funccoltypes, attnum - 1);
*vartypmod = list_nth_int(rte->funccoltypmods, attnum - 1);
+ *varcollid = InvalidOid; // TODO
}
else
{
@@ -2084,6 +2089,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
col = (Node *) list_nth(collist, attnum - 1);
*vartype = exprType(col);
*vartypmod = exprTypmod(col);
+ *varcollid = exprColl(col);
}
break;
case RTE_JOIN:
@@ -2097,6 +2103,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
aliasvar = (Node *) list_nth(rte->joinaliasvars, attnum - 1);
*vartype = exprType(aliasvar);
*vartypmod = exprTypmod(aliasvar);
+ *varcollid = exprColl(aliasvar);
}
break;
case RTE_CTE:
@@ -2105,6 +2112,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
Assert(attnum > 0 && attnum <= list_length(rte->ctecoltypes));
*vartype = list_nth_oid(rte->ctecoltypes, attnum - 1);
*vartypmod = list_nth_int(rte->ctecoltypmods, attnum - 1);
+ *varcollid = InvalidOid; // TODO
}
break;
default:
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index 8d0932b..7205738 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -15,8 +15,10 @@
#include "postgres.h"
#include "catalog/namespace.h"
+#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
#include "lib/stringinfo.h"
+#include "mb/pg_wchar.h"
#include "nodes/makefuncs.h"
#include "parser/parser.h"
#include "parser/parse_type.h"
@@ -29,6 +31,8 @@
static int32 typenameTypeMod(ParseState *pstate, const TypeName *typeName,
Type typ);
+static Oid typenameCollation(ParseState *pstate, const TypeName *typeName,
+ Type typ);
/*
@@ -36,7 +40,8 @@ static int32 typenameTypeMod(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.
+ * stored into *typmod_p, and the collation is looked up and stored into
+ * *colloid_p.
*
* NB: on success, the caller must ReleaseSysCache the type tuple when done
* with it.
@@ -51,15 +56,18 @@ static int32 typenameTypeMod(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)
+ int32 *typmod_p, Oid *colloid_p)
{
Oid typoid;
HeapTuple tup;
int32 typmod;
+ Oid colloid;
if (typeName->names == NIL)
{
@@ -174,6 +182,11 @@ LookupTypeName(ParseState *pstate, const TypeName *typeName,
if (typmod_p)
*typmod_p = typmod;
+ colloid = typenameCollation(pstate, typeName, (Type) tup);
+
+ if (colloid_p)
+ *colloid_p = colloid;
+
return (Type) tup;
}
@@ -185,11 +198,11 @@ LookupTypeName(ParseState *pstate, const TypeName *typeName,
* Callers of this can therefore assume the result is a fully valid type.
*/
Type
-typenameType(ParseState *pstate, const TypeName *typeName, int32 *typmod_p)
+typenameType(ParseState *pstate, const TypeName *typeName, int32 *typmod_p, Oid *colloid_p)
{
Type tup;
- tup = LookupTypeName(pstate, typeName, typmod_p);
+ tup = LookupTypeName(pstate, typeName, typmod_p, colloid_p);
if (tup == NULL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -212,12 +225,12 @@ typenameType(ParseState *pstate, const TypeName *typeName, int32 *typmod_p)
* not the syscache entry.
*/
Oid
-typenameTypeId(ParseState *pstate, const TypeName *typeName, int32 *typmod_p)
+typenameTypeId(ParseState *pstate, const TypeName *typeName, int32 *typmod_p, Oid *colloid_p)
{
Oid typoid;
Type tup;
- tup = typenameType(pstate, typeName, typmod_p);
+ tup = typenameType(pstate, typeName, typmod_p, colloid_p);
typoid = HeapTupleGetOid(tup);
ReleaseSysCache(tup);
@@ -334,6 +347,63 @@ typenameTypeMod(ParseState *pstate, const TypeName *typeName, Type typ)
}
/*
+ * 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)
+{
+ /* return prespecified collation OID if no collation name specified */
+ if (typeName->collnames == NIL)
+ return typeName->collOid;
+
+ if (!((Form_pg_type) GETSTRUCT(typ))->typcollatable)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("collations are not supported by type \"%s\"",
+ TypeNameToString(typeName)),
+ parser_errposition(pstate, typeName->location)));
+
+ return LookupCollation(pstate, typeName->collnames, typeName->location);
+}
+
+Oid
+LookupCollation(ParseState *pstate, List *collnames, int location)
+{
+ Oid colloid;
+ HeapTuple tup;
+
+ colloid = FindCollationByName(collnames);
+ if (!OidIsValid(colloid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("collation \"%s\" does not exist",
+ NameListToString(collnames))));
+
+ tup = SearchSysCache1(COLLOID, ObjectIdGetDatum(colloid));
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for collation %u", colloid);
+
+ if (((Form_pg_collation) GETSTRUCT(tup))->collencoding != GetDatabaseEncoding())
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("collation \"%s\" is not applicable to current database encoding \"%s\"",
+ NameListToString(collnames), GetDatabaseEncodingName()),
+ parser_errposition(pstate, location)));
+
+ ReleaseSysCache(tup);
+
+ return colloid;
+}
+
+/*
* appendTypeNameToBuffer
* Append a string representing the name of a TypeName to a StringInfo.
* This is the shared guts of TypeNameToString and TypeNameListToString.
@@ -635,7 +705,7 @@ parseTypeString(const char *str, Oid *type_id, int32 *typmod_p)
if (typeName->setof)
goto fail;
- *type_id = typenameTypeId(NULL, typeName, typmod_p);
+ *type_id = typenameTypeId(NULL, typeName, typmod_p, NULL);
pfree(buf.data);
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 90d5c76..7a1d616 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -809,7 +809,7 @@ transformOfType(ParseState *pstate, CreateStmtContext *cxt, TypeName *ofTypename
AssertArg(ofTypename);
- tuple = typenameType(NULL, ofTypename, NULL);
+ tuple = typenameType(NULL, ofTypename, NULL, NULL);
typ = (Form_pg_type) GETSTRUCT(tuple);
ofTypeId = HeapTupleGetOid(tuple);
ofTypename->typeOid = ofTypeId; /* cached for later */
@@ -2216,7 +2216,7 @@ transformColumnType(ParseState *pstate, ColumnDef *column)
/*
* All we really need to do here is verify that the type is valid.
*/
- Type ctype = typenameType(pstate, column->typeName, NULL);
+ Type ctype = typenameType(pstate, column->typeName, NULL, NULL);
ReleaseSysCache(ctype);
}
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index fc3c5b0..bfb062e 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -5480,11 +5480,11 @@ make_greater_string(const Const *str_const, FmgrInfo *ltproc)
char *best;
best = "Z";
- if (varstr_cmp(best, 1, "z", 1) < 0)
+ if (varstr_cmp(best, 1, "z", 1, InvalidOid) < 0)
best = "z";
- if (varstr_cmp(best, 1, "y", 1) < 0)
+ if (varstr_cmp(best, 1, "y", 1, InvalidOid) < 0)
best = "y";
- if (varstr_cmp(best, 1, "9", 1) < 0)
+ if (varstr_cmp(best, 1, "9", 1, InvalidOid) < 0)
best = "9";
suffixchar = *best;
}
diff --git a/src/backend/utils/adt/varchar.c b/src/backend/utils/adt/varchar.c
index cbc08f7..4bec7af 100644
--- a/src/backend/utils/adt/varchar.c
+++ b/src/backend/utils/adt/varchar.c
@@ -737,7 +737,8 @@ bpcharlt(PG_FUNCTION_ARGS)
len1 = bcTruelen(arg1);
len2 = bcTruelen(arg2);
- cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2);
+ cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2,
+ PG_GET_COLLATION());
PG_FREE_IF_COPY(arg1, 0);
PG_FREE_IF_COPY(arg2, 1);
@@ -757,7 +758,8 @@ bpcharle(PG_FUNCTION_ARGS)
len1 = bcTruelen(arg1);
len2 = bcTruelen(arg2);
- cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2);
+ cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2,
+ PG_GET_COLLATION());
PG_FREE_IF_COPY(arg1, 0);
PG_FREE_IF_COPY(arg2, 1);
@@ -777,7 +779,8 @@ bpchargt(PG_FUNCTION_ARGS)
len1 = bcTruelen(arg1);
len2 = bcTruelen(arg2);
- cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2);
+ cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2,
+ PG_GET_COLLATION());
PG_FREE_IF_COPY(arg1, 0);
PG_FREE_IF_COPY(arg2, 1);
@@ -797,7 +800,8 @@ bpcharge(PG_FUNCTION_ARGS)
len1 = bcTruelen(arg1);
len2 = bcTruelen(arg2);
- cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2);
+ cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2,
+ PG_GET_COLLATION());
PG_FREE_IF_COPY(arg1, 0);
PG_FREE_IF_COPY(arg2, 1);
@@ -817,7 +821,8 @@ bpcharcmp(PG_FUNCTION_ARGS)
len1 = bcTruelen(arg1);
len2 = bcTruelen(arg2);
- cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2);
+ cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2,
+ PG_GET_COLLATION());
PG_FREE_IF_COPY(arg1, 0);
PG_FREE_IF_COPY(arg2, 1);
@@ -837,7 +842,8 @@ bpchar_larger(PG_FUNCTION_ARGS)
len1 = bcTruelen(arg1);
len2 = bcTruelen(arg2);
- cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2);
+ cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2,
+ PG_GET_COLLATION());
PG_RETURN_BPCHAR_P((cmp >= 0) ? arg1 : arg2);
}
@@ -854,7 +860,8 @@ bpchar_smaller(PG_FUNCTION_ARGS)
len1 = bcTruelen(arg1);
len2 = bcTruelen(arg2);
- cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2);
+ cmp = varstr_cmp(VARDATA_ANY(arg1), len1, VARDATA_ANY(arg2), len2,
+ PG_GET_COLLATION());
PG_RETURN_BPCHAR_P((cmp <= 0) ? arg1 : arg2);
}
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index af28c15..05fadb0 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -15,8 +15,10 @@
#include "postgres.h"
#include <ctype.h>
+#include <xlocale.h>
#include "access/tuptoaster.h"
+#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
#include "libpq/md5.h"
#include "libpq/pqformat.h"
@@ -26,6 +28,7 @@
#include "utils/builtins.h"
#include "utils/bytea.h"
#include "utils/lsyscache.h"
+#include "utils/syscache.h"
#include "utils/pg_locale.h"
@@ -54,7 +57,7 @@ typedef struct
#define PG_GETARG_UNKNOWN_P_COPY(n) DatumGetUnknownPCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_UNKNOWN_P(x) PG_RETURN_POINTER(x)
-static int text_cmp(text *arg1, text *arg2);
+static int text_cmp(text *arg1, text *arg2, Oid collid);
static int32 text_length(Datum str);
static int text_position(text *t1, text *t2);
static void text_position_setup(text *t1, text *t2, TextPositionState *state);
@@ -1267,7 +1270,7 @@ text_position_cleanup(TextPositionState *state)
* whether arg1 is less than, equal to, or greater than arg2.
*/
int
-varstr_cmp(char *arg1, int len1, char *arg2, int len2)
+varstr_cmp(char *arg1, int len1, char *arg2, int len2, Oid collid)
{
int result;
@@ -1277,7 +1280,7 @@ varstr_cmp(char *arg1, int len1, char *arg2, int len2)
* slower, so we optimize the case where LC_COLLATE is C. We also try to
* optimize relatively-short strings by avoiding palloc/pfree overhead.
*/
- if (lc_collate_is_c())
+ if (lc_collate_is_c() && !collid)
{
result = strncmp(arg1, arg2, Min(len1, len2));
if ((result == 0) && (len1 != len2))
@@ -1391,7 +1394,29 @@ varstr_cmp(char *arg1, int len1, char *arg2, int len2)
memcpy(a2p, arg2, len2);
a2p[len2] = '\0';
- result = strcoll(a1p, a2p);
+ if (!collid)
+ result = strcoll(a1p, a2p);
+ else
+ {
+ locale_t mylocale;
+ HeapTuple htup;
+ const char *lc_collate;
+
+ htup = SearchSysCache1(COLLOID, collid);
+ if (!HeapTupleIsValid(htup))
+ elog(ERROR, "cache lookup failed for collation %u", collid);
+
+ lc_collate = NameStr(((Form_pg_collation) GETSTRUCT(htup))->collcollate);
+
+ /* TODO: cache locale lookups */
+ mylocale = newlocale(LC_COLLATE_MASK, lc_collate, NULL);
+ if (!mylocale)
+ elog(ERROR, "could not create locale \"%s\"", lc_collate);
+ result = strcoll_l(a1p, a2p, mylocale);
+ freelocale(mylocale);
+
+ ReleaseSysCache(htup);
+ }
/*
* In some locales strcoll() can claim that nonidentical strings are
@@ -1417,7 +1442,7 @@ varstr_cmp(char *arg1, int len1, char *arg2, int len2)
* Returns -1, 0 or 1
*/
static int
-text_cmp(text *arg1, text *arg2)
+text_cmp(text *arg1, text *arg2, Oid collid)
{
char *a1p,
*a2p;
@@ -1430,7 +1455,7 @@ text_cmp(text *arg1, text *arg2)
len1 = VARSIZE_ANY_EXHDR(arg1);
len2 = VARSIZE_ANY_EXHDR(arg2);
- return varstr_cmp(a1p, len1, a2p, len2);
+ return varstr_cmp(a1p, len1, a2p, len2, collid);
}
/*
@@ -1494,7 +1519,7 @@ text_lt(PG_FUNCTION_ARGS)
text *arg2 = PG_GETARG_TEXT_PP(1);
bool result;
- result = (text_cmp(arg1, arg2) < 0);
+ result = (text_cmp(arg1, arg2, PG_GET_COLLATION()) < 0);
PG_FREE_IF_COPY(arg1, 0);
PG_FREE_IF_COPY(arg2, 1);
@@ -1509,7 +1534,7 @@ text_le(PG_FUNCTION_ARGS)
text *arg2 = PG_GETARG_TEXT_PP(1);
bool result;
- result = (text_cmp(arg1, arg2) <= 0);
+ result = (text_cmp(arg1, arg2, PG_GET_COLLATION()) <= 0);
PG_FREE_IF_COPY(arg1, 0);
PG_FREE_IF_COPY(arg2, 1);
@@ -1524,7 +1549,7 @@ text_gt(PG_FUNCTION_ARGS)
text *arg2 = PG_GETARG_TEXT_PP(1);
bool result;
- result = (text_cmp(arg1, arg2) > 0);
+ result = (text_cmp(arg1, arg2, PG_GET_COLLATION()) > 0);
PG_FREE_IF_COPY(arg1, 0);
PG_FREE_IF_COPY(arg2, 1);
@@ -1539,7 +1564,7 @@ text_ge(PG_FUNCTION_ARGS)
text *arg2 = PG_GETARG_TEXT_PP(1);
bool result;
- result = (text_cmp(arg1, arg2) >= 0);
+ result = (text_cmp(arg1, arg2, PG_GET_COLLATION()) >= 0);
PG_FREE_IF_COPY(arg1, 0);
PG_FREE_IF_COPY(arg2, 1);
@@ -1554,7 +1579,7 @@ bttextcmp(PG_FUNCTION_ARGS)
text *arg2 = PG_GETARG_TEXT_PP(1);
int32 result;
- result = text_cmp(arg1, arg2);
+ result = text_cmp(arg1, arg2, PG_GET_COLLATION());
PG_FREE_IF_COPY(arg1, 0);
PG_FREE_IF_COPY(arg2, 1);
@@ -1570,7 +1595,7 @@ text_larger(PG_FUNCTION_ARGS)
text *arg2 = PG_GETARG_TEXT_PP(1);
text *result;
- result = ((text_cmp(arg1, arg2) > 0) ? arg1 : arg2);
+ result = ((text_cmp(arg1, arg2, PG_GET_COLLATION()) > 0) ? arg1 : arg2);
PG_RETURN_TEXT_P(result);
}
@@ -1582,7 +1607,7 @@ text_smaller(PG_FUNCTION_ARGS)
text *arg2 = PG_GETARG_TEXT_PP(1);
text *result;
- result = ((text_cmp(arg1, arg2) < 0) ? arg1 : arg2);
+ result = ((text_cmp(arg1, arg2, PG_GET_COLLATION()) < 0) ? arg1 : arg2);
PG_RETURN_TEXT_P(result);
}
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 61b06ac..8307beb 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -28,6 +28,7 @@
#include "catalog/pg_auth_members.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_cast.h"
+#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
@@ -266,6 +267,28 @@ static const struct cachedesc cacheinfo[] = {
},
64
},
+ {CollationRelationId, /* COLLNAMENSP */
+ CollationNameNspIndexId,
+ 2,
+ {
+ Anum_pg_collation_collname,
+ Anum_pg_collation_collnamespace,
+ 0,
+ 0
+ },
+ 128
+ },
+ {CollationRelationId, /* COLLOID */
+ CollationOidIndexId,
+ 1,
+ {
+ ObjectIdAttributeNumber,
+ 0,
+ 0,
+ 0
+ },
+ 128
+ },
{ConversionRelationId, /* CONDEFAULT */
ConversionDefaultIndexId,
4,
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index c3c0440..07479b6 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -2403,3 +2403,13 @@ get_call_expr_arg_stable(Node *expr, int argnum)
return false;
}
+
+/*
+ * Wrapper around exprColl() so that fmgr.h doesn't have to include
+ * the world.
+ */
+Oid
+get_call_collation(Node *expr)
+{
+ return exprColl(expr);
+}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index e839639..62f2480 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -5573,7 +5573,7 @@ flatten_set_variable_args(const char *name, List *args)
Datum interval;
char *intervalout;
- typoid = typenameTypeId(NULL, typeName, &typmod);
+ typoid = typenameTypeId(NULL, typeName, &typmod, NULL);
Assert(typoid == INTERVALOID);
interval =
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index f40ad87..f4566bf 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -98,6 +98,7 @@ static char *shdesc_file;
static char *hba_file;
static char *ident_file;
static char *conf_file;
+static char *collation_file;
static char *conversion_file;
static char *dictionary_file;
static char *info_schema_file;
@@ -170,6 +171,7 @@ static void get_set_pwd(void);
static void setup_depend(void);
static void setup_sysviews(void);
static void setup_description(void);
+static void setup_collation(void);
static void setup_conversion(void);
static void setup_dictionary(void);
static void setup_privileges(void);
@@ -1653,6 +1655,45 @@ setup_description(void)
}
/*
+ * populate pg_collation
+ */
+static void
+setup_collation(void)
+{
+ PG_CMD_DECL;
+
+ fputs(_("creating collations ..."), stdout);
+ fflush(stdout);
+
+ snprintf(cmd, sizeof(cmd),
+ "\"%s\" %s template1 >%s",
+ backend_exec, backend_options,
+ DEVNULL);
+
+ PG_CMD_OPEN;
+
+ PG_CMD_PUTS("CREATE TEMP TABLE tmp_pg_collation ( "
+ " locale name, "
+ " encoding text) WITHOUT OIDS;\n");
+
+ PG_CMD_PRINTF1("COPY tmp_pg_collation FROM E'%s';\n",
+ escape_quotes(collation_file));
+
+ PG_CMD_PUTS("INSERT INTO pg_collation (collname, collnamespace, collencoding, collcollate, collctype) "
+ " SELECT locale, "
+ " (SELECT oid FROM pg_namespace WHERE nspname = 'pg_catalog'), "
+ " pg_char_to_encoding(encoding), "
+ " locale, locale "
+ " FROM tmp_pg_collation "
+ " WHERE pg_char_to_encoding(encoding) <> -1;\n");
+
+ PG_CMD_CLOSE;
+
+ check_ok();
+
+}
+
+/*
* load conversion functions
*/
static void
@@ -2761,6 +2802,7 @@ main(int argc, char *argv[])
set_input(&hba_file, "pg_hba.conf.sample");
set_input(&ident_file, "pg_ident.conf.sample");
set_input(&conf_file, "postgresql.conf.sample");
+ set_input(&collation_file, "locales.txt");
set_input(&conversion_file, "conversion_create.sql");
set_input(&dictionary_file, "snowball_create.sql");
set_input(&info_schema_file, "information_schema.sql");
@@ -2794,6 +2836,7 @@ main(int argc, char *argv[])
check_input(hba_file);
check_input(ident_file);
check_input(conf_file);
+ check_input(collation_file);
check_input(conversion_file);
check_input(dictionary_file);
check_input(info_schema_file);
@@ -3109,6 +3152,8 @@ main(int argc, char *argv[])
setup_description();
+ setup_collation();
+
setup_conversion();
setup_dictionary();
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 4f7d920..6242812 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201004261
+#define CATALOG_VERSION_NO 201007071
#endif
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 4f437fd..8060652 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -107,6 +107,11 @@ DECLARE_UNIQUE_INDEX(pg_class_oid_index, 2662, on pg_class using btree(oid oid_o
DECLARE_UNIQUE_INDEX(pg_class_relname_nsp_index, 2663, on pg_class using btree(relname name_ops, relnamespace oid_ops));
#define ClassNameNspIndexId 2663
+DECLARE_UNIQUE_INDEX(pg_collation_name_nsp_index, 3037, on pg_collation using btree(collname name_ops, collnamespace oid_ops));
+#define CollationNameNspIndexId 3037
+DECLARE_UNIQUE_INDEX(pg_collation_oid_index, 3038, on pg_collation using btree(oid oid_ops));
+#define CollationOidIndexId 3038
+
/* This following index is not used for a cache and is not unique */
DECLARE_INDEX(pg_constraint_conname_nsp_index, 2664, on pg_constraint using btree(conname name_ops, connamespace oid_ops));
#define ConstraintNameNspIndexId 2664
diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h
index fe3b783..541728f 100644
--- a/src/include/catalog/namespace.h
+++ b/src/include/catalog/namespace.h
@@ -111,6 +111,7 @@ extern OverrideSearchPath *GetOverrideSearchPath(MemoryContext context);
extern void PushOverrideSearchPath(OverrideSearchPath *newpath);
extern void PopOverrideSearchPath(void);
+extern Oid FindCollationByName(List *collname);
extern Oid FindConversionByName(List *conname);
extern Oid FindDefaultConversionProc(int4 for_encoding, int4 to_encoding);
diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h
index 078cae4..6d14bfb 100644
--- a/src/include/catalog/pg_attribute.h
+++ b/src/include/catalog/pg_attribute.h
@@ -142,6 +142,9 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
/* Number of times inherited from direct parent relation(s) */
int4 attinhcount;
+ /* attribute's collation */
+ Oid attcollation;
+
/*
* VARIABLE LENGTH FIELDS start here. These fields may be NULL, too.
*
@@ -159,10 +162,10 @@ CATALOG(pg_attribute,1249) BKI_BOOTSTRAP BKI_WITHOUT_OIDS BKI_ROWTYPE_OID(75) BK
* ATTRIBUTE_FIXED_PART_SIZE is the size of the fixed-layout,
* guaranteed-not-null part of a pg_attribute row. This is in fact as much
* of the row as gets copied into tuple descriptors, so don't expect you
- * can access fields beyond attinhcount except in a real tuple!
+ * can access fields beyond attcollation except in a real tuple!
*/
#define ATTRIBUTE_FIXED_PART_SIZE \
- (offsetof(FormData_pg_attribute,attinhcount) + sizeof(int4))
+ (offsetof(FormData_pg_attribute,attcollation) + sizeof(Oid))
/* ----------------
* Form_pg_attribute corresponds to a pointer to a tuple with
@@ -176,7 +179,7 @@ typedef FormData_pg_attribute *Form_pg_attribute;
* ----------------
*/
-#define Natts_pg_attribute 19
+#define Natts_pg_attribute 20
#define Anum_pg_attribute_attrelid 1
#define Anum_pg_attribute_attname 2
#define Anum_pg_attribute_atttypid 3
@@ -194,8 +197,9 @@ typedef FormData_pg_attribute *Form_pg_attribute;
#define Anum_pg_attribute_attisdropped 15
#define Anum_pg_attribute_attislocal 16
#define Anum_pg_attribute_attinhcount 17
-#define Anum_pg_attribute_attacl 18
-#define Anum_pg_attribute_attoptions 19
+#define Anum_pg_attribute_attcollation 18
+#define Anum_pg_attribute_attacl 19
+#define Anum_pg_attribute_attoptions 20
/* ----------------
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index 5ea514d..1b64c14 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -132,9 +132,9 @@ typedef FormData_pg_class *Form_pg_class;
*/
/* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */
-DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f f r 28 0 t f f f f f 3 _null_ _null_ ));
+DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f f r 29 0 t f f f f f 3 _null_ _null_ ));
DESCR("");
-DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f f r 19 0 f f f f f f 3 _null_ _null_ ));
+DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f f r 20 0 f f f f f f 3 _null_ _null_ ));
DESCR("");
DATA(insert OID = 1255 ( pg_proc PGNSP 81 0 PGUID 0 0 0 0 0 0 0 f f f r 25 0 t f f f f f 3 _null_ _null_ ));
DESCR("");
diff --git a/src/include/catalog/pg_collation.h b/src/include/catalog/pg_collation.h
new file mode 100644
index 0000000..cf7c9a5
--- /dev/null
+++ b/src/include/catalog/pg_collation.h
@@ -0,0 +1,58 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_collation.h
+ * definition of the system "collation" relation (pg_collation)
+ * along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $PostgreSQL$
+ *
+ * NOTES
+ * the genbki.pl script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_COLLATION_H
+#define PG_COLLATION_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ * pg_collation definition. cpp turns this into
+ * typedef struct FormData_pg_collation
+ * ----------------
+ */
+#define CollationRelationId 2614
+
+CATALOG(pg_collation,2614)
+{
+ NameData collname; /* collation name */
+ Oid collnamespace; /* OID of namespace containing this collation */
+ int4 collencoding; /* encoding that this collation applies to */
+ NameData collcollate; /* LC_COLLATE setting */
+ NameData collctype; /* LC_CTYPE setting */
+} FormData_pg_collation;
+
+/* ----------------
+ * Form_pg_collation corresponds to a pointer to a row with
+ * the format of pg_collation relation.
+ * ----------------
+ */
+typedef FormData_pg_collation *Form_pg_collation;
+
+/* ----------------
+ * compiler constants for pg_collation
+ * ----------------
+ */
+#define Natts_pg_collation 5
+#define Anum_pg_collation_collname 1
+#define Anum_pg_collation_collnamespace 2
+#define Anum_pg_collation_collencoding 3
+#define Anum_pg_collation_collcollate 4
+#define Anum_pg_collation_collctype 5
+
+#endif /* PG_COLLATION_H */
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index c22aacc..9828a50 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -175,6 +175,11 @@ CATALOG(pg_type,1247) BKI_BOOTSTRAP BKI_ROWTYPE_OID(71) BKI_SCHEMA_MACRO
bool typnotnull;
/*
+ * Type can use collations.
+ */
+ bool typcollatable;
+
+ /*
* Domains use typbasetype to show the base (or domain) type that the
* domain is based on. Zero if the type is not a domain.
*/
@@ -224,7 +229,7 @@ typedef FormData_pg_type *Form_pg_type;
* compiler constants for pg_type
* ----------------
*/
-#define Natts_pg_type 28
+#define Natts_pg_type 29
#define Anum_pg_type_typname 1
#define Anum_pg_type_typnamespace 2
#define Anum_pg_type_typowner 3
@@ -248,11 +253,12 @@ typedef FormData_pg_type *Form_pg_type;
#define Anum_pg_type_typalign 21
#define Anum_pg_type_typstorage 22
#define Anum_pg_type_typnotnull 23
-#define Anum_pg_type_typbasetype 24
-#define Anum_pg_type_typtypmod 25
-#define Anum_pg_type_typndims 26
-#define Anum_pg_type_typdefaultbin 27
-#define Anum_pg_type_typdefault 28
+#define Anum_pg_type_typcollatable 24
+#define Anum_pg_type_typbasetype 25
+#define Anum_pg_type_typtypmod 26
+#define Anum_pg_type_typndims 27
+#define Anum_pg_type_typdefaultbin 28
+#define Anum_pg_type_typdefault 29
/* ----------------
@@ -269,83 +275,83 @@ typedef FormData_pg_type *Form_pg_type;
*/
/* OIDS 1 - 99 */
-DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f f 0 -1 0 _null_ _null_ ));
DESCR("boolean, 'true'/'false'");
#define BOOLOID 16
-DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f f 0 -1 0 _null_ _null_ ));
DESCR("variable-length string, binary values escaped");
#define BYTEAOID 17
-DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f f 0 -1 0 _null_ _null_ ));
DESCR("single character");
#define CHAROID 18
-DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f f 0 -1 0 _null_ _null_ ));
DESCR("63-character type for storing system identifiers");
#define NAMEOID 19
-DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f f 0 -1 0 _null_ _null_ ));
DESCR("~18 digit integer, 8-byte storage");
#define INT8OID 20
-DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f f 0 -1 0 _null_ _null_ ));
DESCR("-32 thousand to 32 thousand, 2-byte storage");
#define INT2OID 21
-DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f f 0 -1 0 _null_ _null_ ));
DESCR("array of int2, used in system tables");
#define INT2VECTOROID 22
-DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f f 0 -1 0 _null_ _null_ ));
DESCR("-2 billion to 2 billion integer, 4-byte storage");
#define INT4OID 23
-DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f f 0 -1 0 _null_ _null_ ));
DESCR("registered procedure");
#define REGPROCOID 24
-DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f t 0 -1 0 _null_ _null_ ));
DESCR("variable-length string, no limit specified");
#define TEXTOID 25
-DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f f 0 -1 0 _null_ _null_ ));
DESCR("object identifier(oid), maximum 4 billion");
#define OIDOID 26
-DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f f 0 -1 0 _null_ _null_ ));
DESCR("(block, offset), physical location of tuple");
#define TIDOID 27
-DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f f 0 -1 0 _null_ _null_ ));
DESCR("transaction id");
#define XIDOID 28
-DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f f 0 -1 0 _null_ _null_ ));
DESCR("command identifier type, sequence in transaction id");
#define CIDOID 29
-DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f f 0 -1 0 _null_ _null_ ));
DESCR("array of oids, used in system tables");
#define OIDVECTOROID 30
/* hand-built rowtype entries for bootstrapped catalogs */
/* NB: OIDs assigned here must match the BKI_ROWTYPE_OID declarations */
-DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f f 0 -1 0 _null_ _null_ ));
/* OIDS 100 - 199 */
-DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f f 0 -1 0 _null_ _null_ ));
DESCR("XML content");
#define XMLOID 142
-DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
/* OIDS 200 - 299 */
-DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f f 0 -1 0 _null_ _null_ ));
DESCR("storage manager");
/* OIDS 300 - 399 */
@@ -355,231 +361,231 @@ DESCR("storage manager");
/* OIDS 500 - 599 */
/* OIDS 600 - 699 */
-DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f f 0 -1 0 _null_ _null_ ));
DESCR("geometric point '(x, y)'");
#define POINTOID 600
-DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f f 0 -1 0 _null_ _null_ ));
DESCR("geometric line segment '(pt1,pt2)'");
#define LSEGOID 601
-DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f f 0 -1 0 _null_ _null_ ));
DESCR("geometric path '(pt1,...)'");
#define PATHOID 602
-DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f f 0 -1 0 _null_ _null_ ));
DESCR("geometric box '(lower left,upper right)'");
#define BOXOID 603
-DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f f 0 -1 0 _null_ _null_ ));
DESCR("geometric polygon '(pt1,...)'");
#define POLYGONOID 604
-DATA(insert OID = 628 ( line PGNSP PGUID 32 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 628 ( line PGNSP PGUID 32 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f f 0 -1 0 _null_ _null_ ));
DESCR("geometric line (not implemented)");
#define LINEOID 628
-DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - - d x f f 0 -1 0 _null_ _null_ ));
DESCR("");
/* OIDS 700 - 799 */
-DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f f 0 -1 0 _null_ _null_ ));
DESCR("single-precision floating point number, 4-byte storage");
#define FLOAT4OID 700
-DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f f 0 -1 0 _null_ _null_ ));
DESCR("double-precision floating point number, 8-byte storage");
#define FLOAT8OID 701
-DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f f 0 -1 0 _null_ _null_ ));
DESCR("absolute, limited-range date and time (Unix system time)");
#define ABSTIMEOID 702
-DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f f 0 -1 0 _null_ _null_ ));
DESCR("relative, limited-range time interval (Unix delta time)");
#define RELTIMEOID 703
-DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f f 0 -1 0 _null_ _null_ ));
DESCR("(abstime,abstime), time interval");
#define TINTERVALOID 704
-DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f b X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f b X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f f 0 -1 0 _null_ _null_ ));
DESCR("");
#define UNKNOWNOID 705
-DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f f 0 -1 0 _null_ _null_ ));
DESCR("geometric circle '(center,radius)'");
#define CIRCLEOID 718
-DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - - d x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f f 0 -1 0 _null_ _null_ ));
DESCR("monetary amounts, $d,ddd.cc");
#define CASHOID 790
-DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - - d x f f 0 -1 0 _null_ _null_ ));
/* OIDS 800 - 899 */
-DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f f 0 -1 0 _null_ _null_ ));
DESCR("XX:XX:XX:XX:XX:XX, MAC address");
#define MACADDROID 829
-DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f f 0 -1 0 _null_ _null_ ));
DESCR("IP address/netmask, host address, netmask optional");
#define INETOID 869
-DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f f 0 -1 0 _null_ _null_ ));
DESCR("network IP address/netmask, network address");
#define CIDROID 650
/* OIDS 900 - 999 */
/* OIDS 1000 - 1099 */
-DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
#define INT4ARRAYOID 1007
-DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
#define TEXTARRAYOID 1009
-DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout - i x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout - i x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - - d x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - - d x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - - d x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - - d x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - - d x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
#define FLOAT4ARRAYOID 1021
-DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - - d x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - - d x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f f 0 -1 0 _null_ _null_ ));
DESCR("access control list");
#define ACLITEMOID 1033
-DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
#define CSTRINGARRAYOID 1263
-DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f t 0 -1 0 _null_ _null_ ));
DESCR("char(length), blank-padded string, fixed storage length");
#define BPCHAROID 1042
-DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f t 0 -1 0 _null_ _null_ ));
DESCR("varchar(length), non-blank-padded string, variable storage length");
#define VARCHAROID 1043
-DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f f 0 -1 0 _null_ _null_ ));
DESCR("date");
#define DATEOID 1082
-DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f f 0 -1 0 _null_ _null_ ));
DESCR("time of day");
#define TIMEOID 1083
/* OIDS 1100 - 1199 */
-DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f f 0 -1 0 _null_ _null_ ));
DESCR("date and time");
#define TIMESTAMPOID 1114
-DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout - d x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout - d x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f f 0 -1 0 _null_ _null_ ));
DESCR("date and time with time zone");
#define TIMESTAMPTZOID 1184
-DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout - d x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f f 0 -1 0 _null_ _null_ ));
DESCR("@ <number> <units>, time interval");
#define INTERVALOID 1186
-DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout - d x f f 0 -1 0 _null_ _null_ ));
/* OIDS 1200 - 1299 */
-DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout - i x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f f 0 -1 0 _null_ _null_ ));
DESCR("time of day with time zone");
#define TIMETZOID 1266
-DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout - d x f f 0 -1 0 _null_ _null_ ));
/* OIDS 1500 - 1599 */
-DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f f 0 -1 0 _null_ _null_ ));
DESCR("fixed-length bit string");
#define BITOID 1560
-DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout - i x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f f 0 -1 0 _null_ _null_ ));
DESCR("variable-length bit string");
#define VARBITOID 1562
-DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout - i x f f 0 -1 0 _null_ _null_ ));
/* OIDS 1600 - 1699 */
/* OIDS 1700 - 1799 */
-DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f f 0 -1 0 _null_ _null_ ));
DESCR("numeric(precision, decimal), arbitrary precision number");
#define NUMERICOID 1700
-DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f f 0 -1 0 _null_ _null_ ));
DESCR("reference to cursor (portal name)");
#define REFCURSOROID 1790
/* OIDS 2200 - 2299 */
-DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f f 0 -1 0 _null_ _null_ ));
DESCR("registered procedure (with args)");
#define REGPROCEDUREOID 2202
-DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f f 0 -1 0 _null_ _null_ ));
DESCR("registered operator");
#define REGOPEROID 2203
-DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f f 0 -1 0 _null_ _null_ ));
DESCR("registered operator (with args)");
#define REGOPERATOROID 2204
-DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f f 0 -1 0 _null_ _null_ ));
DESCR("registered class");
#define REGCLASSOID 2205
-DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f f 0 -1 0 _null_ _null_ ));
DESCR("registered type");
#define REGTYPEOID 2206
-DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
#define REGTYPEARRAYOID 2211
/* uuid */
-DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f f 0 -1 0 _null_ _null_ ));
DESCR("UUID datatype");
-DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
/* text search */
-DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f f 0 -1 0 _null_ _null_ ));
DESCR("text representation for text search");
#define TSVECTOROID 3614
-DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f f 0 -1 0 _null_ _null_ ));
DESCR("GiST index internal text representation for text search");
#define GTSVECTOROID 3642
-DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f f 0 -1 0 _null_ _null_ ));
DESCR("query representation for text search");
#define TSQUERYOID 3615
-DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f f 0 -1 0 _null_ _null_ ));
DESCR("registered text search configuration");
#define REGCONFIGOID 3734
-DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f f 0 -1 0 _null_ _null_ ));
DESCR("registered text search dictionary");
#define REGDICTIONARYOID 3769
-DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - - i x f f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f f 0 -1 0 _null_ _null_ ));
DESCR("txid snapshot");
-DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - - d x f f 0 -1 0 _null_ _null_ ));
/*
* pseudo-types
@@ -594,31 +600,31 @@ DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 a
* but there is now support for it in records and arrays. Perhaps we should
* just treat it as a regular base type?
*/
-DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f f 0 -1 0 _null_ _null_ ));
#define RECORDOID 2249
-DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - - d x f f 0 -1 0 _null_ _null_ ));
#define RECORDARRAYOID 2287
-DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f f 0 -1 0 _null_ _null_ ));
#define CSTRINGOID 2275
-DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f f 0 -1 0 _null_ _null_ ));
#define ANYOID 2276
-DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f f 0 -1 0 _null_ _null_ ));
#define ANYARRAYOID 2277
-DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out - - - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out - - - - - i p f f 0 -1 0 _null_ _null_ ));
#define VOIDOID 2278
-DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f f 0 -1 0 _null_ _null_ ));
#define TRIGGEROID 2279
-DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f f 0 -1 0 _null_ _null_ ));
#define LANGUAGE_HANDLEROID 2280
-DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f f 0 -1 0 _null_ _null_ ));
#define INTERNALOID 2281
-DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f f 0 -1 0 _null_ _null_ ));
#define OPAQUEOID 2282
-DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f f 0 -1 0 _null_ _null_ ));
#define ANYELEMENTOID 2283
-DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f f 0 -1 0 _null_ _null_ ));
#define ANYNONARRAYOID 2776
-DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f f 0 -1 0 _null_ _null_ ));
#define ANYENUMOID 3500
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index c502b96..cd6d802 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -296,6 +296,7 @@ extern struct varlena *pg_detoast_datum_packed(struct varlena * datum);
#define PG_RETURN_VARCHAR_P(x) PG_RETURN_POINTER(x)
#define PG_RETURN_HEAPTUPLEHEADER(x) PG_RETURN_POINTER(x)
+#define PG_GET_COLLATION() get_call_collation(fcinfo->flinfo->fn_expr)
/*-------------------------------------------------------------------------
* Support for detecting call convention of dynamically-loaded functions
@@ -518,6 +519,7 @@ extern Oid get_fn_expr_argtype(FmgrInfo *flinfo, int argnum);
extern Oid get_call_expr_argtype(fmNodePtr expr, int argnum);
extern bool get_fn_expr_arg_stable(FmgrInfo *flinfo, int argnum);
extern bool get_call_expr_arg_stable(fmNodePtr expr, int argnum);
+extern Oid get_call_collation(fmNodePtr expr);
/*
* Routines in dfmgr.c
diff --git a/src/include/nodes/nodeFuncs.h b/src/include/nodes/nodeFuncs.h
index aad6dc1..e3ff5d6 100644
--- a/src/include/nodes/nodeFuncs.h
+++ b/src/include/nodes/nodeFuncs.h
@@ -27,6 +27,7 @@
extern Oid exprType(Node *expr);
extern int32 exprTypmod(Node *expr);
+extern Oid exprColl(Node *expr);
extern bool exprIsLengthCoercion(Node *expr, int32 *coercedTypmod);
extern bool expression_returns_set(Node *clause);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index a5f5df5..42a6c70 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -164,6 +164,7 @@ typedef enum NodeTag
T_JoinExpr,
T_FromExpr,
T_IntoClause,
+ T_CollateClause,
/*
* TAGS FOR EXPRESSION STATE NODES (execnodes.h)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index b591073..01a3a39 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -164,7 +164,8 @@ typedef struct Query
* specify the type by OID than by name. If "names" is NIL then the
* actual type OID is given by typeOid, otherwise typeOid is unused.
* Similarly, if "typmods" is NIL then the actual typmod is expected to
- * be prespecified in typemod, otherwise typemod is unused.
+ * be prespecified in typemod, otherwise typemod is unused. Similarly
+ * for collnames/collOid.
*
* If pct_type is TRUE, then names is actually a field name and we look up
* the type of that field. Otherwise (the normal case), names is a type
@@ -180,6 +181,8 @@ typedef struct TypeName
List *typmods; /* type modifier expression(s) */
int32 typemod; /* prespecified type modifier */
List *arrayBounds; /* array bounds */
+ List *collnames; /* collation name */
+ Oid collOid; /* collation by OID */
int location; /* token location, or -1 if unknown */
} TypeName;
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index c523d72..013bc0d 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -144,6 +144,7 @@ typedef struct Var
* levels up */
Index varnoold; /* original value of varno, for debugging */
AttrNumber varoattno; /* original value of varattno */
+ Oid varcollid; /* collation */
int location; /* token location, or -1 if unknown */
} Var;
@@ -324,6 +325,7 @@ typedef struct FuncExpr
bool funcretset; /* true if function returns set */
CoercionForm funcformat; /* how to display this function call */
List *args; /* arguments to the function */
+ Oid collid; /* collation OID to use by function */
int location; /* token location, or -1 if unknown */
} FuncExpr;
@@ -367,6 +369,7 @@ typedef struct OpExpr
Oid opresulttype; /* PG_TYPE OID of result value */
bool opretset; /* true if operator returns set */
List *args; /* arguments to the operator (1 or 2) */
+ Oid collid; /* collation OID to use by operator */
int location; /* token location, or -1 if unknown */
} OpExpr;
@@ -642,6 +645,18 @@ typedef struct RelabelType
int location; /* token location, or -1 if unknown */
} RelabelType;
+/*
+ * CollateClause - COLLATE
+ */
+typedef struct CollateClause
+{
+ NodeTag type;
+ Node *arg; /* original expression */
+ List *collnames; /* assigned collation */
+ Oid collOid; /* resolved collation OID */
+ int location; /* token location, or -1 if unknown */
+} CollateClause;
+
/* ----------------
* CoerceViaIO
*
diff --git a/src/include/parser/parse_type.h b/src/include/parser/parse_type.h
index fa99ef7..0e17f7f 100644
--- a/src/include/parser/parse_type.h
+++ b/src/include/parser/parse_type.h
@@ -20,11 +20,13 @@
typedef HeapTuple Type;
extern Type LookupTypeName(ParseState *pstate, const TypeName *typeName,
- int32 *typmod_p);
+ int32 *typmod_p, Oid *colloid_p);
extern Type typenameType(ParseState *pstate, const TypeName *typeName,
- int32 *typmod_p);
+ int32 *typmod_p, Oid *colloid_p);
extern Oid typenameTypeId(ParseState *pstate, const TypeName *typeName,
- int32 *typmod_p);
+ int32 *typmod_p, Oid *colloid_p);
+
+extern Oid LookupCollation(ParseState *pstate, List *collnames, int location);
extern char *TypeNameToString(const TypeName *typeName);
extern char *TypeNameListToString(List *typenames);
diff --git a/src/include/parser/parsetree.h b/src/include/parser/parsetree.h
index 377589a..db36c18 100644
--- a/src/include/parser/parsetree.h
+++ b/src/include/parser/parsetree.h
@@ -52,7 +52,7 @@ extern char *get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum);
* type and typemod info for that attribute of that RTE.
*/
extern void get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
- Oid *vartype, int32 *vartypmod);
+ Oid *vartype, int32 *vartypmod, Oid *varcollid);
/*
* Check whether an attribute of an RTE has been dropped (note that
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 0b60e09..ab2b925 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -705,7 +705,7 @@ extern Datum textoverlay(PG_FUNCTION_ARGS);
extern Datum textoverlay_no_len(PG_FUNCTION_ARGS);
extern Datum name_text(PG_FUNCTION_ARGS);
extern Datum text_name(PG_FUNCTION_ARGS);
-extern int varstr_cmp(char *arg1, int len1, char *arg2, int len2);
+extern int varstr_cmp(char *arg1, int len1, char *arg2, int len2, Oid collid);
extern List *textToQualifiedNameList(text *textval);
extern bool SplitIdentifierString(char *rawstring, char separator,
List **namelist);
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 2f19e5c..92ce4e2 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -44,6 +44,8 @@ enum SysCacheIdentifier
CASTSOURCETARGET,
CLAAMNAMENSP,
CLAOID,
+ COLLNAMENSP,
+ COLLOID,
CONDEFAULT,
CONNAMENSP,
CONSTROID,
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 656ea73..31842a9 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -1536,7 +1536,7 @@ plpgsql_parse_wordtype(char *ident)
* Word wasn't found in the namespace stack. Try to find a data type with
* that name, but ignore shell types and complex types.
*/
- typeTup = LookupTypeName(NULL, makeTypeName(ident), NULL);
+ typeTup = LookupTypeName(NULL, makeTypeName(ident), NULL, NULL);
if (typeTup)
{
Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
diff --git a/src/test/regress/expected/collate.out b/src/test/regress/expected/collate.out
new file mode 100644
index 0000000..19ab9a5
--- /dev/null
+++ b/src/test/regress/expected/collate.out
@@ -0,0 +1,42 @@
+CREATE TABLE collate_test1 (
+ a int,
+ b text COLLATE "en_US.utf8"
+);
+CREATE TABLE collate_test_fail (
+ a int,
+ b text COLLATE "en_US"
+);
+ERROR: collation "en_US" is not applicable to current database encoding "UTF8"
+LINE 3: b text COLLATE "en_US"
+ ^
+CREATE TABLE collate_test_fail (
+ a int,
+ b text COLLATE "foo"
+);
+ERROR: collation "foo" does not exist
+CREATE TABLE collate_test_fail (
+ a int COLLATE "en_US.utf8",
+ b text
+);
+ERROR: collations are not supported by type "pg_catalog.int4"
+LINE 2: a int COLLATE "en_US.utf8",
+ ^
+CREATE TABLE collate_test2 (
+ a int,
+ b text COLLATE "sv_SE.utf8"
+);
+INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'äbc'), (3, 'bbc');
+INSERT INTO collate_test2 SELECT * FROM collate_test1;
+SELECT * FROM collate_test1 WHERE b >= 'bbc';
+ a | b
+---+-----
+ 3 | bbc
+(1 row)
+
+SELECT * FROM collate_test2 WHERE b >= 'bbc';
+ a | b
+---+-----
+ 2 | äbc
+ 3 | bbc
+(2 rows)
+
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 1d9e110..8608171 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -92,6 +92,7 @@ SELECT relname, relhasindex
pg_authid | t
pg_cast | t
pg_class | t
+ pg_collation | t
pg_constraint | t
pg_conversion | t
pg_database | t
@@ -153,7 +154,7 @@ SELECT relname, relhasindex
timetz_tbl | f
tinterval_tbl | f
varchar_tbl | f
-(142 rows)
+(143 rows)
--
-- another sanity check: every system catalog that has OIDs should have
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index 7529777..e8cdb79 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -84,7 +84,7 @@ test: rules
# ----------
# Another group of parallel tests
# ----------
-test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap
+test: select_views portals_p2 foreign_key cluster dependency guc bitmapops combocid tsearch tsdicts foreign_data window xmlmap collate
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 5f185f9..761bff6 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -103,6 +103,7 @@ test: tsdicts
test: foreign_data
test: window
test: xmlmap
+test: collate
test: plancache
test: limit
test: plpgsql
diff --git a/src/test/regress/sql/collate.sql b/src/test/regress/sql/collate.sql
new file mode 100644
index 0000000..60ffbe1
--- /dev/null
+++ b/src/test/regress/sql/collate.sql
@@ -0,0 +1,30 @@
+CREATE TABLE collate_test1 (
+ a int,
+ b text COLLATE "en_US.utf8"
+);
+
+CREATE TABLE collate_test_fail (
+ a int,
+ b text COLLATE "en_US"
+);
+
+CREATE TABLE collate_test_fail (
+ a int,
+ b text COLLATE "foo"
+);
+
+CREATE TABLE collate_test_fail (
+ a int COLLATE "en_US.utf8",
+ b text
+);
+
+CREATE TABLE collate_test2 (
+ a int,
+ b text COLLATE "sv_SE.utf8"
+);
+
+INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'äbc'), (3, 'bbc');
+INSERT INTO collate_test2 SELECT * FROM collate_test1;
+
+SELECT * FROM collate_test1 WHERE b >= 'bbc';
+SELECT * FROM collate_test2 WHERE b >= 'bbc';
Peter Eisentraut <peter_e@gmx.net> wrote:
Here is a proof of concept for per-column collation support.
Did you want a WIP review of that patch? (CF closing to new
submissions soon....)
-Kevin
Hello
I have only one question - If I understand well you can use collate
just for sort. What is your plan for range search operation? Sort is
interesting and I am sure important for multilangual applications, for
me - more important is case sensitive, case insensitive, accent
sensitive, insensitive filtering - do you have a plan for it?
Regards
Pavel Stehule
2010/7/13 Peter Eisentraut <peter_e@gmx.net>:
Here is a proof of concept for per-column collation support.
Here is how it works: When creating a table, an optional COLLATE clause
can specify a collation name, which is stored (by OID) in pg_attribute.
This becomes part of the type information and is propagated through the
expression parse analysis, like typmod. When an operator or function
call is parsed (transformed), the collations of the arguments are
unified, using some rules (like type analysis, but different in detail).
The collations of the function/operator arguments come either from Var
nodes which in turn got them from pg_attribute, or from other
function and operator calls, or you can override them with explicit
COLLATE clauses (not yet implemented, but will work a bit like
RelabelType). At the end, each function or operator call gets one
collation to use.
what about DISTINCT clause, maybe GROUP BY clause ?
regards
Pavel
Show quoted text
The function call itself can then look up the collation using the
fcinfo->flinfo->fn_expr field. (Works for operator calls, but doesn't
work for sort operations, needs more thought.)A collation is in this implementation defined as an lc_collate string
and an lc_ctype string. The implementation of functions interested in
that information, such as comparison operators, or upper and lower
functions, will take the collation OID that is passed in, look up the
locale string, and use the xlocale.h interface (newlocale(),
strcoll_l()) to compute the result.(Note that the xlocale stuff is only 10 or so lines in this patch. It
should be feasible to allow other appropriate locale libraries to be
used.)Loose ends:
- Support function calls (currently only operator calls) (easy)
- Implementation of sort clauses
- Indexing support/integration
- Domain support (should be straightforward)
- Make all expression node types deal with collation information
appropriately- Explicit COLLATE clause on expressions
- Caching and not leaking memory of locale lookups
- I have typcollatable to mark which types can accept collation
information, but perhaps there should also be proicareaboutcollation
to skip collation resolution when none of the functions in the
expression tree care.You can start by reading the collate.sql regression test file to see
what it can do. Btw., regression tests only work with "make check
MULTIBYTE=UTF8". And it (probably) only works with glibc for now.--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On ons, 2010-07-14 at 19:35 +0200, Pavel Stehule wrote:
I have only one question - If I understand well you can use collate
just for sort. What is your plan for range search operation?
My patch does range searches. Sorting uses the same operators, so both
will be supported. (Sorting is not yet implemented, as I had written.)
Sort is
interesting and I am sure important for multilangual applications, for
me - more important is case sensitive, case insensitive, accent
sensitive, insensitive filtering - do you have a plan for it?
You may be able to do some of these by using appropriate locale
definitions. I'd need some examples to be able to tell for sure.
what about DISTINCT clause, maybe GROUP BY clause ?
DISTINCT and GROUP BY work with equality, which is not affected by
locales (at least under the current rules).
2010/7/14 Peter Eisentraut <peter_e@gmx.net>:
On ons, 2010-07-14 at 19:35 +0200, Pavel Stehule wrote:
I have only one question - If I understand well you can use collate
just for sort. What is your plan for range search operation?My patch does range searches. Sorting uses the same operators, so both
will be supported. (Sorting is not yet implemented, as I had written.)Sort is
interesting and I am sure important for multilangual applications, for
me - more important is case sensitive, case insensitive, accent
sensitive, insensitive filtering - do you have a plan for it?You may be able to do some of these by using appropriate locale
definitions. I'd need some examples to be able to tell for sure.what about DISTINCT clause, maybe GROUP BY clause ?
DISTINCT and GROUP BY work with equality, which is not affected by
locales (at least under the current rules).
:( maybe we have to enhance a locales - or do some work in this way.
In Czech's IS is relative often operation some like
name = 'Stěhule' COLLATION cs_CZ_cs_ai -- compare case insensitive
accent insensitive
PostgreSQL is last db, that doesn't integreated support for it
Regards
Pavel
Show quoted text
On tor, 2010-07-15 at 05:57 +0200, Pavel Stehule wrote:
:( maybe we have to enhance a locales - or do some work in this way.
In Czech's IS is relative often operation some likename = 'Stěhule' COLLATION cs_CZ_cs_ai -- compare case insensitive
accent insensitivePostgreSQL is last db, that doesn't integreated support for it
Well, the comparison function varstr_cmp() contains this comment:
/*
* In some locales strcoll() can claim that nonidentical strings are
* equal. Believing that would be bad news for a number of reasons,
* so we follow Perl's lead and sort "equal" strings according to
* strcmp().
*/
This might not be strictly necessary, seeing that citext obviously
doesn't work that way, but resolving this is really an orthogonal issue.
If you fix that and you have a locale that does what you want, my patch
will help you get your example working.
Peter Eisentraut <peter_e@gmx.net> writes:
Well, the comparison function varstr_cmp() contains this comment:
/*
* In some locales strcoll() can claim that nonidentical strings are
* equal. Believing that would be bad news for a number of reasons,
* so we follow Perl's lead and sort "equal" strings according to
* strcmp().
*/
This might not be strictly necessary, seeing that citext obviously
doesn't work that way, but resolving this is really an orthogonal issue.
The problem with not doing that is it breaks hashing --- hash joins and
hash aggregation being the real pain points.
citext works around this in a rather klugy fashion by decreeing that two
strings are equal iff their str_tolower() conversions are bitwise equal.
So it can hash the str_tolower() representation. But that's kinda slow
and it fails in the general case anyhow, I think.
regards, tom lane
On Thu, Jul 15, 2010 at 4:24 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
The problem with not doing that is it breaks hashing --- hash joins and
hash aggregation being the real pain points.citext works around this in a rather klugy fashion by decreeing that two
strings are equal iff their str_tolower() conversions are bitwise equal.
So it can hash the str_tolower() representation. But that's kinda slow
and it fails in the general case anyhow, I think.
I think the general equivalent would be to call strxfrm and hash the
result of that.
--
greg
On Tue, Jul 13, 2010 at 1:25 PM, Peter Eisentraut <peter_e@gmx.net> wrote:
Here is a proof of concept for per-column collation support.
Hi,
i was looking at this.
nowadays, CREATE DATABASE has a lc_collate clause. is the new collate
clause similar as the lc_collate?
i mean, is lc_collate what we will use as a default?
if yes, then probably we need to use pg_collation there too because
lc_collate and the new collate clause use different collation names.
"""
postgres=# create database test with lc_collate 'en_US.UTF-8';
CREATE DATABASE
test=# create table t1 (col1 text collate "en_US.UTF-8");
ERROR: collation "en_US.UTF-8" does not exist
test=# create table t1 (col1 text collate "en_US.utf8");
CREATE TABLE
"""
also i got errors from regression tests when MULTIBYTE=UTF8
(attached). it seems i was trying to create locales that weren't
defined on locales.txt (from were was fed that file?). i added a line
to that file (for es_EC.utf8) then i create a table with a column
using that collate and execute "select * from t2 where col1 > 'n'; "
and i got this error: "ERROR: could not create locale "es_EC.utf8""
(of course, that last part was me messing the things up, but it show
we shouldn't be using a file locales.txt, i think)
i can attach a collate to a domain but i can't see where are we
storing that info (actually it says it's not collatable):
--
Jaime Casanova www.2ndQuadrant.com
Soporte y capacitación de PostgreSQL
Attachments:
regression.diffsapplication/octet-stream; name=regression.diffsDownload
*** /media/linux_hhd/pgsql/releases/9.1/src/test/regress/expected/collate.out 2010-08-01 18:58:50.000000000 -0500
--- /media/linux_hhd/pgsql/releases/9.1/src/test/regress/results/collate.out 2010-08-01 19:23:14.000000000 -0500
***************
*** 6,14 ****
a int,
b text COLLATE "en_US"
);
! ERROR: collation "en_US" is not applicable to current database encoding "UTF8"
! LINE 3: b text COLLATE "en_US"
! ^
CREATE TABLE collate_test_fail (
a int,
b text COLLATE "foo"
--- 6,12 ----
a int,
b text COLLATE "en_US"
);
! ERROR: collation "en_US" does not exist
CREATE TABLE collate_test_fail (
a int,
b text COLLATE "foo"
***************
*** 25,32 ****
--- 23,34 ----
a int,
b text COLLATE "sv_SE.utf8"
);
+ ERROR: collation "sv_SE.utf8" does not exist
INSERT INTO collate_test1 VALUES (1, 'abc'), (2, 'äbc'), (3, 'bbc');
INSERT INTO collate_test2 SELECT * FROM collate_test1;
+ ERROR: relation "collate_test2" does not exist
+ LINE 1: INSERT INTO collate_test2 SELECT * FROM collate_test1;
+ ^
SELECT * FROM collate_test1 WHERE b >= 'bbc';
a | b
---+-----
***************
*** 34,42 ****
(1 row)
SELECT * FROM collate_test2 WHERE b >= 'bbc';
! a | b
! ---+-----
! 2 | äbc
! 3 | bbc
! (2 rows)
!
--- 36,41 ----
(1 row)
SELECT * FROM collate_test2 WHERE b >= 'bbc';
! ERROR: relation "collate_test2" does not exist
! LINE 1: SELECT * FROM collate_test2 WHERE b >= 'bbc';
! ^
======================================================================
On mån, 2010-08-02 at 01:43 -0500, Jaime Casanova wrote:
nowadays, CREATE DATABASE has a lc_collate clause. is the new collate
clause similar as the lc_collate?
i mean, is lc_collate what we will use as a default?
Yes, if you do not specify anything per column, the database default is
used.
How to integrate the per-database or per-cluster configuration with the
new system is something to figure out in the future.
if yes, then probably we need to use pg_collation there too because
lc_collate and the new collate clause use different collation names.
"""
postgres=# create database test with lc_collate 'en_US.UTF-8';
CREATE DATABASE
test=# create table t1 (col1 text collate "en_US.UTF-8");
ERROR: collation "en_US.UTF-8" does not exist
test=# create table t1 (col1 text collate "en_US.utf8");
CREATE TABLE
"""
This is something that libc does for you. The locale as listed by
locale -a is called "en_US.utf8", but apparently libc takes
"en_US.UTF-8" as well.
also i got errors from regression tests when MULTIBYTE=UTF8
(attached). it seems i was trying to create locales that weren't
defined on locales.txt (from were was fed that file?). i added a line
to that file (for es_EC.utf8) then i create a table with a column
using that collate and execute "select * from t2 where col1 > 'n'; "
and i got this error: "ERROR: could not create locale "es_EC.utf8""
(of course, that last part was me messing the things up, but it show
we shouldn't be using a file locales.txt, i think)
It might be that you don't have those locales installed in your system.
locales.txt is created by using locale -a. Check what that gives you.
i can attach a collate to a domain but i can't see where are we
storing that info (actually it says it's not collatable):
Domain support is not done yet.
Hi,
sorry for the delay...
btw, the patch no longer apply cleanly but most are just hunks the
worst it's in src/backend/catalog/namespace.c because
FindConversionByName() is now called get_conversion_oid()... so maybe
this function should be named get_collation_oid(), i guess
On Tue, Aug 3, 2010 at 11:32 AM, Peter Eisentraut <peter_e@gmx.net> wrote:
On mån, 2010-08-02 at 01:43 -0500, Jaime Casanova wrote:
nowadays, CREATE DATABASE has a lc_collate clause. is the new collate
clause similar as the lc_collate?
i mean, is lc_collate what we will use as a default?Yes, if you do not specify anything per column, the database default is
used.How to integrate the per-database or per-cluster configuration with the
new system is something to figure out in the future.
well at least pg_collation should be a shared catalog, no?
and i think we shouldn't be thinking in this without think first how
to integrate this with at least per-database configuration
if yes, then probably we need to use pg_collation there too because
lc_collate and the new collate clause use different collation names.
"""
postgres=# create database test with lc_collate 'en_US.UTF-8';
CREATE DATABASE
test=# create table t1 (col1 text collate "en_US.UTF-8");
ERROR: collation "en_US.UTF-8" does not exist
test=# create table t1 (col1 text collate "en_US.utf8");
CREATE TABLE
"""This is something that libc does for you. The locale as listed by
locale -a is called "en_US.utf8", but apparently libc takes
"en_US.UTF-8" as well.
ok, but at least this is confusing
also, it doesn't recognize C collate although it is in the locales.txt
"""
test3=# create database test4 with template=template0 encoding 'utf-8'
lc_collate='C';
CREATE DATABASE
test3=# create table t3 (col1 text collate "C" );
ERROR: collation "C" does not exist
"""
BTW, why the double quotes?
also i got errors from regression tests when MULTIBYTE=UTF8
(attached). it seems i was trying to create locales that weren't
defined on locales.txt (from were was fed that file?). i added a line
to that file (for es_EC.utf8) then i create a table with a column
using that collate and execute "select * from t2 where col1 > 'n'; "
and i got this error: "ERROR: could not create locale "es_EC.utf8""
(of course, that last part was me messing the things up, but it show
we shouldn't be using a file locales.txt, i think)It might be that you don't have those locales installed in your system.
locales.txt is created by using locale -a. Check what that gives you.
sorry to state the obvious but this doesn't work on windows, does it?
and for some reason it also didn't work on a centos 5 (this error
ocurred when initdb'ing)
"""
loading system objects' descriptions ... ok
creating collations ...FATAL: invalid byte sequence for encoding
"UTF8": 0xe56c09
CONTEXT: COPY tmp_pg_collation, line 86
STATEMENT: COPY tmp_pg_collation FROM
E'/usr/local/pgsql/9.1/share/locales.txt';
"""
--
Jaime Casanova www.2ndQuadrant.com
Soporte y capacitación de PostgreSQL
On lör, 2010-08-14 at 02:05 -0500, Jaime Casanova wrote:
btw, the patch no longer apply cleanly but most are just hunks the
worst it's in src/backend/catalog/namespace.c because
FindConversionByName() is now called get_conversion_oid()... so maybe
this function should be named get_collation_oid(), i guess
OK, that will need to be adjusted.
well at least pg_collation should be a shared catalog, no?
and i think we shouldn't be thinking in this without think first how
to integrate this with at least per-database configuration
Good point. But one might also want to create "private" collations, so
a collation in a schema would also be useful. Tricky.
also, it doesn't recognize C collate although it is in the locales.txt
"""
test3=# create database test4 with template=template0 encoding 'utf-8'
lc_collate='C';
CREATE DATABASE
test3=# create table t3 (col1 text collate "C" );
ERROR: collation "C" does not exist
"""
I've fixed this in the meantime. Your version of the patch doesn't
support the C locale yet.
BTW, why the double quotes?
Because the name contains upper case letters?
sorry to state the obvious but this doesn't work on windows, does it?
Probably not, but hopefully there is some similar API that could be used
on Windows.
and for some reason it also didn't work on a centos 5 (this error
ocurred when initdb'ing)
"""
loading system objects' descriptions ... ok
creating collations ...FATAL: invalid byte sequence for encoding
"UTF8": 0xe56c09
CONTEXT: COPY tmp_pg_collation, line 86
STATEMENT: COPY tmp_pg_collation FROM
E'/usr/local/pgsql/9.1/share/locales.txt';
"""
Hmm, what is in that file on that line?
On Mon, Aug 16, 2010 at 10:56 PM, Peter Eisentraut <peter_e@gmx.net> wrote:
On lör, 2010-08-14 at 02:05 -0500, Jaime Casanova wrote:
BTW, why the double quotes?
Because the name contains upper case letters?
why everything seems so obvious once someone else state it? :)
sorry to state the obvious but this doesn't work on windows, does it?
Probably not, but hopefully there is some similar API that could be used
on Windows.
good luck with that! ;)
seriously, maybe this helps
http://msdn.microsoft.com/en-us/library/system.windows.forms.inputlanguage.installedinputlanguages.aspx
but probably you will need to write the code yourself... at least i
don't think there is something like "locale -a"
and for some reason it also didn't work on a centos 5 (this error
ocurred when initdb'ing)
"""
loading system objects' descriptions ... ok
creating collations ...FATAL: invalid byte sequence for encoding
"UTF8": 0xe56c09
CONTEXT: COPY tmp_pg_collation, line 86
STATEMENT: COPY tmp_pg_collation FROM
E'/usr/local/pgsql/9.1/share/locales.txt';
"""Hmm, what is in that file on that line?
bokmål ISO-8859-1
(i'm attaching the locales.txt just in case)
--
Jaime Casanova www.2ndQuadrant.com
Soporte y capacitación de PostgreSQL
Attachments:
On tis, 2010-08-17 at 01:16 -0500, Jaime Casanova wrote:
creating collations ...FATAL: invalid byte sequence for encoding
"UTF8": 0xe56c09
CONTEXT: COPY tmp_pg_collation, line 86
STATEMENT: COPY tmp_pg_collation FROM
E'/usr/local/pgsql/9.1/share/locales.txt';
"""Hmm, what is in that file on that line?
bokmål ISO-8859-1
Hey, that borders on genius: Use a non-ASCII letter in the name of a
locale whose purpose it is to configure how non-ASCII letters are
interpreted. :-/
Interestingly, I don't see this on a Debian system. Good thing to know
that this needs separate testing on different Linux variants.
On Wed, Aug 18, 2010 at 11:29 AM, Peter Eisentraut <peter_e@gmx.net> wrote:
On tis, 2010-08-17 at 01:16 -0500, Jaime Casanova wrote:
creating collations ...FATAL: invalid byte sequence for encoding
"UTF8": 0xe56c09
CONTEXT: COPY tmp_pg_collation, line 86
STATEMENT: COPY tmp_pg_collation FROM
E'/usr/local/pgsql/9.1/share/locales.txt';
"""Hmm, what is in that file on that line?
bokmål ISO-8859-1
Hey, that borders on genius: Use a non-ASCII letter in the name of a
locale whose purpose it is to configure how non-ASCII letters are
interpreted. :-/Interestingly, I don't see this on a Debian system. Good thing to know
that this needs separate testing on different Linux variants.
Yeah! and when installing centos 5 i don't have a chance to choose
what locales i want, it just installs all of them
--
Jaime Casanova www.2ndQuadrant.com
Soporte y capacitación de PostgreSQL