<entry><para>
<structfield>typcollation</structfield> specifies the collation
of the type. If the type does not support collations, this will
- be zero. A base type that supports collations will have
- <symbol>DEFAULT_COLLATION_OID</symbol> here. A domain over a
- collatable type can have some other collation OID, if one was
- specified for the domain.
+ be zero. A base type that supports collations will have a nonzero
+ value here, typically <symbol>DEFAULT_COLLATION_OID</symbol>.
+ A domain over a collatable type can have a collation OID different
+ from its base type's, if one was specified for the domain.
</para></entry>
</row>
/* Be careful to compare chars as unsigned */
PG_RETURN_INT32((int32) ((uint8) a) - (int32) ((uint8) b));
}
-
-Datum
-btnamecmp(PG_FUNCTION_ARGS)
-{
- Name a = PG_GETARG_NAME(0);
- Name b = PG_GETARG_NAME(1);
-
- PG_RETURN_INT32(strncmp(NameStr(*a), NameStr(*b), NAMEDATALEN));
-}
-
-static int
-btnamefastcmp(Datum x, Datum y, SortSupport ssup)
-{
- Name a = DatumGetName(x);
- Name b = DatumGetName(y);
-
- return strncmp(NameStr(*a), NameStr(*b), NAMEDATALEN);
-}
-
-Datum
-btnamesortsupport(PG_FUNCTION_ARGS)
-{
- SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
-
- ssup->comparator = btnamefastcmp;
- PG_RETURN_VOID();
-}
F_INT4IN, F_INT4OUT},
{"float4", FLOAT4OID, 0, 4, FLOAT4PASSBYVAL, 'i', 'p', InvalidOid,
F_FLOAT4IN, F_FLOAT4OUT},
- {"name", NAMEOID, CHAROID, NAMEDATALEN, false, 'c', 'p', InvalidOid,
+ {"name", NAMEOID, CHAROID, NAMEDATALEN, false, 'c', 'p', C_COLLATION_OID,
F_NAMEIN, F_NAMEOUT},
{"regclass", REGCLASSOID, 0, 4, true, 'i', 'p', InvalidOid,
F_REGCLASSIN, F_REGCLASSOUT},
#include "postgres.h"
#include "access/xact.h"
-#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
#include "commands/createas.h"
#include "commands/defrem.h"
TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);
/*
- * Print COLLATE if it's not default. There are some cases where this is
- * redundant, eg if expression is a column whose declared collation is
- * that collation, but it's hard to distinguish that here.
+ * Print COLLATE if it's not default for the column's type. There are
+ * some cases where this is redundant, eg if expression is a column whose
+ * declared collation is that collation, but it's hard to distinguish that
+ * here (and arguably, printing COLLATE explicitly is a good idea anyway
+ * in such cases).
*/
- if (OidIsValid(collation) && collation != DEFAULT_COLLATION_OID)
+ if (OidIsValid(collation) && collation != get_typcollation(sortcoltype))
{
char *collname = get_collation_name(collation);
coll = ((const MinMaxExpr *) expr)->minmaxcollid;
break;
case T_SQLValueFunction:
- coll = InvalidOid; /* all cases return non-collatable types */
+ /* Returns either NAME or a non-collatable type */
+ if (((const SQLValueFunction *) expr)->type == NAMEOID)
+ coll = C_COLLATION_OID;
+ else
+ coll = InvalidOid;
break;
case T_XmlExpr:
((MinMaxExpr *) expr)->minmaxcollid = collation;
break;
case T_SQLValueFunction:
- Assert(!OidIsValid(collation)); /* no collatable results */
+ Assert((((SQLValueFunction *) expr)->type == NAMEOID) ?
+ (collation == C_COLLATION_OID) :
+ (collation == InvalidOid));
break;
case T_XmlExpr:
Assert((((XmlExpr *) expr)->op == IS_XMLSERIALIZE) ?
break;
case NAMEOID:
- collation = InvalidOid;
+ collation = C_COLLATION_OID;
constlen = NAMEDATALEN;
break;
tf->coltypes = lappend_oid(tf->coltypes, typid);
tf->coltypmods = lappend_int(tf->coltypmods, typmod);
tf->colcollations = lappend_oid(tf->colcollations,
- type_is_collatable(typid) ? DEFAULT_COLLATION_OID : InvalidOid);
+ get_typcollation(typid));
/* Transform the PATH and DEFAULT expressions */
if (rawc->colexpr)
#include "postgres.h"
#include "catalog/namespace.h"
+#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
#include "libpq/pqformat.h"
#include "mb/pg_wchar.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
+#include "utils/varlena.h"
/*****************************************************************************
/*****************************************************************************
- * PUBLIC ROUTINES *
+ * COMPARISON/SORTING ROUTINES *
*****************************************************************************/
/*
* nameeq - returns 1 iff arguments are equal
* namene - returns 1 iff arguments are not equal
- *
- * BUGS:
- * Assumes that "xy\0\0a" should be equal to "xy\0b".
- * If not, can do the comparison backwards for efficiency.
- *
* namelt - returns 1 iff a < b
* namele - returns 1 iff a <= b
* namegt - returns 1 iff a > b
* namege - returns 1 iff a >= b
*
+ * Note that the use of strncmp with NAMEDATALEN limit is mostly historical;
+ * strcmp would do as well, because we do not allow NAME values that don't
+ * have a '\0' terminator. Whatever might be past the terminator is not
+ * considered relevant to comparisons.
*/
Datum
nameeq(PG_FUNCTION_ARGS)
Name arg1 = PG_GETARG_NAME(0);
Name arg2 = PG_GETARG_NAME(1);
+ /* Collation doesn't matter: equal only if bitwise-equal */
PG_RETURN_BOOL(strncmp(NameStr(*arg1), NameStr(*arg2), NAMEDATALEN) == 0);
}
Name arg1 = PG_GETARG_NAME(0);
Name arg2 = PG_GETARG_NAME(1);
+ /* Collation doesn't matter: equal only if bitwise-equal */
PG_RETURN_BOOL(strncmp(NameStr(*arg1), NameStr(*arg2), NAMEDATALEN) != 0);
}
+static int
+namecmp(Name arg1, Name arg2, Oid collid)
+{
+ /* Fast path for common case used in system catalogs */
+ if (collid == C_COLLATION_OID)
+ return strncmp(NameStr(*arg1), NameStr(*arg2), NAMEDATALEN);
+
+ /* Else rely on the varstr infrastructure */
+ return varstr_cmp(NameStr(*arg1), strlen(NameStr(*arg1)),
+ NameStr(*arg2), strlen(NameStr(*arg2)),
+ collid);
+}
+
Datum
namelt(PG_FUNCTION_ARGS)
{
Name arg1 = PG_GETARG_NAME(0);
Name arg2 = PG_GETARG_NAME(1);
- PG_RETURN_BOOL(strncmp(NameStr(*arg1), NameStr(*arg2), NAMEDATALEN) < 0);
+ PG_RETURN_BOOL(namecmp(arg1, arg2, PG_GET_COLLATION()) < 0);
}
Datum
Name arg1 = PG_GETARG_NAME(0);
Name arg2 = PG_GETARG_NAME(1);
- PG_RETURN_BOOL(strncmp(NameStr(*arg1), NameStr(*arg2), NAMEDATALEN) <= 0);
+ PG_RETURN_BOOL(namecmp(arg1, arg2, PG_GET_COLLATION()) <= 0);
}
Datum
Name arg1 = PG_GETARG_NAME(0);
Name arg2 = PG_GETARG_NAME(1);
- PG_RETURN_BOOL(strncmp(NameStr(*arg1), NameStr(*arg2), NAMEDATALEN) > 0);
+ PG_RETURN_BOOL(namecmp(arg1, arg2, PG_GET_COLLATION()) > 0);
}
Datum
Name arg1 = PG_GETARG_NAME(0);
Name arg2 = PG_GETARG_NAME(1);
- PG_RETURN_BOOL(strncmp(NameStr(*arg1), NameStr(*arg2), NAMEDATALEN) >= 0);
+ PG_RETURN_BOOL(namecmp(arg1, arg2, PG_GET_COLLATION()) >= 0);
+}
+
+Datum
+btnamecmp(PG_FUNCTION_ARGS)
+{
+ Name arg1 = PG_GETARG_NAME(0);
+ Name arg2 = PG_GETARG_NAME(1);
+
+ PG_RETURN_INT32(namecmp(arg1, arg2, PG_GET_COLLATION()));
}
+Datum
+btnamesortsupport(PG_FUNCTION_ARGS)
+{
+ SortSupport ssup = (SortSupport) PG_GETARG_POINTER(0);
+ Oid collid = ssup->ssup_collation;
+ MemoryContext oldcontext;
+
+ oldcontext = MemoryContextSwitchTo(ssup->ssup_cxt);
+
+ /* Use generic string SortSupport */
+ varstr_sortsupport(ssup, NAMEOID, collid);
-/* (see char.c for comparison/operation routines) */
+ MemoryContextSwitchTo(oldcontext);
+
+ PG_RETURN_VOID();
+}
+
+
+/*****************************************************************************
+ * MISCELLANEOUS PUBLIC ROUTINES *
+ *****************************************************************************/
int
namecpy(Name n1, const NameData *n2)
}
#endif
-#ifdef NOT_USED
-int
-namecmp(Name n1, Name n2)
-{
- return strncmp(NameStr(*n1), NameStr(*n2), NAMEDATALEN);
-}
-#endif
-
int
namestrcpy(Name name, const char *str)
{
}
#endif
+/*
+ * Compare a NAME to a C string
+ *
+ * Assumes C collation always; be careful when using this for
+ * anything but equality checks!
+ */
int
namestrcmp(Name name, const char *str)
{
char *workstr;
int len;
Datum cmpstr;
- text *cmptxt = NULL;
+ char *cmptxt = NULL;
mbcharacter_incrementer charinc;
/*
* Get a modifiable copy of the prefix string in C-string format, and set
* up the string we will compare to as a Datum. In C locale this can just
- * be the given prefix string, otherwise we need to add a suffix. Types
- * NAME and BYTEA sort bytewise so they don't need a suffix either.
+ * be the given prefix string, otherwise we need to add a suffix. Type
+ * BYTEA sorts bytewise so it never needs a suffix either.
*/
- if (datatype == NAMEOID)
- {
- workstr = DatumGetCString(DirectFunctionCall1(nameout,
- str_const->constvalue));
- len = strlen(workstr);
- cmpstr = str_const->constvalue;
- }
- else if (datatype == BYTEAOID)
+ if (datatype == BYTEAOID)
{
bytea *bstr = DatumGetByteaPP(str_const->constvalue);
}
else
{
- workstr = TextDatumGetCString(str_const->constvalue);
+ if (datatype == NAMEOID)
+ workstr = DatumGetCString(DirectFunctionCall1(nameout,
+ str_const->constvalue));
+ else
+ workstr = TextDatumGetCString(str_const->constvalue);
len = strlen(workstr);
if (lc_collate_is_c(collation) || len == 0)
cmpstr = str_const->constvalue;
}
/* And build the string to compare to */
- cmptxt = (text *) palloc(VARHDRSZ + len + 1);
- SET_VARSIZE(cmptxt, VARHDRSZ + len + 1);
- memcpy(VARDATA(cmptxt), workstr, len);
- *(VARDATA(cmptxt) + len) = suffixchar;
- cmpstr = PointerGetDatum(cmptxt);
+ if (datatype == NAMEOID)
+ {
+ cmptxt = palloc(len + 2);
+ memcpy(cmptxt, workstr, len);
+ cmptxt[len] = suffixchar;
+ cmptxt[len + 1] = '\0';
+ cmpstr = PointerGetDatum(cmptxt);
+ }
+ else
+ {
+ cmptxt = palloc(VARHDRSZ + len + 1);
+ SET_VARSIZE(cmptxt, VARHDRSZ + len + 1);
+ memcpy(VARDATA(cmptxt), workstr, len);
+ *(VARDATA(cmptxt) + len) = suffixchar;
+ cmpstr = PointerGetDatum(cmptxt);
+ }
}
}
break;
case NAMEOID:
- collation = InvalidOid;
+ collation = C_COLLATION_OID;
constlen = NAMEDATALEN;
break;
#include "access/hash.h"
#include "access/tuptoaster.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_type.h"
#include "libpq/pqformat.h"
#include "nodes/nodeFuncs.h"
#include "utils/array.h"
oldcontext = MemoryContextSwitchTo(ssup->ssup_cxt);
/* Use generic string SortSupport */
- varstr_sortsupport(ssup, collid, true);
+ varstr_sortsupport(ssup, BPCHAROID, collid);
MemoryContextSwitchTo(oldcontext);
oldcontext = MemoryContextSwitchTo(ssup->ssup_cxt);
/* Use generic string SortSupport, forcing "C" collation */
- varstr_sortsupport(ssup, C_COLLATION_OID, true);
+ varstr_sortsupport(ssup, BPCHAROID, C_COLLATION_OID);
MemoryContextSwitchTo(oldcontext);
int last_returned; /* Last comparison result (cache) */
bool cache_blob; /* Does buf2 contain strxfrm() blob, etc? */
bool collate_c;
- bool bpchar; /* Sorting bpchar, not varchar/text/bytea? */
+ Oid typeid; /* Actual datatype (text/bpchar/bytea/name) */
hyperLogLogState abbr_card; /* Abbreviated key cardinality state */
hyperLogLogState full_card; /* Full key cardinality state */
double prop_card; /* Required cardinality proportion */
static int varstrfastcmp_c(Datum x, Datum y, SortSupport ssup);
static int bpcharfastcmp_c(Datum x, Datum y, SortSupport ssup);
-static int varstrfastcmp_locale(Datum x, Datum y, SortSupport ssup);
+static int namefastcmp_c(Datum x, Datum y, SortSupport ssup);
+static int varlenafastcmp_locale(Datum x, Datum y, SortSupport ssup);
+static int namefastcmp_locale(Datum x, Datum y, SortSupport ssup);
+static int varstrfastcmp_locale(char *a1p, int len1, char *a2p, int len2, SortSupport ssup);
static int varstrcmp_abbrev(Datum x, Datum y, SortSupport ssup);
static Datum varstr_abbrev_convert(Datum original, SortSupport ssup);
static bool varstr_abbrev_abort(int memtupcount, SortSupport ssup);
oldcontext = MemoryContextSwitchTo(ssup->ssup_cxt);
/* Use generic string SortSupport */
- varstr_sortsupport(ssup, collid, false);
+ varstr_sortsupport(ssup, TEXTOID, collid);
MemoryContextSwitchTo(oldcontext);
* this will not work with any other collation, though.
*/
void
-varstr_sortsupport(SortSupport ssup, Oid collid, bool bpchar)
+varstr_sortsupport(SortSupport ssup, Oid typeid, Oid collid)
{
bool abbreviate = ssup->abbreviate;
bool collate_c = false;
* overhead of a trip through the fmgr layer for every comparison, which
* can be substantial.
*
- * Most typically, we'll set the comparator to varstrfastcmp_locale, which
- * uses strcoll() to perform comparisons and knows about the special
- * requirements of BpChar callers. However, if LC_COLLATE = C, we can
- * make things quite a bit faster with varstrfastcmp_c or bpcharfastcmp_c,
- * both of which use memcmp() rather than strcoll().
+ * Most typically, we'll set the comparator to varlenafastcmp_locale,
+ * which uses strcoll() to perform comparisons. We use that for the
+ * BpChar case too, but type NAME uses namefastcmp_locale. However, if
+ * LC_COLLATE = C, we can make things quite a bit faster with
+ * varstrfastcmp_c, bpcharfastcmp_c, or namefastcmp_c, all of which use
+ * memcmp() rather than strcoll().
*/
if (lc_collate_is_c(collid))
{
- if (!bpchar)
- ssup->comparator = varstrfastcmp_c;
- else
+ if (typeid == BPCHAROID)
ssup->comparator = bpcharfastcmp_c;
+ else if (typeid == NAMEOID)
+ {
+ ssup->comparator = namefastcmp_c;
+ /* Not supporting abbreviation with type NAME, for now */
+ abbreviate = false;
+ }
+ else
+ ssup->comparator = varstrfastcmp_c;
collate_c = true;
}
return;
#endif
- ssup->comparator = varstrfastcmp_locale;
+ /*
+ * We use varlenafastcmp_locale except for type NAME.
+ */
+ if (typeid == NAMEOID)
+ {
+ ssup->comparator = namefastcmp_locale;
+ /* Not supporting abbreviation with type NAME, for now */
+ abbreviate = false;
+ }
+ else
+ ssup->comparator = varlenafastcmp_locale;
}
/*
*/
sss->cache_blob = true;
sss->collate_c = collate_c;
- sss->bpchar = bpchar;
+ sss->typeid = typeid;
ssup->ssup_extra = sss;
/*
}
/*
- * sortsupport comparison func (for locale case)
+ * sortsupport comparison func (for NAME C locale case)
+ */
+static int
+namefastcmp_c(Datum x, Datum y, SortSupport ssup)
+{
+ Name arg1 = DatumGetName(x);
+ Name arg2 = DatumGetName(y);
+
+ return strncmp(NameStr(*arg1), NameStr(*arg2), NAMEDATALEN);
+}
+
+/*
+ * sortsupport comparison func (for locale case with all varlena types)
*/
static int
-varstrfastcmp_locale(Datum x, Datum y, SortSupport ssup)
+varlenafastcmp_locale(Datum x, Datum y, SortSupport ssup)
{
VarString *arg1 = DatumGetVarStringPP(x);
VarString *arg2 = DatumGetVarStringPP(y);
- bool arg1_match;
- VarStringSortSupport *sss = (VarStringSortSupport *) ssup->ssup_extra;
-
- /* working state */
char *a1p,
*a2p;
int len1,
len1 = VARSIZE_ANY_EXHDR(arg1);
len2 = VARSIZE_ANY_EXHDR(arg2);
+ result = varstrfastcmp_locale(a1p, len1, a2p, len2, ssup);
+
+ /* We can't afford to leak memory here. */
+ if (PointerGetDatum(arg1) != x)
+ pfree(arg1);
+ if (PointerGetDatum(arg2) != y)
+ pfree(arg2);
+
+ return result;
+}
+
+/*
+ * sortsupport comparison func (for locale case with NAME type)
+ */
+static int
+namefastcmp_locale(Datum x, Datum y, SortSupport ssup)
+{
+ Name arg1 = DatumGetName(x);
+ Name arg2 = DatumGetName(y);
+
+ return varstrfastcmp_locale(NameStr(*arg1), strlen(NameStr(*arg1)),
+ NameStr(*arg2), strlen(NameStr(*arg2)),
+ ssup);
+}
+
+/*
+ * sortsupport comparison func for locale cases
+ */
+static int
+varstrfastcmp_locale(char *a1p, int len1, char *a2p, int len2, SortSupport ssup)
+{
+ VarStringSortSupport *sss = (VarStringSortSupport *) ssup->ssup_extra;
+ int result;
+ bool arg1_match;
+
/* Fast pre-check for equality, as discussed in varstr_cmp() */
if (len1 == len2 && memcmp(a1p, a2p, len1) == 0)
{
* (not limited to padding), so we need make no distinction between
* padding space characters and "real" space characters.
*/
- result = 0;
- goto done;
+ return 0;
}
- if (sss->bpchar)
+ if (sss->typeid == BPCHAROID)
{
/* Get true number of bytes, ignoring trailing spaces */
len1 = bpchartruelen(a1p, len1);
else if (arg1_match && !sss->cache_blob)
{
/* Use result cached following last actual strcoll() call */
- result = sss->last_returned;
- goto done;
+ return sss->last_returned;
}
if (sss->locale)
/* Cache result, perhaps saving an expensive strcoll() call next time */
sss->cache_blob = false;
sss->last_returned = result;
-done:
- /* We can't afford to leak memory here. */
- if (PointerGetDatum(arg1) != x)
- pfree(arg1);
- if (PointerGetDatum(arg2) != y)
- pfree(arg2);
-
return result;
}
{
/*
* When 0 is returned, the core system will call varstrfastcmp_c()
- * (bpcharfastcmp_c() in BpChar case) or varstrfastcmp_locale(). Even a
+ * (bpcharfastcmp_c() in BpChar case) or varlenafastcmp_locale(). Even a
* strcmp() on two non-truncated strxfrm() blobs cannot indicate *equality*
* authoritatively, for the same reason that there is a strcoll()
* tie-breaker call to strcmp() in varstr_cmp().
len = VARSIZE_ANY_EXHDR(authoritative);
/* Get number of bytes, ignoring trailing spaces */
- if (sss->bpchar)
+ if (sss->typeid == BPCHAROID)
len = bpchartruelen(authoritative_data, len);
/*
oldcontext = MemoryContextSwitchTo(ssup->ssup_cxt);
/* Use generic string SortSupport, forcing "C" collation */
- varstr_sortsupport(ssup, C_COLLATION_OID, false);
+ varstr_sortsupport(ssup, TEXTOID, C_COLLATION_OID);
MemoryContextSwitchTo(oldcontext);
oldcontext = MemoryContextSwitchTo(ssup->ssup_cxt);
/* Use generic string SortSupport, forcing "C" collation */
- varstr_sortsupport(ssup, C_COLLATION_OID, false);
+ varstr_sortsupport(ssup, BYTEAOID, C_COLLATION_OID);
MemoryContextSwitchTo(oldcontext);
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201812182
+#define CATALOG_VERSION_NO 201812191
#endif
typname => 'name', typlen => 'NAMEDATALEN', typbyval => 'f',
typcategory => 'S', typelem => 'char', typinput => 'namein',
typoutput => 'nameout', typreceive => 'namerecv', typsend => 'namesend',
- typalign => 'c' },
+ typalign => 'c', typcollation => '950' },
{ oid => '20', array_type_oid => '1016',
descr => '~18 digit integer, 8-byte storage',
typname => 'int8', typlen => '8', typbyval => 'FLOAT8PASSBYVAL',
int32 typndims BKI_DEFAULT(0);
/*
- * Collation: 0 if type cannot use collations, DEFAULT_COLLATION_OID for
- * collatable base types, possibly other OID for domains
+ * Collation: 0 if type cannot use collations, nonzero (typically
+ * DEFAULT_COLLATION_OID) for collatable base types, possibly some other
+ * OID for domains over collatable types
*/
Oid typcollation BKI_DEFAULT(0);
#include "utils/sortsupport.h"
extern int varstr_cmp(const char *arg1, int len1, const char *arg2, int len2, Oid collid);
-extern void varstr_sortsupport(SortSupport ssup, Oid collid, bool bpchar);
+extern void varstr_sortsupport(SortSupport ssup, Oid typeid, Oid collid);
extern int varstr_levenshtein(const char *source, int slen,
const char *target, int tlen,
int ins_c, int del_c, int sub_c,
var = plpgsql_build_variable("tg_name", 0,
plpgsql_build_datatype(NAMEOID,
-1,
- InvalidOid),
+ function->fn_input_collation),
true);
Assert(var->dtype == PLPGSQL_DTYPE_VAR);
var->dtype = PLPGSQL_DTYPE_PROMISE;
var = plpgsql_build_variable("tg_relname", 0,
plpgsql_build_datatype(NAMEOID,
-1,
- InvalidOid),
+ function->fn_input_collation),
true);
Assert(var->dtype == PLPGSQL_DTYPE_VAR);
var->dtype = PLPGSQL_DTYPE_PROMISE;
var = plpgsql_build_variable("tg_table_name", 0,
plpgsql_build_datatype(NAMEOID,
-1,
- InvalidOid),
+ function->fn_input_collation),
true);
Assert(var->dtype == PLPGSQL_DTYPE_VAR);
var->dtype = PLPGSQL_DTYPE_PROMISE;
var = plpgsql_build_variable("tg_table_schema", 0,
plpgsql_build_datatype(NAMEOID,
-1,
- InvalidOid),
+ function->fn_input_collation),
true);
Assert(var->dtype == PLPGSQL_DTYPE_VAR);
var->dtype = PLPGSQL_DTYPE_PROMISE;