+List *
+expand_indexqual_conditions(List *indexquals)
+{
+ List *resultquals = NIL;
+ List *q;
+
+ foreach(q, indexquals)
+ {
+ Expr *clause = (Expr *) lfirst(q);
+ /* we know these will succeed */
+ Var *leftop = get_leftop(clause);
+ Var *rightop = get_rightop(clause);
+ Oid expr_op = ((Oper *) clause->oper)->opno;
+ Datum constvalue;
+ char *patt;
+ char *prefix;
+ Prefix_Status pstatus;
+
+ switch (expr_op)
+ {
+ /*
+ * LIKE and regex operators are not members of any index opclass,
+ * so if we find one in an indexqual list we can assume that
+ * it was accepted by match_special_index_operator().
+ */
+ case OID_TEXT_LIKE_OP:
+ case OID_BPCHAR_LIKE_OP:
+ case OID_VARCHAR_LIKE_OP:
+ case OID_NAME_LIKE_OP:
+ /* the right-hand const is type text for all of these */
+ constvalue = ((Const *) rightop)->constvalue;
+ patt = textout((text *) DatumGetPointer(constvalue));
+ pstatus = like_fixed_prefix(patt, &prefix);
+ resultquals = nconc(resultquals,
+ prefix_quals(leftop, expr_op,
+ prefix, pstatus));
+ if (prefix) pfree(prefix);
+ pfree(patt);
+ break;
+
+ case OID_TEXT_REGEXEQ_OP:
+ case OID_BPCHAR_REGEXEQ_OP:
+ case OID_VARCHAR_REGEXEQ_OP:
+ case OID_NAME_REGEXEQ_OP:
+ /* the right-hand const is type text for all of these */
+ constvalue = ((Const *) rightop)->constvalue;
+ patt = textout((text *) DatumGetPointer(constvalue));
+ pstatus = regex_fixed_prefix(patt, false, &prefix);
+ resultquals = nconc(resultquals,
+ prefix_quals(leftop, expr_op,
+ prefix, pstatus));
+ if (prefix) pfree(prefix);
+ pfree(patt);
+ break;
+
+ case OID_TEXT_ICREGEXEQ_OP:
+ case OID_BPCHAR_ICREGEXEQ_OP:
+ case OID_VARCHAR_ICREGEXEQ_OP:
+ case OID_NAME_ICREGEXEQ_OP:
+ /* the right-hand const is type text for all of these */
+ constvalue = ((Const *) rightop)->constvalue;
+ patt = textout((text *) DatumGetPointer(constvalue));
+ pstatus = regex_fixed_prefix(patt, true, &prefix);
+ resultquals = nconc(resultquals,
+ prefix_quals(leftop, expr_op,
+ prefix, pstatus));
+ if (prefix) pfree(prefix);
+ pfree(patt);
+ break;
+
+ default:
+ resultquals = lappend(resultquals, clause);
+ break;
+ }
+ }
+
+ return resultquals;
+}
+
+/*
+ * Extract the fixed prefix, if any, for a LIKE pattern.
+ * *prefix is set to a palloc'd prefix string,
+ * or to NULL if no fixed prefix exists for the pattern.
+ * The return value distinguishes no fixed prefix, a partial prefix,
+ * or an exact-match-only pattern.
+ */
+static Prefix_Status
+like_fixed_prefix(char *patt, char **prefix)
+{
+ char *match;
+ int pos,
+ match_pos;
+
+ *prefix = match = palloc(strlen(patt)+1);
+ match_pos = 0;
+
+ for (pos = 0; patt[pos]; pos++)
+ {
+ /* % and _ are wildcard characters in LIKE */
+ if (patt[pos] == '%' ||
+ patt[pos] == '_')
+ break;
+ /* Backslash quotes the next character */
+ if (patt[pos] == '\\')
+ {
+ pos++;
+ if (patt[pos] == '\0')
+ break;
+ }
+ /*
+ * NOTE: this code used to think that %% meant a literal %,
+ * but textlike() itself does not think that, and the SQL92
+ * spec doesn't say any such thing either.
+ */
+ match[match_pos++] = patt[pos];
+ }
+
+ match[match_pos] = '\0';
+
+ /* in LIKE, an empty pattern is an exact match! */
+ if (patt[pos] == '\0')
+ return Prefix_Exact; /* reached end of pattern, so exact */
+
+ if (match_pos > 0)
+ return Prefix_Partial;
+ return Prefix_None;
+}
+
+/*
+ * Extract the fixed prefix, if any, for a regex pattern.
+ * *prefix is set to a palloc'd prefix string,
+ * or to NULL if no fixed prefix exists for the pattern.
+ * The return value distinguishes no fixed prefix, a partial prefix,
+ * or an exact-match-only pattern.
+ */
+static Prefix_Status
+regex_fixed_prefix(char *patt, bool case_insensitive,
+ char **prefix)