8
8
*
9
9
*
10
10
* IDENTIFICATION
11
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.74 1999/12/31 03:41:03 tgl Exp $
11
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.75 1999/12/31 05:38:25 tgl Exp $
12
12
*
13
13
*-------------------------------------------------------------------------
14
14
*/
23
23
#include "catalog/pg_amop.h"
24
24
#include "catalog/pg_operator.h"
25
25
#include "executor/executor.h"
26
+ #include "mb/pg_wchar.h"
26
27
#include "nodes/makefuncs.h"
27
28
#include "nodes/nodeFuncs.h"
28
29
#include "optimizer/clauses.h"
@@ -92,7 +93,12 @@ static Prefix_Status regex_fixed_prefix(char *patt, bool case_insensitive,
92
93
char * * prefix );
93
94
static List * prefix_quals (Var * leftop , Oid expr_op ,
94
95
char * prefix , Prefix_Status pstatus );
96
+ static char * make_greater_string (const char * str , Oid datatype );
95
97
static Oid find_operator (const char * opname , Oid datatype );
98
+ static Datum string_to_datum (const char * str , Oid datatype );
99
+ static Const * string_to_const (const char * str , Oid datatype );
100
+ static bool string_lessthan (const char * str1 , const char * str2 ,
101
+ Oid datatype );
96
102
97
103
98
104
/*
@@ -1653,31 +1659,31 @@ match_special_index_operator(Expr *clause, Oid opclass, Oid relam,
1653
1659
case OID_TEXT_REGEXEQ_OP :
1654
1660
case OID_TEXT_ICREGEXEQ_OP :
1655
1661
if (! op_class (find_operator (">=" , TEXTOID ), opclass , relam ) ||
1656
- ! op_class (find_operator ("<= " , TEXTOID ), opclass , relam ))
1662
+ ! op_class (find_operator ("<" , TEXTOID ), opclass , relam ))
1657
1663
isIndexable = false;
1658
1664
break ;
1659
1665
1660
1666
case OID_BPCHAR_LIKE_OP :
1661
1667
case OID_BPCHAR_REGEXEQ_OP :
1662
1668
case OID_BPCHAR_ICREGEXEQ_OP :
1663
1669
if (! op_class (find_operator (">=" , BPCHAROID ), opclass , relam ) ||
1664
- ! op_class (find_operator ("<= " , BPCHAROID ), opclass , relam ))
1670
+ ! op_class (find_operator ("<" , BPCHAROID ), opclass , relam ))
1665
1671
isIndexable = false;
1666
1672
break ;
1667
1673
1668
1674
case OID_VARCHAR_LIKE_OP :
1669
1675
case OID_VARCHAR_REGEXEQ_OP :
1670
1676
case OID_VARCHAR_ICREGEXEQ_OP :
1671
1677
if (! op_class (find_operator (">=" , VARCHAROID ), opclass , relam ) ||
1672
- ! op_class (find_operator ("<= " , VARCHAROID ), opclass , relam ))
1678
+ ! op_class (find_operator ("<" , VARCHAROID ), opclass , relam ))
1673
1679
isIndexable = false;
1674
1680
break ;
1675
1681
1676
1682
case OID_NAME_LIKE_OP :
1677
1683
case OID_NAME_REGEXEQ_OP :
1678
1684
case OID_NAME_ICREGEXEQ_OP :
1679
1685
if (! op_class (find_operator (">=" , NAMEOID ), opclass , relam ) ||
1680
- ! op_class (find_operator ("<= " , NAMEOID ), opclass , relam ))
1686
+ ! op_class (find_operator ("<" , NAMEOID ), opclass , relam ))
1681
1687
isIndexable = false;
1682
1688
break ;
1683
1689
}
@@ -1774,7 +1780,7 @@ expand_indexqual_conditions(List *indexquals)
1774
1780
1775
1781
/*
1776
1782
* Extract the fixed prefix, if any, for a LIKE pattern.
1777
- * *prefix is set to a palloc'd prefix string with 1 spare byte ,
1783
+ * *prefix is set to a palloc'd prefix string,
1778
1784
* or to NULL if no fixed prefix exists for the pattern.
1779
1785
* The return value distinguishes no fixed prefix, a partial prefix,
1780
1786
* or an exact-match-only pattern.
@@ -1786,7 +1792,7 @@ like_fixed_prefix(char *patt, char **prefix)
1786
1792
int pos ,
1787
1793
match_pos ;
1788
1794
1789
- * prefix = match = palloc (strlen (patt )+ 2 );
1795
+ * prefix = match = palloc (strlen (patt )+ 1 );
1790
1796
match_pos = 0 ;
1791
1797
1792
1798
for (pos = 0 ; patt [pos ]; pos ++ )
@@ -1823,7 +1829,7 @@ like_fixed_prefix(char *patt, char **prefix)
1823
1829
1824
1830
/*
1825
1831
* Extract the fixed prefix, if any, for a regex pattern.
1826
- * *prefix is set to a palloc'd prefix string with 1 spare byte ,
1832
+ * *prefix is set to a palloc'd prefix string,
1827
1833
* or to NULL if no fixed prefix exists for the pattern.
1828
1834
* The return value distinguishes no fixed prefix, a partial prefix,
1829
1835
* or an exact-match-only pattern.
@@ -1858,7 +1864,7 @@ regex_fixed_prefix(char *patt, bool case_insensitive,
1858
1864
}
1859
1865
1860
1866
/* OK, allocate space for pattern */
1861
- * prefix = match = palloc (strlen (patt )+ 2 );
1867
+ * prefix = match = palloc (strlen (patt )+ 1 );
1862
1868
match_pos = 0 ;
1863
1869
1864
1870
/* note start at pos 1 to skip leading ^ */
@@ -1906,11 +1912,10 @@ prefix_quals(Var *leftop, Oid expr_op,
1906
1912
List * result ;
1907
1913
Oid datatype ;
1908
1914
Oid oproid ;
1909
- void * conval ;
1910
1915
Const * con ;
1911
1916
Oper * op ;
1912
1917
Expr * expr ;
1913
- int prefixlen ;
1918
+ char * greaterstr ;
1914
1919
1915
1920
Assert (pstatus != Prefix_None );
1916
1921
@@ -1953,14 +1958,7 @@ prefix_quals(Var *leftop, Oid expr_op,
1953
1958
oproid = find_operator ("=" , datatype );
1954
1959
if (oproid == InvalidOid )
1955
1960
elog (ERROR , "prefix_quals: no = operator for type %u" , datatype );
1956
- /* Note: we cheat a little by assuming that textin() will do for
1957
- * bpchar and varchar constants too...
1958
- */
1959
- conval = (datatype == NAMEOID ) ?
1960
- (void * ) namein (prefix ) : (void * ) textin (prefix );
1961
- con = makeConst (datatype , ((datatype == NAMEOID ) ? NAMEDATALEN : -1 ),
1962
- PointerGetDatum (conval ),
1963
- false, false, false, false);
1961
+ con = string_to_const (prefix , datatype );
1964
1962
op = makeOper (oproid , InvalidOid , BOOLOID , 0 , NULL );
1965
1963
expr = make_opclause (op , leftop , (Var * ) con );
1966
1964
result = lcons (expr , NIL );
@@ -1975,43 +1973,92 @@ prefix_quals(Var *leftop, Oid expr_op,
1975
1973
oproid = find_operator (">=" , datatype );
1976
1974
if (oproid == InvalidOid )
1977
1975
elog (ERROR , "prefix_quals: no >= operator for type %u" , datatype );
1978
- conval = (datatype == NAMEOID ) ?
1979
- (void * ) namein (prefix ) : (void * ) textin (prefix );
1980
- con = makeConst (datatype , ((datatype == NAMEOID ) ? NAMEDATALEN : -1 ),
1981
- PointerGetDatum (conval ),
1982
- false, false, false, false);
1976
+ con = string_to_const (prefix , datatype );
1983
1977
op = makeOper (oproid , InvalidOid , BOOLOID , 0 , NULL );
1984
1978
expr = make_opclause (op , leftop , (Var * ) con );
1985
1979
result = lcons (expr , NIL );
1986
1980
1987
1981
/*
1988
- * In ASCII locale we say "x <= prefix\377". This does not
1989
- * work for non-ASCII collation orders, and it's not really
1990
- * right even for ASCII. FIX ME!
1991
- * Note we assume the passed prefix string is workspace with
1992
- * an extra byte, as created by the xxx_fixed_prefix routines above.
1982
+ * If we can create a string larger than the prefix, say "x < greaterstr".
1993
1983
*/
1994
- #ifndef USE_LOCALE
1995
- prefixlen = strlen (prefix );
1996
- prefix [prefixlen ] = '\377' ;
1997
- prefix [prefixlen + 1 ] = '\0' ;
1984
+ greaterstr = make_greater_string (prefix , datatype );
1985
+ if (greaterstr )
1986
+ {
1987
+ oproid = find_operator ("<" , datatype );
1988
+ if (oproid == InvalidOid )
1989
+ elog (ERROR , "prefix_quals: no < operator for type %u" , datatype );
1990
+ con = string_to_const (greaterstr , datatype );
1991
+ op = makeOper (oproid , InvalidOid , BOOLOID , 0 , NULL );
1992
+ expr = make_opclause (op , leftop , (Var * ) con );
1993
+ result = lappend (result , expr );
1994
+ pfree (greaterstr );
1995
+ }
1998
1996
1999
- oproid = find_operator ("<=" , datatype );
2000
- if (oproid == InvalidOid )
2001
- elog (ERROR , "prefix_quals: no <= operator for type %u" , datatype );
2002
- conval = (datatype == NAMEOID ) ?
2003
- (void * ) namein (prefix ) : (void * ) textin (prefix );
2004
- con = makeConst (datatype , ((datatype == NAMEOID ) ? NAMEDATALEN : -1 ),
2005
- PointerGetDatum (conval ),
2006
- false, false, false, false);
2007
- op = makeOper (oproid , InvalidOid , BOOLOID , 0 , NULL );
2008
- expr = make_opclause (op , leftop , (Var * ) con );
2009
- result = lappend (result , expr );
1997
+ return result ;
1998
+ }
1999
+
2000
+ /*
2001
+ * Try to generate a string greater than the given string or any string it is
2002
+ * a prefix of. If successful, return a palloc'd string; else return NULL.
2003
+ *
2004
+ * To work correctly in non-ASCII locales with weird collation orders,
2005
+ * we cannot simply increment "foo" to "fop" --- we have to check whether
2006
+ * we actually produced a string greater than the given one. If not,
2007
+ * increment the righthand byte again and repeat. If we max out the righthand
2008
+ * byte, truncate off the last character and start incrementing the next.
2009
+ * For example, if "z" were the last character in the sort order, then we
2010
+ * could produce "foo" as a string greater than "fonz".
2011
+ *
2012
+ * This could be rather slow in the worst case, but in most cases we won't
2013
+ * have to try more than one or two strings before succeeding.
2014
+ *
2015
+ * XXX in a sufficiently weird locale, this might produce incorrect results?
2016
+ * For example, in German I believe "ss" is treated specially --- if we are
2017
+ * given "foos" and return "foot", will this actually be greater than "fooss"?
2018
+ */
2019
+ static char *
2020
+ make_greater_string (const char * str , Oid datatype )
2021
+ {
2022
+ char * workstr ;
2023
+ int len ;
2024
+
2025
+ /* Make a modifiable copy, which will be our return value if successful */
2026
+ workstr = pstrdup ((char * ) str );
2027
+
2028
+ while ((len = strlen (workstr )) > 0 )
2029
+ {
2030
+ unsigned char * lastchar = (unsigned char * ) (workstr + len - 1 );
2031
+
2032
+ /*
2033
+ * Try to generate a larger string by incrementing the last byte.
2034
+ */
2035
+ while (* lastchar < (unsigned char ) 255 )
2036
+ {
2037
+ (* lastchar )++ ;
2038
+ if (string_lessthan (str , workstr , datatype ))
2039
+ return workstr ; /* Success! */
2040
+ }
2041
+ /*
2042
+ * Truncate off the last character, which might be more than 1 byte
2043
+ * in MULTIBYTE case.
2044
+ */
2045
+ #ifdef MULTIBYTE
2046
+ len = pg_mbcliplen ((const unsigned char * ) workstr , len , len - 1 );
2047
+ workstr [len ] = '\0' ;
2048
+ #else
2049
+ * lastchar = '\0' ;
2010
2050
#endif
2051
+ }
2011
2052
2012
- return result ;
2053
+ /* Failed... */
2054
+ pfree (workstr );
2055
+ return NULL ;
2013
2056
}
2014
2057
2058
+ /*
2059
+ * Handy subroutines for match_special_index_operator() and friends.
2060
+ */
2061
+
2015
2062
/* See if there is a binary op of the given name for the given datatype */
2016
2063
static Oid
2017
2064
find_operator (const char * opname , Oid datatype )
@@ -2027,3 +2074,74 @@ find_operator(const char * opname, Oid datatype)
2027
2074
return InvalidOid ;
2028
2075
return optup -> t_data -> t_oid ;
2029
2076
}
2077
+
2078
+ /*
2079
+ * Generate a Datum of the appropriate type from a C string.
2080
+ * Note that all of the supported types are pass-by-ref, so the
2081
+ * returned value should be pfree'd if no longer needed.
2082
+ */
2083
+ static Datum
2084
+ string_to_datum (const char * str , Oid datatype )
2085
+ {
2086
+ /* We cheat a little by assuming that textin() will do for
2087
+ * bpchar and varchar constants too...
2088
+ */
2089
+ if (datatype == NAMEOID )
2090
+ return PointerGetDatum (namein ((char * ) str ));
2091
+ else
2092
+ return PointerGetDatum (textin ((char * ) str ));
2093
+ }
2094
+
2095
+ /*
2096
+ * Generate a Const node of the appropriate type from a C string.
2097
+ */
2098
+ static Const *
2099
+ string_to_const (const char * str , Oid datatype )
2100
+ {
2101
+ Datum conval = string_to_datum (str , datatype );
2102
+
2103
+ return makeConst (datatype , ((datatype == NAMEOID ) ? NAMEDATALEN : -1 ),
2104
+ conval , false, false, false, false);
2105
+ }
2106
+
2107
+ /*
2108
+ * Test whether two strings are "<" according to the rules of the given
2109
+ * datatype. We do this the hard way, ie, actually calling the type's
2110
+ * "<" operator function, to ensure we get the right result...
2111
+ */
2112
+ static bool
2113
+ string_lessthan (const char * str1 , const char * str2 , Oid datatype )
2114
+ {
2115
+ Datum datum1 = string_to_datum (str1 , datatype );
2116
+ Datum datum2 = string_to_datum (str2 , datatype );
2117
+ bool result ;
2118
+
2119
+ switch (datatype )
2120
+ {
2121
+ case TEXTOID :
2122
+ result = text_lt ((text * ) datum1 , (text * ) datum2 );
2123
+ break ;
2124
+
2125
+ case BPCHAROID :
2126
+ result = bpcharlt ((char * ) datum1 , (char * ) datum2 );
2127
+ break ;
2128
+
2129
+ case VARCHAROID :
2130
+ result = varcharlt ((char * ) datum1 , (char * ) datum2 );
2131
+ break ;
2132
+
2133
+ case NAMEOID :
2134
+ result = namelt ((NameData * ) datum1 , (NameData * ) datum2 );
2135
+ break ;
2136
+
2137
+ default :
2138
+ elog (ERROR , "string_lessthan: unexpected datatype %u" , datatype );
2139
+ result = false;
2140
+ break ;
2141
+ }
2142
+
2143
+ pfree (DatumGetPointer (datum1 ));
2144
+ pfree (DatumGetPointer (datum2 ));
2145
+
2146
+ return result ;
2147
+ }
0 commit comments