aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/ruleutils.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2005-12-28 01:30:02 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2005-12-28 01:30:02 +0000
commit6e07709760a29d8dbfb93b9846c905bd40689082 (patch)
tree9bf0084587d7e313ba087ce53c24bc748c63a456 /src/backend/utils/adt/ruleutils.c
parenta37422e042a6114ab0e513f50dac4a47fab22313 (diff)
downloadpostgresql-6e07709760a29d8dbfb93b9846c905bd40689082.tar.gz
postgresql-6e07709760a29d8dbfb93b9846c905bd40689082.zip
Implement SQL-compliant treatment of row comparisons for < <= > >= cases
(previously we only did = and <> correctly). Also, allow row comparisons with any operators that are in btree opclasses, not only those with these specific names. This gets rid of a whole lot of indefensible assumptions about the behavior of particular operators based on their names ... though it's still true that IN and NOT IN expand to "= ANY". The patch adds a RowCompareExpr expression node type, and makes some changes in the representation of ANY/ALL/ROWCOMPARE SubLinks so that they can share code with RowCompareExpr. I have not yet done anything about making RowCompareExpr an indexable operator, but will look at that soon. initdb forced due to changes in stored rules.
Diffstat (limited to 'src/backend/utils/adt/ruleutils.c')
-rw-r--r--src/backend/utils/adt/ruleutils.c160
1 files changed, 107 insertions, 53 deletions
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 5a102e5fed5..eaf4f195077 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -3,7 +3,7 @@
* back to source text
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.210 2005/12/10 19:21:03 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.211 2005/12/28 01:30:00 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -215,7 +215,6 @@ static void printSubscripts(ArrayRef *aref, deparse_context *context);
static char *generate_relation_name(Oid relid);
static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes);
static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
-static void print_operator_name(StringInfo buf, List *opname);
static text *string_to_text(char *str);
#define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
@@ -3106,6 +3105,7 @@ get_rule_expr(Node *node, deparse_context *context,
break;
case PARAM_NUM:
case PARAM_EXEC:
+ case PARAM_SUBLINK:
appendStringInfo(buf, "$%d", param->paramid);
break;
default:
@@ -3514,6 +3514,50 @@ get_rule_expr(Node *node, deparse_context *context,
}
break;
+ case T_RowCompareExpr:
+ {
+ RowCompareExpr *rcexpr = (RowCompareExpr *) node;
+ ListCell *arg;
+ char *sep;
+
+ /*
+ * SQL99 allows "ROW" to be omitted when there is more than
+ * one column, but for simplicity we always print it.
+ */
+ appendStringInfo(buf, "(ROW(");
+ sep = "";
+ foreach(arg, rcexpr->largs)
+ {
+ Node *e = (Node *) lfirst(arg);
+
+ appendStringInfoString(buf, sep);
+ get_rule_expr(e, context, true);
+ sep = ", ";
+ }
+ /*
+ * We assume that the name of the first-column operator
+ * will do for all the rest too. This is definitely
+ * open to failure, eg if some but not all operators
+ * were renamed since the construct was parsed, but there
+ * seems no way to be perfect.
+ */
+ appendStringInfo(buf, ") %s ROW(",
+ generate_operator_name(linitial_oid(rcexpr->opnos),
+ exprType(linitial(rcexpr->largs)),
+ exprType(linitial(rcexpr->rargs))));
+ sep = "";
+ foreach(arg, rcexpr->rargs)
+ {
+ Node *e = (Node *) lfirst(arg);
+
+ appendStringInfoString(buf, sep);
+ get_rule_expr(e, context, true);
+ sep = ", ";
+ }
+ appendStringInfo(buf, "))");
+ }
+ break;
+
case T_CoalesceExpr:
{
CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
@@ -3967,6 +4011,7 @@ get_sublink_expr(SubLink *sublink, deparse_context *context)
{
StringInfo buf = context->buf;
Query *query = (Query *) (sublink->subselect);
+ char *opname = NULL;
bool need_paren;
if (sublink->subLinkType == ARRAY_SUBLINK)
@@ -3974,25 +4019,67 @@ get_sublink_expr(SubLink *sublink, deparse_context *context)
else
appendStringInfoChar(buf, '(');
- if (sublink->lefthand != NIL)
+ /*
+ * Note that we print the name of only the first operator, when there
+ * are multiple combining operators. This is an approximation that
+ * could go wrong in various scenarios (operators in different schemas,
+ * renamed operators, etc) but there is not a whole lot we can do about
+ * it, since the syntax allows only one operator to be shown.
+ */
+ if (sublink->testexpr)
{
- need_paren = (list_length(sublink->lefthand) > 1);
- if (need_paren)
+ if (IsA(sublink->testexpr, OpExpr))
+ {
+ /* single combining operator */
+ OpExpr *opexpr = (OpExpr *) sublink->testexpr;
+
+ get_rule_expr(linitial(opexpr->args), context, true);
+ opname = generate_operator_name(opexpr->opno,
+ exprType(linitial(opexpr->args)),
+ exprType(lsecond(opexpr->args)));
+ }
+ else if (IsA(sublink->testexpr, BoolExpr))
+ {
+ /* multiple combining operators, = or <> cases */
+ char *sep;
+ ListCell *l;
+
appendStringInfoChar(buf, '(');
- get_rule_expr((Node *) sublink->lefthand, context, true);
- if (need_paren)
+ sep = "";
+ foreach(l, ((BoolExpr *) sublink->testexpr)->args)
+ {
+ OpExpr *opexpr = (OpExpr *) lfirst(l);
+
+ Assert(IsA(opexpr, OpExpr));
+ appendStringInfoString(buf, sep);
+ get_rule_expr(linitial(opexpr->args), context, true);
+ if (!opname)
+ opname = generate_operator_name(opexpr->opno,
+ exprType(linitial(opexpr->args)),
+ exprType(lsecond(opexpr->args)));
+ sep = ", ";
+ }
appendStringInfoChar(buf, ')');
- appendStringInfoChar(buf, ' ');
+ }
+ else if (IsA(sublink->testexpr, RowCompareExpr))
+ {
+ /* multiple combining operators, < <= > >= cases */
+ RowCompareExpr *rcexpr = (RowCompareExpr *) sublink->testexpr;
+
+ appendStringInfoChar(buf, '(');
+ get_rule_expr((Node *) rcexpr->largs, context, true);
+ opname = generate_operator_name(linitial_oid(rcexpr->opnos),
+ exprType(linitial(rcexpr->largs)),
+ exprType(linitial(rcexpr->rargs)));
+ appendStringInfoChar(buf, ')');
+ }
+ else
+ elog(ERROR, "unrecognized testexpr type: %d",
+ (int) nodeTag(sublink->testexpr));
}
need_paren = true;
- /*
- * XXX we regurgitate the originally given operator name, with or without
- * schema qualification. This is not necessarily 100% right but it's the
- * best we can do, since the operators actually used might not all be in
- * the same schema.
- */
switch (sublink->subLinkType)
{
case EXISTS_SUBLINK:
@@ -4000,27 +4087,18 @@ get_sublink_expr(SubLink *sublink, deparse_context *context)
break;
case ANY_SUBLINK:
- if (list_length(sublink->operName) == 1 &&
- strcmp(strVal(linitial(sublink->operName)), "=") == 0)
- {
- /* Represent = ANY as IN */
- appendStringInfo(buf, "IN ");
- }
+ if (strcmp(opname, "=") == 0) /* Represent = ANY as IN */
+ appendStringInfo(buf, " IN ");
else
- {
- print_operator_name(buf, sublink->operName);
- appendStringInfo(buf, " ANY ");
- }
+ appendStringInfo(buf, " %s ANY ", opname);
break;
case ALL_SUBLINK:
- print_operator_name(buf, sublink->operName);
- appendStringInfo(buf, " ALL ");
+ appendStringInfo(buf, " %s ALL ", opname);
break;
- case MULTIEXPR_SUBLINK:
- print_operator_name(buf, sublink->operName);
- appendStringInfoChar(buf, ' ');
+ case ROWCOMPARE_SUBLINK:
+ appendStringInfo(buf, " %s ", opname);
break;
case EXPR_SUBLINK:
@@ -4813,30 +4891,6 @@ generate_operator_name(Oid operid, Oid arg1, Oid arg2)
}
/*
- * Print out a possibly-qualified operator name
- */
-static void
-print_operator_name(StringInfo buf, List *opname)
-{
- ListCell *op = list_head(opname);
- int nnames = list_length(opname);
-
- if (nnames == 1)
- appendStringInfoString(buf, strVal(lfirst(op)));
- else
- {
- appendStringInfo(buf, "OPERATOR(");
- while (nnames-- > 1)
- {
- appendStringInfo(buf, "%s.",
- quote_identifier(strVal(lfirst(op))));
- op = lnext(op);
- }
- appendStringInfo(buf, "%s)", strVal(lfirst(op)));
- }
-}
-
-/*
* Given a C string, produce a TEXT datum.
*
* We assume that the input was palloc'd and may be freed.