? GNUmakefile ? config.log ? config.status ? src/Makefile.global ? src/backend/parser/.gram.y.swp ? src/include/pg_config.h ? src/include/stamp-h ? src/test/regress/pg_regress ? src/test/regress/regression.diffs ? src/test/regress/regression.out ? src/test/regress/results ? src/test/regress/expected/constraints.out ? src/test/regress/expected/copy.out ? src/test/regress/expected/create_function_1.out ? src/test/regress/expected/create_function_2.out ? src/test/regress/expected/misc.out ? src/test/regress/sql/constraints.sql ? src/test/regress/sql/copy.sql ? src/test/regress/sql/create_function_1.sql ? src/test/regress/sql/create_function_2.sql ? src/test/regress/sql/misc.sql Index: src/backend/executor/execQual.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/executor/execQual.c,v retrieving revision 1.97 diff -c -r1.97 execQual.c *** src/backend/executor/execQual.c 2002/07/06 20:16:35 1.97 --- src/backend/executor/execQual.c 2002/07/10 00:40:31 *************** *** 38,43 **** --- 38,46 ---- #include "executor/execdebug.h" #include "executor/functions.h" #include "executor/nodeSubplan.h" + #include "nodes/makefuncs.h" + #include "parser/parse.h" + #include "parser/parse_expr.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/fcache.h" *************** *** 62,67 **** --- 65,72 ---- static Datum ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull); static Datum ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); + static Datum ExecEvalBetweenExpr(BetweenExpr *btest, ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalNullTest(NullTest *ntest, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalBooleanTest(BooleanTest *btest, ExprContext *econtext, *************** *** 1189,1194 **** --- 1194,1297 ---- } /* ---------------------------------------------------------------- + * ExecEvalBetweenExpr + * + * Evaluate a BetweenExpr node. Result is + * a boolean. If any of the three expression + * parameters are NULL, result is NULL. + * ---------------------------------------------------------------- + */ + static Datum + ExecEvalBetweenExpr(BetweenExpr *btest, + ExprContext *econtext, + bool *isNull, + ExprDoneCond *isDone) + { + Datum expr_result; + Datum lexpr_result; + Datum rexpr_result; + bool result = FALSE; + Node *expr_const; + Node *lexpr_const; + Node *rexpr_const; + + /* Evaluate subexpressons and Auto-return if we find a NULL */ + expr_result = ExecEvalExpr(btest->expr, econtext, isNull, isDone); + if (*isNull) + return (Datum) 0; + + lexpr_result = ExecEvalExpr(btest->lexpr, econtext, isNull, isDone); + if (*isNull) + return (Datum) 0; + + rexpr_result = ExecEvalExpr(btest->rexpr, econtext, isNull, isDone); + if (*isNull) + return (Datum) 0; + + /* + * Make a Constant out of our newly found Datums + * Types were coerced during transformExpr to be common + */ + expr_const = (Node *) makeConst(btest->typeId, btest->typeLen, + expr_result, false, + btest->typeByVal, false, true); + + lexpr_const = (Node *) makeConst(btest->typeId, btest->typeLen, + lexpr_result, false, + btest->typeByVal, false, true); + + rexpr_const = (Node *) makeConst(btest->typeId, btest->typeLen, + rexpr_result, false, + btest->typeByVal, false, true); + + /* + * Test the between case which for the straight forward method. + * expr >= lexpr and expr <= rexpr + * + * Of course, can't use makeA_Expr here without requiring another + * transform, so we've already prepared a gthan and lthan operator + * set in the parsing stage. + */ + btest->gthan->args = makeList2(expr_const, lexpr_const); + if (DatumGetBool(ExecEvalExpr((Node *) btest->gthan, + econtext, + isNull, isDone))) + { + btest->lthan->args = makeList2(expr_const, rexpr_const); + result = DatumGetBool(ExecEvalExpr((Node *) btest->lthan, + econtext, + isNull, isDone)); + } + + /* + * If this is a symmetric BETWEEN, we win a second try with the operators + * reversed. (a >= min(b,c) and a <= max(b,c)) + */ + if (!result && btest->symmetric) + { + btest->gthan->args = makeList2(expr_const, rexpr_const); + if (DatumGetBool(ExecEvalExpr((Node *) btest->gthan, + econtext, + isNull, isDone))) + { + btest->lthan->args = makeList2(expr_const, lexpr_const); + result = DatumGetBool(ExecEvalExpr((Node *) btest->lthan, + econtext, + isNull, isDone)); + } + } + + /* Apply NOT as necessary */ + if (btest->not) + result = !result; + + /* We're not returning a null */ + *isNull = false; + + return (BoolGetDatum(result)); + } + + /* ---------------------------------------------------------------- * ExecEvalNullTest * * Evaluate a NullTest node. *************** *** 1524,1529 **** --- 1627,1638 ---- isNull, isDone); break; + case T_BetweenExpr: + retDatum = ExecEvalBetweenExpr((BetweenExpr *) expression, + econtext, + isNull, + isDone); + break; case T_NullTest: retDatum = ExecEvalNullTest((NullTest *) expression, econtext, *************** *** 1536,1542 **** isNull, isDone); break; - default: elog(ERROR, "ExecEvalExpr: unknown expression type %d", nodeTag(expression)); --- 1645,1650 ---- Index: src/backend/nodes/copyfuncs.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v retrieving revision 1.192 diff -c -r1.192 copyfuncs.c *** src/backend/nodes/copyfuncs.c 2002/07/01 15:27:51 1.192 --- src/backend/nodes/copyfuncs.c 2002/07/10 00:40:47 *************** *** 1000,1005 **** --- 1000,1031 ---- } /* ---------------- + * _copyBetweenExpr + * ---------------- + */ + static BetweenExpr * + _copyBetweenExpr(BetweenExpr *from) + { + BetweenExpr *newnode = makeNode(BetweenExpr); + + /* + * copy remainder of node + */ + Node_Copy(from, newnode, expr); + Node_Copy(from, newnode, lexpr); + Node_Copy(from, newnode, rexpr); + Node_Copy(from, newnode, lthan); + Node_Copy(from, newnode, gthan); + newnode->symmetric = from->symmetric; + newnode->not = from->not; + newnode->typeId = from->typeId; + newnode->typeLen = from->typeLen; + newnode->typeByVal = from->typeByVal; + + return newnode; + } + + /* ---------------- * _copyCaseWhen * ---------------- */ *************** *** 3048,3053 **** --- 3074,3082 ---- break; case T_CaseExpr: retval = _copyCaseExpr(from); + break; + case T_BetweenExpr: + retval = _copyBetweenExpr(from); break; case T_CaseWhen: retval = _copyCaseWhen(from); Index: src/backend/nodes/equalfuncs.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v retrieving revision 1.139 diff -c -r1.139 equalfuncs.c *** src/backend/nodes/equalfuncs.c 2002/07/01 15:27:52 1.139 --- src/backend/nodes/equalfuncs.c 2002/07/10 00:40:56 *************** *** 1762,1767 **** --- 1762,1794 ---- } static bool + _equalBetweenExpr(BetweenExpr *a, BetweenExpr *b) + { + if (!equal(a->expr, b->expr)) + return false; + if (!equal(a->lexpr, b->lexpr)) + return false; + if (!equal(a->rexpr, b->rexpr)) + return false; + if (!equal(a->lthan, b->lthan)) + return false; + if (!equal(a->gthan, b->gthan)) + return false; + if (a->symmetric != b->symmetric) + return false; + if (a->not != b->not) + return false; + if (a->typeId != b->typeId) + return false; + if (a->typeLen != b->typeLen) + return false; + if (a->typeByVal != b->typeByVal) + return false; + + return true; + } + + static bool _equalCaseWhen(CaseWhen *a, CaseWhen *b) { if (!equal(a->expr, b->expr)) *************** *** 2208,2213 **** --- 2235,2243 ---- break; case T_CaseExpr: retval = _equalCaseExpr(a, b); + break; + case T_BetweenExpr: + retval = _equalBetweenExpr(a, b); break; case T_CaseWhen: retval = _equalCaseWhen(a, b); Index: src/backend/nodes/outfuncs.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/nodes/outfuncs.c,v retrieving revision 1.161 diff -c -r1.161 outfuncs.c *** src/backend/nodes/outfuncs.c 2002/07/04 15:23:53 1.161 --- src/backend/nodes/outfuncs.c 2002/07/10 00:41:20 *************** *** 1480,1485 **** --- 1480,1517 ---- } /* + * BetweenExpr + */ + static void + _outBetweenExpr(StringInfo str, BetweenExpr *node) + { + appendStringInfo(str, " BETWEENEXPR :expr "); + _outNode(str, node->expr); + + appendStringInfo(str, " :not %s", + booltostr(node->not)); + + appendStringInfo(str, " :symmetric %s", + booltostr(node->symmetric)); + + appendStringInfo(str, " :lexpr "); + _outNode(str, node->lexpr); + + appendStringInfo(str, " :rexpr "); + _outNode(str, node->rexpr); + + appendStringInfo(str, " :gthan "); + _outNode(str, node->gthan); + + appendStringInfo(str, " :lthan "); + _outNode(str, node->lthan); + + appendStringInfo(str, " :typeid %u :typelen %d :typebyval %s", + node->typeId, node->typeLen, + booltostr(node->typeByVal)); + } + + /* * NullTest */ static void *************** *** 1762,1767 **** --- 1794,1802 ---- break; case T_CaseExpr: _outCaseExpr(str, obj); + break; + case T_BetweenExpr: + _outBetweenExpr(str, obj); break; case T_CaseWhen: _outCaseWhen(str, obj); Index: src/backend/nodes/readfuncs.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/nodes/readfuncs.c,v retrieving revision 1.124 diff -c -r1.124 readfuncs.c *** src/backend/nodes/readfuncs.c 2002/07/04 15:23:54 1.124 --- src/backend/nodes/readfuncs.c 2002/07/10 00:41:36 *************** *** 881,886 **** --- 881,933 ---- return local_node; } + static BetweenExpr * + _readBetweenExpr(void) + { + BetweenExpr *local_node; + char *token; + int length; + + local_node = makeNode(BetweenExpr); + + token = pg_strtok(&length); /* eat :expr */ + local_node->expr = nodeRead(true); + + token = pg_strtok(&length); /* eat :not */ + token = pg_strtok(&length); /* get not */ + local_node->not = strtobool(token); + + token = pg_strtok(&length); /* eat :symmetric */ + token = pg_strtok(&length); /* get symmetric */ + local_node->symmetric = strtobool(token); + + token = pg_strtok(&length); /* eat :lexpr */ + local_node->lexpr = nodeRead(true); + + token = pg_strtok(&length); /* eat :rexpr */ + local_node->rexpr = nodeRead(true); + + token = pg_strtok(&length); /* eat :gthan */ + local_node->gthan = nodeRead(true); + + token = pg_strtok(&length); /* eat :lthan */ + local_node->lthan = nodeRead(true); + + token = pg_strtok(&length); /* eat :typeid */ + token = pg_strtok(&length); /* get typeid */ + local_node->typeId = atooid(token); + + token = pg_strtok(&length); /* eat :typelen */ + token = pg_strtok(&length); /* get typelen */ + local_node->typeLen = atoui(token); + + token = pg_strtok(&length); /* eat :typebyval */ + token = pg_strtok(&length); /* get typebyval */ + local_node->typeByVal = strtobool(token); + + return local_node; + } + /* ---------------- * _readNullTest * *************** *** 2132,2137 **** --- 2179,2186 ---- return_value = _readNullTest(); else if (length == 11 && strncmp(token, "BOOLEANTEST", length) == 0) return_value = _readBooleanTest(); + else if (length == 11 && strncmp(token, "BETWEENEXPR", length) == 0) + return_value = _readBetweenExpr(); else elog(ERROR, "badly formatted planstring \"%.10s\"...", token); Index: src/backend/optimizer/util/clauses.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v retrieving revision 1.103 diff -c -r1.103 clauses.c *** src/backend/optimizer/util/clauses.c 2002/07/06 20:16:35 1.103 --- src/backend/optimizer/util/clauses.c 2002/07/10 00:41:55 *************** *** 1817,1824 **** case T_Var: case T_Param: case T_RangeTblRef: - /* primitive node types with no subnodes */ - break; case T_Expr: { Expr *expr = (Expr *) node; --- 1817,1822 ---- *************** *** 1965,1970 **** --- 1963,1980 ---- return true; } break; + case T_BetweenExpr: + { + BetweenExpr *betwn = (BetweenExpr *) node; + + if (walker(betwn->expr, context)) + return true; + if (walker(betwn->lexpr, context)) + return true; + if (walker(betwn->rexpr, context)) + return true; + } + break; default: elog(ERROR, "expression_tree_walker: Unexpected node type %d", nodeTag(node)); *************** *** 2123,2130 **** case T_Var: case T_Param: case T_RangeTblRef: - /* primitive node types with no subnodes */ - return (Node *) copyObject(node); case T_Expr: { Expr *expr = (Expr *) node; --- 2133,2138 ---- *************** *** 2269,2274 **** --- 2277,2294 ---- FLATCOPY(newnode, btest, BooleanTest); MUTATE(newnode->arg, btest->arg, Node *); + return (Node *) newnode; + } + break; + case T_BetweenExpr: + { + BetweenExpr *bexpr = (BetweenExpr *) node; + BetweenExpr *newnode; + + FLATCOPY(newnode, bexpr, BetweenExpr); + MUTATE(newnode->expr, bexpr->expr, Node *); + MUTATE(newnode->lexpr, bexpr->lexpr, Node *); + MUTATE(newnode->rexpr, bexpr->rexpr, Node *); return (Node *) newnode; } break; Index: src/backend/parser/gram.y =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/parser/gram.y,v retrieving revision 2.337 diff -c -r2.337 gram.y *** src/backend/parser/gram.y 2002/07/06 20:16:35 2.337 --- src/backend/parser/gram.y 2002/07/10 00:43:21 *************** *** 241,247 **** %type extract_list, overlay_list, position_list %type substr_list, trim_list ! %type opt_interval %type overlay_placing, substr_from, substr_for %type opt_instead, opt_cursor --- 241,247 ---- %type extract_list, overlay_list, position_list %type substr_list, trim_list ! %type opt_interval, opt_symmetry %type overlay_placing, substr_from, substr_for %type opt_instead, opt_cursor *************** *** 327,333 **** /* ordinary key words in alphabetical order */ %token ABORT_TRANS, ABSOLUTE, ACCESS, ACTION, ADD, AFTER, AGGREGATE, ALL, ALTER, ANALYSE, ANALYZE, AND, ANY, AS, ASC, ! ASSERTION, ASSIGNMENT, AT, AUTHORIZATION, BACKWARD, BEFORE, BEGIN_TRANS, BETWEEN, BIGINT, BINARY, BIT, BOTH, BOOLEAN, BY, --- 327,333 ---- /* ordinary key words in alphabetical order */ %token ABORT_TRANS, ABSOLUTE, ACCESS, ACTION, ADD, AFTER, AGGREGATE, ALL, ALTER, ANALYSE, ANALYZE, AND, ANY, AS, ASC, ! ASSERTION, ASSIGNMENT, ASYMMETRIC, AT, AUTHORIZATION, BACKWARD, BEFORE, BEGIN_TRANS, BETWEEN, BIGINT, BINARY, BIT, BOTH, BOOLEAN, BY, *************** *** 386,392 **** SERIALIZABLE, SESSION, SESSION_USER, SET, SETOF, SHARE, SHOW, SIMILAR, SIMPLE, SMALLINT, SOME, STABLE, START, STATEMENT, STATISTICS, STDIN, STDOUT, STORAGE, STRICT, SUBSTRING, ! SYSID, TABLE, TEMP, TEMPLATE, TEMPORARY, THEN, TIME, TIMESTAMP, TO, TOAST, TRAILING, TRANSACTION, TREAT, TRIGGER, TRIM, TRUE_P, --- 386,392 ---- SERIALIZABLE, SESSION, SESSION_USER, SET, SETOF, SHARE, SHOW, SIMILAR, SIMPLE, SMALLINT, SOME, STABLE, START, STATEMENT, STATISTICS, STDIN, STDOUT, STORAGE, STRICT, SUBSTRING, ! SYMMETRIC, SYSID, TABLE, TEMP, TEMPLATE, TEMPORARY, THEN, TIME, TIMESTAMP, TO, TOAST, TRAILING, TRANSACTION, TREAT, TRIGGER, TRIM, TRUE_P, *************** *** 5463,5479 **** } | a_expr IS DISTINCT FROM a_expr %prec DISTINCT { $$ = (Node *) makeSimpleA_Expr(DISTINCT, "=", $1, $5); } ! | a_expr BETWEEN b_expr AND b_expr %prec BETWEEN { ! $$ = (Node *) makeA_Expr(AND, NIL, ! (Node *) makeSimpleA_Expr(OP, ">=", $1, $3), ! (Node *) makeSimpleA_Expr(OP, "<=", $1, $5)); ! } ! | a_expr NOT BETWEEN b_expr AND b_expr %prec BETWEEN ! { ! $$ = (Node *) makeA_Expr(OR, NIL, ! (Node *) makeSimpleA_Expr(OP, "<", $1, $4), ! (Node *) makeSimpleA_Expr(OP, ">", $1, $6)); } | a_expr IN_P in_expr { --- 5463,5487 ---- } | a_expr IS DISTINCT FROM a_expr %prec DISTINCT { $$ = (Node *) makeSimpleA_Expr(DISTINCT, "=", $1, $5); } ! | a_expr BETWEEN opt_symmetry b_expr AND b_expr %prec BETWEEN { ! BetweenExpr *n = makeNode(BetweenExpr); ! n->expr = $1; ! n->symmetric = $3; ! n->lexpr = $4; ! n->rexpr = $6; ! n->not = false; ! $$ = (Node *)n; ! } ! | a_expr NOT BETWEEN opt_symmetry b_expr AND b_expr %prec BETWEEN ! { ! BetweenExpr *n = makeNode(BetweenExpr); ! n->expr = $1; ! n->symmetric = $4; ! n->lexpr = $5; ! n->rexpr = $7; ! n->not = true; ! $$ = (Node *)n; } | a_expr IN_P in_expr { *************** *** 5545,5550 **** --- 5553,5563 ---- { $$ = $1; } ; + opt_symmetry: SYMMETRIC { $$ = TRUE; } + | ASYMMETRIC { $$ = FALSE; } + | /* EMPTY */ { $$ = FALSE; } + ; + /* * Restricted expressions * *************** *** 6880,6885 **** --- 6893,6899 ---- | ANY | AS | ASC + | ASYMMETRIC | BOTH | CASE | CAST *************** *** 6930,6935 **** --- 6944,6950 ---- | SELECT | SESSION_USER | SOME + | SYMMETRIC | TABLE | THEN | TO Index: src/backend/parser/keywords.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/parser/keywords.c,v retrieving revision 1.118 diff -c -r1.118 keywords.c *** src/backend/parser/keywords.c 2002/07/04 15:24:01 1.118 --- src/backend/parser/keywords.c 2002/07/10 00:43:23 *************** *** 45,50 **** --- 45,51 ---- {"asc", ASC}, {"assertion", ASSERTION}, {"assignment", ASSIGNMENT}, + {"asymmetric", ASYMMETRIC}, {"at", AT}, {"authorization", AUTHORIZATION}, {"backward", BACKWARD}, *************** *** 271,276 **** --- 272,278 ---- {"storage", STORAGE}, {"strict", STRICT}, {"substring", SUBSTRING}, + {"symmetric", SYMMETRIC}, {"sysid", SYSID}, {"table", TABLE}, {"temp", TEMP}, Index: src/backend/parser/parse_expr.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/parser/parse_expr.c,v retrieving revision 1.121 diff -c -r1.121 parse_expr.c *** src/backend/parser/parse_expr.c 2002/07/06 20:16:36 1.121 --- src/backend/parser/parse_expr.c 2002/07/10 00:44:12 *************** *** 69,79 **** * here. * * NOTE: there are various cases in which this routine will get applied to ! * an already-transformed expression. Some examples: ! * 1. At least one construct (BETWEEN/AND) puts the same nodes ! * into two branches of the parse tree; hence, some nodes ! * are transformed twice. ! * 2. Another way it can happen is that coercion of an operator or * function argument to the required type (via coerce_type()) * can apply transformExpr to an already-transformed subexpression. * An example here is "SELECT count(*) + 1.0 FROM table". --- 69,76 ---- * here. * * NOTE: there are various cases in which this routine will get applied to ! * an already-transformed expression. An examples: ! * - Another way it can happen is that coercion of an operator or * function argument to the required type (via coerce_type()) * can apply transformExpr to an already-transformed subexpression. * An example here is "SELECT count(*) + 1.0 FROM table". *************** *** 588,593 **** --- 585,666 ---- result = expr; break; } + case T_BetweenExpr: + { + BetweenExpr *b = (BetweenExpr *) expr; + List *typeIds = NIL; + HeapTuple tup; + Form_pg_operator opform; + + /* Transform the expressions */ + b->expr = transformExpr(pstate, b->expr); + b->lexpr = transformExpr(pstate, b->lexpr); + b->rexpr = transformExpr(pstate, b->rexpr); + + /* Find coercion type for all 3 entities */ + typeIds = lappendi(typeIds, exprType(b->expr)); + typeIds = lappendi(typeIds, exprType(b->lexpr)); + typeIds = lappendi(typeIds, exprType(b->rexpr)); + b->typeId = select_common_type(typeIds, "TransformExpr"); + + /* Additional type information */ + b->typeLen = get_typlen(b->typeId); + b->typeByVal = get_typbyval(b->typeId); + + /* Coerce the three expressions to the type */ + b->expr = coerce_to_common_type(pstate, b->expr, + b->typeId, + "TransformExpr"); + + b->lexpr = coerce_to_common_type(pstate, b->lexpr, + b->typeId, + "TransformExpr"); + + b->rexpr = coerce_to_common_type(pstate, b->rexpr, + b->typeId, + "TransformExpr"); + + /* Build the >= operator */ + tup = oper(makeList1(makeString(">=")), + b->typeId, b->typeId, false); + opform = (Form_pg_operator) GETSTRUCT(tup); + + /* Triple check our types */ + if (b->typeId != opform->oprright || b->typeId != opform->oprleft) + elog(ERROR, "transformExpr: Unable to find appropriate" + " operator for between operation"); + + b->gthan = makeNode(Expr); + b->gthan->typeOid = opform->oprresult; + b->gthan->opType = OP_EXPR; + b->gthan->oper = (Node *) makeOper(oprid(tup), /* opno */ + oprfuncid(tup), /* opid */ + opform->oprresult, /* opresulttype */ + get_func_retset(opform->oprcode));/* opretset */ + ReleaseSysCache(tup); + + /* Build the equation for <= operator */ + tup = oper(makeList1(makeString("<=")), + b->typeId, b->typeId, false); + opform = (Form_pg_operator) GETSTRUCT(tup); + + /* Triple check the types */ + if (b->typeId != opform->oprright || b->typeId != opform->oprleft) + elog(ERROR, "transformExpr: Unable to find appropriate" + " operator for between operation"); + + b->lthan = makeNode(Expr); + b->lthan->typeOid = opform->oprresult; + b->lthan->opType = OP_EXPR; + b->lthan->oper = (Node *) makeOper(oprid(tup), /* opno */ + oprfuncid(tup), /* opid */ + opform->oprresult, /* opresulttype */ + get_func_retset(opform->oprcode));/* opretset */ + ReleaseSysCache(tup); + + result = expr; + break; + } /* * Quietly accept node types that may be presented when we are *************** *** 609,615 **** result = (Node *) expr; break; } - default: /* should not reach here */ elog(ERROR, "transformExpr: does not know how to transform node %d" --- 682,687 ---- *************** *** 888,893 **** --- 960,968 ---- type = BOOLOID; break; case T_BooleanTest: + type = BOOLOID; + break; + case T_BetweenExpr: type = BOOLOID; break; default: Index: src/backend/utils/adt/ruleutils.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v retrieving revision 1.109 diff -c -r1.109 ruleutils.c *** src/backend/utils/adt/ruleutils.c 2002/07/04 15:24:07 1.109 --- src/backend/utils/adt/ruleutils.c 2002/07/10 00:44:35 *************** *** 1879,1885 **** --- 1879,1910 ---- } } break; + case T_BetweenExpr: + { + BetweenExpr *btwn = (BetweenExpr *) node; + get_rule_expr(btwn->expr, context); + + if (btwn->not) + appendStringInfo(buf, " NOT"); + + appendStringInfo(buf, " BETWEEN"); + + /* + * Output both symmetric and asymmetric, even though + * asymmetric is default + */ + if (btwn->symmetric) + appendStringInfo(buf, " SYMMETRIC "); + else + appendStringInfo(buf, " ASYMMETRIC "); + + get_rule_expr(btwn->lexpr, context); + + appendStringInfo(buf, " AND "); + get_rule_expr(btwn->rexpr, context); + } + break; default: elog(ERROR, "get_rule_expr: unknown node type %d", nodeTag(node)); break; Index: src/include/nodes/makefuncs.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/nodes/makefuncs.h,v retrieving revision 1.37 diff -c -r1.37 makefuncs.h *** src/include/nodes/makefuncs.h 2002/06/20 20:29:49 1.37 --- src/include/nodes/makefuncs.h 2002/07/10 00:44:53 *************** *** 57,61 **** --- 57,62 ---- extern RangeVar *makeRangeVar(char *schemaname, char *relname); extern TypeName *makeTypeName(char *typnam); + extern TypeName *makeQualifiedTypeName(List *lst); #endif /* MAKEFUNC_H */ Index: src/include/nodes/nodes.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/nodes/nodes.h,v retrieving revision 1.109 diff -c -r1.109 nodes.h *** src/include/nodes/nodes.h 2002/06/20 20:29:51 1.109 --- src/include/nodes/nodes.h 2002/07/10 00:44:55 *************** *** 225,230 **** --- 225,231 ---- T_GroupClause, T_NullTest, T_BooleanTest, + T_BetweenExpr, T_CaseExpr, T_CaseWhen, T_FkConstraint, Index: src/include/nodes/parsenodes.h =================================================================== RCS file: /projects/cvsroot/pgsql/src/include/nodes/parsenodes.h,v retrieving revision 1.183 diff -c -r1.183 parsenodes.h *** src/include/nodes/parsenodes.h 2002/07/01 15:27:56 1.183 --- src/include/nodes/parsenodes.h 2002/07/10 00:45:16 *************** *** 174,179 **** --- 174,198 ---- } A_Const; /* + * BetweenExpr - an SQL99 BETWEEN expression + */ + + typedef struct BetweenExpr + { + NodeTag type; + Node *expr; /* Expression to check */ + Node *lexpr; /* First bound */ + Node *rexpr; /* Second bound */ + bool not; /* Do we want inverse? */ + bool symmetric; /* True if SYMMETRIC, false if ASYMMETRIC */ + Oid typeId; /* Information abotu common type */ + int16 typeLen; + bool typeByVal; + Expr *gthan; + Expr *lthan; + } BetweenExpr; + + /* * TypeCast - a CAST expression * * NOTE: for mostly historical reasons, A_Const parsenodes contain Index: src/test/regress/expected/select.out =================================================================== RCS file: /projects/cvsroot/pgsql/src/test/regress/expected/select.out,v retrieving revision 1.10 diff -c -r1.10 select.out *** src/test/regress/expected/select.out 2001/07/16 05:07:00 1.10 --- src/test/regress/expected/select.out 2002/07/10 00:45:26 *************** *** 430,432 **** --- 430,579 ---- mary | 8 (58 rows) + -- + -- Test between syntax + -- + SELECT 2 BETWEEN 1 AND 3; + ?column? + ---------- + t + (1 row) + + SELECT 2 BETWEEN 3 AND 1; + ?column? + ---------- + f + (1 row) + + SELECT 2 BETWEEN ASYMMETRIC 1 AND 3; + ?column? + ---------- + t + (1 row) + + SELECT 2 BETWEEN ASYMMETRIC 3 AND 1; + ?column? + ---------- + f + (1 row) + + SELECT 2 BETWEEN SYMMETRIC 1 AND 3; + ?column? + ---------- + t + (1 row) + + SELECT 2 BETWEEN SYMMETRIC 3 AND 1; + ?column? + ---------- + t + (1 row) + + SELECT 2 NOT BETWEEN 1 AND 3; + ?column? + ---------- + f + (1 row) + + SELECT 2 NOT BETWEEN 3 AND 1; + ?column? + ---------- + t + (1 row) + + SELECT 2 NOT BETWEEN ASYMMETRIC 1 AND 3; + ?column? + ---------- + f + (1 row) + + SELECT 2 NOT BETWEEN ASYMMETRIC 3 AND 1; + ?column? + ---------- + t + (1 row) + + SELECT 2 NOT BETWEEN SYMMETRIC 1 AND 3; + ?column? + ---------- + f + (1 row) + + SELECT 2 NOT BETWEEN SYMMETRIC 3 AND 1; + ?column? + ---------- + f + (1 row) + + SELECT -4 BETWEEN -1 AND -3; + ?column? + ---------- + f + (1 row) + + SELECT -4 BETWEEN -3 AND -1; + ?column? + ---------- + f + (1 row) + + SELECT -4 BETWEEN ASYMMETRIC -1 AND -3; + ?column? + ---------- + f + (1 row) + + SELECT -4 BETWEEN ASYMMETRIC -3 AND -1; + ?column? + ---------- + f + (1 row) + + SELECT -4 BETWEEN SYMMETRIC -1 AND -3; + ?column? + ---------- + f + (1 row) + + SELECT -4 BETWEEN SYMMETRIC -3 AND -1; + ?column? + ---------- + f + (1 row) + + SELECT -4 NOT BETWEEN -1 AND -3; + ?column? + ---------- + t + (1 row) + + SELECT -4 NOT BETWEEN -3 AND -1; + ?column? + ---------- + t + (1 row) + + SELECT -4 NOT BETWEEN ASYMMETRIC -1 AND -3; + ?column? + ---------- + t + (1 row) + + SELECT -4 NOT BETWEEN ASYMMETRIC -3 AND -1; + ?column? + ---------- + t + (1 row) + + SELECT -4 NOT BETWEEN SYMMETRIC -1 AND -3; + ?column? + ---------- + t + (1 row) + + SELECT -4 NOT BETWEEN SYMMETRIC -3 AND -1; + ?column? + ---------- + t + (1 row) + Index: src/test/regress/sql/select.sql =================================================================== RCS file: /projects/cvsroot/pgsql/src/test/regress/sql/select.sql,v retrieving revision 1.6 diff -c -r1.6 select.sql *** src/test/regress/sql/select.sql 2001/07/16 05:07:00 1.6 --- src/test/regress/sql/select.sql 2002/07/10 00:45:27 *************** *** 103,105 **** --- 103,133 ---- -- SELECT p.name, p.age FROM person* p ORDER BY age using >, name; + -- + -- Test between syntax + -- + SELECT 2 BETWEEN 1 AND 3; + SELECT 2 BETWEEN 3 AND 1; + SELECT 2 BETWEEN ASYMMETRIC 1 AND 3; + SELECT 2 BETWEEN ASYMMETRIC 3 AND 1; + SELECT 2 BETWEEN SYMMETRIC 1 AND 3; + SELECT 2 BETWEEN SYMMETRIC 3 AND 1; + SELECT 2 NOT BETWEEN 1 AND 3; + SELECT 2 NOT BETWEEN 3 AND 1; + SELECT 2 NOT BETWEEN ASYMMETRIC 1 AND 3; + SELECT 2 NOT BETWEEN ASYMMETRIC 3 AND 1; + SELECT 2 NOT BETWEEN SYMMETRIC 1 AND 3; + SELECT 2 NOT BETWEEN SYMMETRIC 3 AND 1; + SELECT -4 BETWEEN -1 AND -3; + SELECT -4 BETWEEN -3 AND -1; + SELECT -4 BETWEEN ASYMMETRIC -1 AND -3; + SELECT -4 BETWEEN ASYMMETRIC -3 AND -1; + SELECT -4 BETWEEN SYMMETRIC -1 AND -3; + SELECT -4 BETWEEN SYMMETRIC -3 AND -1; + SELECT -4 NOT BETWEEN -1 AND -3; + SELECT -4 NOT BETWEEN -3 AND -1; + SELECT -4 NOT BETWEEN ASYMMETRIC -1 AND -3; + SELECT -4 NOT BETWEEN ASYMMETRIC -3 AND -1; + SELECT -4 NOT BETWEEN SYMMETRIC -1 AND -3; + SELECT -4 NOT BETWEEN SYMMETRIC -3 AND -1; +