#include "access/htup_details.h"
#include "access/stratnum.h"
#include "catalog/pg_collation.h"
+#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
#include "catalog/pg_statistic.h"
#include "catalog/pg_type.h"
Pattern_Prefix_Status pstatus;
Oid ldatatype;
Oid rdatatype;
- Oid oproid;
+ Oid eqopr;
+ Oid ltopr;
+ Oid geopr;
+ bool collation_aware;
Expr *expr;
FmgrInfo ltproc;
Const *greaterstr;
return NIL;
/*
- * Must also check that index's opfamily supports the operators we will
- * want to apply. (A hash index, for example, will not support ">=".)
- * Currently, only btree and spgist support the operators we need.
- *
- * Note: actually, in the Pattern_Prefix_Exact case, we only need "=" so a
- * hash index would work. Currently it doesn't seem worth checking for
- * that, however.
- *
- * We insist on the opfamily being one of the specific ones we expect,
- * else we'd do the wrong thing if someone were to make a reverse-sort
- * opfamily with the same operators.
- *
- * The non-pattern opclasses will not sort the way we need in most non-C
- * locales. We can use such an index anyway for an exact match (simple
- * equality), but not for prefix-match cases. Note that here we are
- * looking at the index's collation, not the expression's collation --
- * this test is *not* dependent on the LIKE/regex operator's collation.
- *
- * While we're at it, identify the type the comparison constant(s) should
- * have, based on the opfamily.
+ * Identify the operators we want to use, based on the type of the
+ * left-hand argument. Usually these are just the type's regular
+ * comparison operators, but if we are considering one of the semi-legacy
+ * "pattern" opclasses, use the "pattern" operators instead. Those are
+ * not collation-sensitive but always use C collation, as we want. The
+ * selected operators also determine the needed type of the prefix
+ * constant.
*/
- switch (opfamily)
+ ldatatype = exprType(leftop);
+ switch (ldatatype)
{
- case TEXT_BTREE_FAM_OID:
- if (!(pstatus == Pattern_Prefix_Exact ||
- lc_collate_is_c(indexcollation)))
- return NIL;
+ case TEXTOID:
+ if (opfamily == TEXT_PATTERN_BTREE_FAM_OID ||
+ opfamily == TEXT_SPGIST_FAM_OID)
+ {
+ eqopr = TextEqualOperator;
+ ltopr = TextPatternLessOperator;
+ geopr = TextPatternGreaterEqualOperator;
+ collation_aware = false;
+ }
+ else
+ {
+ eqopr = TextEqualOperator;
+ ltopr = TextLessOperator;
+ geopr = TextGreaterEqualOperator;
+ collation_aware = true;
+ }
rdatatype = TEXTOID;
break;
+ case NAMEOID:
- case TEXT_PATTERN_BTREE_FAM_OID:
- case TEXT_SPGIST_FAM_OID:
+ /*
+ * Note that here, we need the RHS type to be text, so that the
+ * comparison value isn't improperly truncated to NAMEDATALEN.
+ */
+ eqopr = NameEqualTextOperator;
+ ltopr = NameLessTextOperator;
+ geopr = NameGreaterEqualTextOperator;
+ collation_aware = true;
rdatatype = TEXTOID;
break;
-
- case BPCHAR_BTREE_FAM_OID:
- if (!(pstatus == Pattern_Prefix_Exact ||
- lc_collate_is_c(indexcollation)))
- return NIL;
- rdatatype = BPCHAROID;
- break;
-
- case BPCHAR_PATTERN_BTREE_FAM_OID:
+ case BPCHAROID:
+ if (opfamily == BPCHAR_PATTERN_BTREE_FAM_OID)
+ {
+ eqopr = BpcharEqualOperator;
+ ltopr = BpcharPatternLessOperator;
+ geopr = BpcharPatternGreaterEqualOperator;
+ collation_aware = false;
+ }
+ else
+ {
+ eqopr = BpcharEqualOperator;
+ ltopr = BpcharLessOperator;
+ geopr = BpcharGreaterEqualOperator;
+ collation_aware = true;
+ }
rdatatype = BPCHAROID;
break;
-
- case BYTEA_BTREE_FAM_OID:
+ case BYTEAOID:
+ eqopr = ByteaEqualOperator;
+ ltopr = ByteaLessOperator;
+ geopr = ByteaGreaterEqualOperator;
+ collation_aware = false;
rdatatype = BYTEAOID;
break;
-
default:
+ /* Can't get here unless we're attached to the wrong operator */
return NIL;
}
- /* OK, prepare to create the indexqual(s) */
- ldatatype = exprType(leftop);
+ /*
+ * If necessary, verify that the index's collation behavior is compatible.
+ * For an exact-match case, we don't have to be picky. Otherwise, insist
+ * that the index collation be "C". Note that here we are looking at the
+ * index's collation, not the expression's collation -- this test is *not*
+ * dependent on the LIKE/regex operator's collation.
+ */
+ if (collation_aware)
+ {
+ if (!(pstatus == Pattern_Prefix_Exact ||
+ lc_collate_is_c(indexcollation)))
+ return NIL;
+ }
/*
* If necessary, coerce the prefix constant to the right type. The given
/*
* If we found an exact-match pattern, generate an "=" indexqual.
*
- * (Despite the checks above, we might fail to find a suitable operator in
- * some cases with binary-compatible opclasses. Just punt if so.)
+ * Here and below, check to see whether the desired operator is actually
+ * supported by the index opclass, and fail quietly if not. This allows
+ * us to not be concerned with specific opclasses (except for the legacy
+ * "pattern" cases); any index that correctly implements the operators
+ * will work.
*/
if (pstatus == Pattern_Prefix_Exact)
{
- oproid = get_opfamily_member(opfamily, ldatatype, rdatatype,
- BTEqualStrategyNumber);
- if (oproid == InvalidOid)
+ if (!op_in_opfamily(eqopr, opfamily))
return NIL;
- expr = make_opclause(oproid, BOOLOID, false,
+ expr = make_opclause(eqopr, BOOLOID, false,
(Expr *) leftop, (Expr *) prefix,
InvalidOid, indexcollation);
result = list_make1(expr);
*
* We can always say "x >= prefix".
*/
- oproid = get_opfamily_member(opfamily, ldatatype, rdatatype,
- BTGreaterEqualStrategyNumber);
- if (oproid == InvalidOid)
+ if (!op_in_opfamily(geopr, opfamily))
return NIL;
- expr = make_opclause(oproid, BOOLOID, false,
+ expr = make_opclause(geopr, BOOLOID, false,
(Expr *) leftop, (Expr *) prefix,
InvalidOid, indexcollation);
result = list_make1(expr);
* using a C-locale index collation.
*-------
*/
- oproid = get_opfamily_member(opfamily, ldatatype, rdatatype,
- BTLessStrategyNumber);
- if (oproid == InvalidOid)
+ if (!op_in_opfamily(ltopr, opfamily))
return result;
- fmgr_info(get_opcode(oproid), <proc);
+ fmgr_info(get_opcode(ltopr), <proc);
greaterstr = make_greater_string(prefix, <proc, indexcollation);
if (greaterstr)
{
- expr = make_opclause(oproid, BOOLOID, false,
+ expr = make_opclause(ltopr, BOOLOID, false,
(Expr *) leftop, (Expr *) greaterstr,
InvalidOid, indexcollation);
result = lappend(result, expr);
oprcode => 'starts_with', oprrest => 'prefixsel',
oprjoin => 'prefixjoinsel' },
-{ oid => '254', descr => 'equal',
+{ oid => '254', oid_symbol => 'NameEqualTextOperator', descr => 'equal',
oprname => '=', oprcanmerge => 't', oprcanhash => 't', oprleft => 'name',
oprright => 'text', oprresult => 'bool', oprcom => '=(text,name)',
oprnegate => '<>(name,text)', oprcode => 'nameeqtext', oprrest => 'eqsel',
oprjoin => 'eqjoinsel' },
-{ oid => '255', descr => 'less than',
+{ oid => '255', oid_symbol => 'NameLessTextOperator', descr => 'less than',
oprname => '<', oprleft => 'name', oprright => 'text', oprresult => 'bool',
oprcom => '>(text,name)', oprnegate => '>=(name,text)',
oprcode => 'namelttext', oprrest => 'scalarltsel',
oprcom => '>=(text,name)', oprnegate => '>(name,text)',
oprcode => 'nameletext', oprrest => 'scalarlesel',
oprjoin => 'scalarlejoinsel' },
-{ oid => '257', descr => 'greater than or equal',
+{ oid => '257', oid_symbol => 'NameGreaterEqualTextOperator',
+ descr => 'greater than or equal',
oprname => '>=', oprleft => 'name', oprright => 'text', oprresult => 'bool',
oprcom => '<=(text,name)', oprnegate => '<(name,text)',
oprcode => 'namegetext', oprrest => 'scalargesel',
oprname => '>=', oprleft => 'name', oprright => 'name', oprresult => 'bool',
oprcom => '<=(name,name)', oprnegate => '<(name,name)', oprcode => 'namege',
oprrest => 'scalargesel', oprjoin => 'scalargejoinsel' },
-{ oid => '664', descr => 'less than',
+{ oid => '664', oid_symbol => 'TextLessOperator', descr => 'less than',
oprname => '<', oprleft => 'text', oprright => 'text', oprresult => 'bool',
oprcom => '>(text,text)', oprnegate => '>=(text,text)', oprcode => 'text_lt',
oprrest => 'scalarltsel', oprjoin => 'scalarltjoinsel' },
oprname => '>', oprleft => 'text', oprright => 'text', oprresult => 'bool',
oprcom => '<(text,text)', oprnegate => '<=(text,text)', oprcode => 'text_gt',
oprrest => 'scalargtsel', oprjoin => 'scalargtjoinsel' },
-{ oid => '667', descr => 'greater than or equal',
+{ oid => '667', oid_symbol => 'TextGreaterEqualOperator',
+ descr => 'greater than or equal',
oprname => '>=', oprleft => 'text', oprright => 'text', oprresult => 'bool',
oprcom => '<=(text,text)', oprnegate => '<(text,text)', oprcode => 'text_ge',
oprrest => 'scalargesel', oprjoin => 'scalargejoinsel' },
oprname => '@@', oprkind => 'l', oprleft => '0', oprright => 'polygon',
oprresult => 'point', oprcode => 'poly_center' },
-{ oid => '1054', descr => 'equal',
+{ oid => '1054', oid_symbol => 'BpcharEqualOperator', descr => 'equal',
oprname => '=', oprcanmerge => 't', oprcanhash => 't', oprleft => 'bpchar',
oprright => 'bpchar', oprresult => 'bool', oprcom => '=(bpchar,bpchar)',
oprnegate => '<>(bpchar,bpchar)', oprcode => 'bpchareq', oprrest => 'eqsel',
oprresult => 'bool', oprcom => '<>(bpchar,bpchar)',
oprnegate => '=(bpchar,bpchar)', oprcode => 'bpcharne', oprrest => 'neqsel',
oprjoin => 'neqjoinsel' },
-{ oid => '1058', descr => 'less than',
+{ oid => '1058', oid_symbol => 'BpcharLessOperator', descr => 'less than',
oprname => '<', oprleft => 'bpchar', oprright => 'bpchar',
oprresult => 'bool', oprcom => '>(bpchar,bpchar)',
oprnegate => '>=(bpchar,bpchar)', oprcode => 'bpcharlt',
oprresult => 'bool', oprcom => '<(bpchar,bpchar)',
oprnegate => '<=(bpchar,bpchar)', oprcode => 'bpchargt',
oprrest => 'scalargtsel', oprjoin => 'scalargtjoinsel' },
-{ oid => '1061', descr => 'greater than or equal',
+{ oid => '1061', oid_symbol => 'BpcharGreaterEqualOperator',
+ descr => 'greater than or equal',
oprname => '>=', oprleft => 'bpchar', oprright => 'bpchar',
oprresult => 'bool', oprcom => '<=(bpchar,bpchar)',
oprnegate => '<(bpchar,bpchar)', oprcode => 'bpcharge',
oprresult => 'numeric', oprcode => 'numeric_uplus' },
# bytea operators
-{ oid => '1955', descr => 'equal',
+{ oid => '1955', oid_symbol => 'ByteaEqualOperator', descr => 'equal',
oprname => '=', oprcanmerge => 't', oprcanhash => 't', oprleft => 'bytea',
oprright => 'bytea', oprresult => 'bool', oprcom => '=(bytea,bytea)',
oprnegate => '<>(bytea,bytea)', oprcode => 'byteaeq', oprrest => 'eqsel',
oprname => '<>', oprleft => 'bytea', oprright => 'bytea', oprresult => 'bool',
oprcom => '<>(bytea,bytea)', oprnegate => '=(bytea,bytea)',
oprcode => 'byteane', oprrest => 'neqsel', oprjoin => 'neqjoinsel' },
-{ oid => '1957', descr => 'less than',
+{ oid => '1957', oid_symbol => 'ByteaLessOperator', descr => 'less than',
oprname => '<', oprleft => 'bytea', oprright => 'bytea', oprresult => 'bool',
oprcom => '>(bytea,bytea)', oprnegate => '>=(bytea,bytea)',
oprcode => 'bytealt', oprrest => 'scalarltsel',
oprcom => '<(bytea,bytea)', oprnegate => '<=(bytea,bytea)',
oprcode => 'byteagt', oprrest => 'scalargtsel',
oprjoin => 'scalargtjoinsel' },
-{ oid => '1960', descr => 'greater than or equal',
+{ oid => '1960', oid_symbol => 'ByteaGreaterEqualOperator',
+ descr => 'greater than or equal',
oprname => '>=', oprleft => 'bytea', oprright => 'bytea', oprresult => 'bool',
oprcom => '<=(bytea,bytea)', oprnegate => '<(bytea,bytea)',
oprcode => 'byteage', oprrest => 'scalargesel',
oprresult => 'timestamp', oprcode => 'timestamp_mi_interval' },
# character-by-character (not collation order) comparison operators for character types
-{ oid => '2314', descr => 'less than',
+{ oid => '2314', oid_symbol => 'TextPatternLessOperator',
+ descr => 'less than',
oprname => '~<~', oprleft => 'text', oprright => 'text', oprresult => 'bool',
oprcom => '~>~(text,text)', oprnegate => '~>=~(text,text)',
oprcode => 'text_pattern_lt', oprrest => 'scalarltsel',
oprcom => '~>=~(text,text)', oprnegate => '~>~(text,text)',
oprcode => 'text_pattern_le', oprrest => 'scalarlesel',
oprjoin => 'scalarlejoinsel' },
-{ oid => '2317', descr => 'greater than or equal',
+{ oid => '2317', oid_symbol => 'TextPatternGreaterEqualOperator',
+ descr => 'greater than or equal',
oprname => '~>=~', oprleft => 'text', oprright => 'text', oprresult => 'bool',
oprcom => '~<=~(text,text)', oprnegate => '~<~(text,text)',
oprcode => 'text_pattern_ge', oprrest => 'scalargesel',
oprcode => 'text_pattern_gt', oprrest => 'scalargtsel',
oprjoin => 'scalargtjoinsel' },
-{ oid => '2326', descr => 'less than',
+{ oid => '2326', oid_symbol => 'BpcharPatternLessOperator',
+ descr => 'less than',
oprname => '~<~', oprleft => 'bpchar', oprright => 'bpchar',
oprresult => 'bool', oprcom => '~>~(bpchar,bpchar)',
oprnegate => '~>=~(bpchar,bpchar)', oprcode => 'bpchar_pattern_lt',
oprresult => 'bool', oprcom => '~>=~(bpchar,bpchar)',
oprnegate => '~>~(bpchar,bpchar)', oprcode => 'bpchar_pattern_le',
oprrest => 'scalarlesel', oprjoin => 'scalarlejoinsel' },
-{ oid => '2329', descr => 'greater than or equal',
+{ oid => '2329', oid_symbol => 'BpcharPatternGreaterEqualOperator',
+ descr => 'greater than or equal',
oprname => '~>=~', oprleft => 'bpchar', oprright => 'bpchar',
oprresult => 'bool', oprcom => '~<=~(bpchar,bpchar)',
oprnegate => '~<~(bpchar,bpchar)', oprcode => 'bpchar_pattern_ge',