Commit b89140a7 authored by Tom Lane's avatar Tom Lane

Do honest transformation and preprocessing of LIMIT/OFFSET clauses,

instead of the former kluge whereby gram.y emitted already-transformed
expressions.  This is needed so that Params appearing in these clauses
actually work correctly.  I suppose some might claim that the side effect
of 'SELECT ... LIMIT 2+2' working is a new feature, but I say this is
a bug fix.
parent 455891bf
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.155 2003/06/16 02:03:37 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.156 2003/07/03 19:07:20 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -47,7 +47,8 @@ ...@@ -47,7 +47,8 @@
#define EXPRKIND_QUAL 0 #define EXPRKIND_QUAL 0
#define EXPRKIND_TARGET 1 #define EXPRKIND_TARGET 1
#define EXPRKIND_RTFUNC 2 #define EXPRKIND_RTFUNC 2
#define EXPRKIND_ININFO 3 #define EXPRKIND_LIMIT 3
#define EXPRKIND_ININFO 4
static Node *preprocess_expression(Query *parse, Node *expr, int kind); static Node *preprocess_expression(Query *parse, Node *expr, int kind);
...@@ -232,6 +233,11 @@ subquery_planner(Query *parse, double tuple_fraction) ...@@ -232,6 +233,11 @@ subquery_planner(Query *parse, double tuple_fraction)
parse->havingQual = preprocess_expression(parse, parse->havingQual, parse->havingQual = preprocess_expression(parse, parse->havingQual,
EXPRKIND_QUAL); EXPRKIND_QUAL);
parse->limitOffset = preprocess_expression(parse, parse->limitOffset,
EXPRKIND_LIMIT);
parse->limitCount = preprocess_expression(parse, parse->limitCount,
EXPRKIND_LIMIT);
parse->in_info_list = (List *) parse->in_info_list = (List *)
preprocess_expression(parse, (Node *) parse->in_info_list, preprocess_expression(parse, (Node *) parse->in_info_list,
EXPRKIND_ININFO); EXPRKIND_ININFO);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.145 2003/07/03 16:33:07 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.146 2003/07/03 19:07:25 tgl Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -2390,6 +2390,10 @@ query_tree_walker(Query *query, ...@@ -2390,6 +2390,10 @@ query_tree_walker(Query *query,
return true; return true;
if (walker(query->havingQual, context)) if (walker(query->havingQual, context))
return true; return true;
if (walker(query->limitOffset, context))
return true;
if (walker(query->limitCount, context))
return true;
if (walker(query->in_info_list, context)) if (walker(query->in_info_list, context))
return true; return true;
foreach(rt, query->rtable) foreach(rt, query->rtable)
...@@ -2863,6 +2867,8 @@ query_tree_mutator(Query *query, ...@@ -2863,6 +2867,8 @@ query_tree_mutator(Query *query,
MUTATE(query->jointree, query->jointree, FromExpr *); MUTATE(query->jointree, query->jointree, FromExpr *);
MUTATE(query->setOperations, query->setOperations, Node *); MUTATE(query->setOperations, query->setOperations, Node *);
MUTATE(query->havingQual, query->havingQual, Node *); MUTATE(query->havingQual, query->havingQual, Node *);
MUTATE(query->limitOffset, query->limitOffset, Node *);
MUTATE(query->limitCount, query->limitCount, Node *);
MUTATE(query->in_info_list, query->in_info_list, List *); MUTATE(query->in_info_list, query->in_info_list, List *);
FastListInit(&newrt); FastListInit(&newrt);
foreach(rt, query->rtable) foreach(rt, query->rtable)
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.277 2003/06/25 04:19:24 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.278 2003/07/03 19:07:30 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -459,7 +459,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt) ...@@ -459,7 +459,7 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
qry->distinctClause = NIL; qry->distinctClause = NIL;
/* fix where clause */ /* fix where clause */
qual = transformWhereClause(pstate, stmt->whereClause); qual = transformWhereClause(pstate, stmt->whereClause, "WHERE");
/* done building the range table and jointree */ /* done building the range table and jointree */
qry->rtable = pstate->p_rtable; qry->rtable = pstate->p_rtable;
...@@ -1588,7 +1588,8 @@ transformIndexStmt(ParseState *pstate, IndexStmt *stmt) ...@@ -1588,7 +1588,8 @@ transformIndexStmt(ParseState *pstate, IndexStmt *stmt)
/* no to join list, yes to namespace */ /* no to join list, yes to namespace */
addRTEtoQuery(pstate, rte, false, true); addRTEtoQuery(pstate, rte, false, true);
stmt->whereClause = transformWhereClause(pstate, stmt->whereClause); stmt->whereClause = transformWhereClause(pstate, stmt->whereClause,
"WHERE");
} }
/* take care of any index expressions */ /* take care of any index expressions */
...@@ -1699,7 +1700,8 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt, ...@@ -1699,7 +1700,8 @@ transformRuleStmt(ParseState *pstate, RuleStmt *stmt,
} }
/* take care of the where clause */ /* take care of the where clause */
stmt->whereClause = transformWhereClause(pstate, stmt->whereClause); stmt->whereClause = transformWhereClause(pstate, stmt->whereClause,
"WHERE");
if (length(pstate->p_rtable) != 2) /* naughty, naughty... */ if (length(pstate->p_rtable) != 2) /* naughty, naughty... */
elog(ERROR, "Rule WHERE condition may not contain references to other relations"); elog(ERROR, "Rule WHERE condition may not contain references to other relations");
...@@ -1891,13 +1893,14 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) ...@@ -1891,13 +1893,14 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
markTargetListOrigins(pstate, qry->targetList); markTargetListOrigins(pstate, qry->targetList);
/* transform WHERE */ /* transform WHERE */
qual = transformWhereClause(pstate, stmt->whereClause); qual = transformWhereClause(pstate, stmt->whereClause, "WHERE");
/* /*
* Initial processing of HAVING clause is just like WHERE clause. * Initial processing of HAVING clause is just like WHERE clause.
* Additional work will be done in optimizer/plan/planner.c. * Additional work will be done in optimizer/plan/planner.c.
*/ */
qry->havingQual = transformWhereClause(pstate, stmt->havingClause); qry->havingQual = transformWhereClause(pstate, stmt->havingClause,
"HAVING");
/* /*
* Transform sorting/grouping stuff. Do ORDER BY first because both * Transform sorting/grouping stuff. Do ORDER BY first because both
...@@ -1918,8 +1921,10 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt) ...@@ -1918,8 +1921,10 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
qry->targetList, qry->targetList,
&qry->sortClause); &qry->sortClause);
qry->limitOffset = stmt->limitOffset; qry->limitOffset = transformLimitClause(pstate, stmt->limitOffset,
qry->limitCount = stmt->limitCount; "OFFSET");
qry->limitCount = transformLimitClause(pstate, stmt->limitCount,
"LIMIT");
qry->rtable = pstate->p_rtable; qry->rtable = pstate->p_rtable;
qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
...@@ -2124,8 +2129,10 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt) ...@@ -2124,8 +2129,10 @@ transformSetOperationStmt(ParseState *pstate, SelectStmt *stmt)
if (tllen != length(qry->targetList)) if (tllen != length(qry->targetList))
elog(ERROR, "ORDER BY on a UNION/INTERSECT/EXCEPT result must be on one of the result columns"); elog(ERROR, "ORDER BY on a UNION/INTERSECT/EXCEPT result must be on one of the result columns");
qry->limitOffset = limitOffset; qry->limitOffset = transformLimitClause(pstate, limitOffset,
qry->limitCount = limitCount; "OFFSET");
qry->limitCount = transformLimitClause(pstate, limitCount,
"LIMIT");
qry->rtable = pstate->p_rtable; qry->rtable = pstate->p_rtable;
qry->jointree = makeFromExpr(pstate->p_joinlist, NULL); qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
...@@ -2376,7 +2383,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt) ...@@ -2376,7 +2383,7 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
qry->targetList = transformTargetList(pstate, stmt->targetList); qry->targetList = transformTargetList(pstate, stmt->targetList);
qual = transformWhereClause(pstate, stmt->whereClause); qual = transformWhereClause(pstate, stmt->whereClause, "WHERE");
qry->rtable = pstate->p_rtable; qry->rtable = pstate->p_rtable;
qry->jointree = makeFromExpr(pstate->p_joinlist, qual); qry->jointree = makeFromExpr(pstate->p_joinlist, qual);
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.425 2003/07/03 16:33:37 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.426 2003/07/03 19:07:36 tgl Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -4585,12 +4585,13 @@ select_limit: ...@@ -4585,12 +4585,13 @@ select_limit:
| OFFSET select_offset_value | OFFSET select_offset_value
{ $$ = makeList2($2, NULL); } { $$ = makeList2($2, NULL); }
| LIMIT select_limit_value ',' select_offset_value | LIMIT select_limit_value ',' select_offset_value
/* Disabled because it was too confusing, bjm 2002-02-18 */ {
{ elog(ERROR, /* Disabled because it was too confusing, bjm 2002-02-18 */
"LIMIT #,# syntax not supported.\n\tUse separate LIMIT and OFFSET clauses."); } elog(ERROR,
"LIMIT #,# syntax not supported.\n\tUse separate LIMIT and OFFSET clauses.");
}
; ;
opt_select_limit: opt_select_limit:
select_limit { $$ = $1; } select_limit { $$ = $1; }
| /* EMPTY */ | /* EMPTY */
...@@ -4598,67 +4599,18 @@ opt_select_limit: ...@@ -4598,67 +4599,18 @@ opt_select_limit:
; ;
select_limit_value: select_limit_value:
Iconst a_expr { $$ = $1; }
{
Const *n = makeNode(Const);
if ($1 < 0)
elog(ERROR, "LIMIT must not be negative");
n->consttype = INT4OID;
n->constlen = sizeof(int4);
n->constvalue = Int32GetDatum($1);
n->constisnull = FALSE;
n->constbyval = TRUE;
$$ = (Node *)n;
}
| ALL | ALL
{ {
/* LIMIT ALL is represented as a NULL constant */ /* LIMIT ALL is represented as a NULL constant */
Const *n = makeNode(Const); A_Const *n = makeNode(A_Const);
n->val.type = T_Null;
n->consttype = INT4OID;
n->constlen = sizeof(int4);
n->constvalue = (Datum) 0;
n->constisnull = TRUE;
n->constbyval = TRUE;
$$ = (Node *)n;
}
| PARAM
{
Param *n = makeNode(Param);
n->paramkind = PARAM_NUM;
n->paramid = $1;
n->paramtype = INT4OID;
$$ = (Node *)n; $$ = (Node *)n;
} }
; ;
select_offset_value: select_offset_value:
Iconst a_expr { $$ = $1; }
{
Const *n = makeNode(Const);
if ($1 < 0)
elog(ERROR, "OFFSET must not be negative");
n->consttype = INT4OID;
n->constlen = sizeof(int4);
n->constvalue = Int32GetDatum($1);
n->constisnull = FALSE;
n->constbyval = TRUE;
$$ = (Node *)n;
}
| PARAM
{
Param *n = makeNode(Param);
n->paramkind = PARAM_NUM;
n->paramid = $1;
n->paramtype = INT4OID;
$$ = (Node *)n;
}
; ;
/* /*
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.116 2003/06/16 02:03:37 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.117 2003/07/03 19:07:45 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -317,10 +317,7 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j, ...@@ -317,10 +317,7 @@ transformJoinOnClause(ParseState *pstate, JoinExpr *j,
save_namespace = pstate->p_namespace; save_namespace = pstate->p_namespace;
pstate->p_namespace = makeList2(j->larg, j->rarg); pstate->p_namespace = makeList2(j->larg, j->rarg);
/* This part is just like transformWhereClause() */ result = transformWhereClause(pstate, j->quals, "JOIN/ON");
result = transformExpr(pstate, j->quals);
result = coerce_to_boolean(pstate, result, "JOIN/ON");
pstate->p_namespace = save_namespace; pstate->p_namespace = save_namespace;
...@@ -945,10 +942,38 @@ buildMergedJoinVar(ParseState *pstate, JoinType jointype, ...@@ -945,10 +942,38 @@ buildMergedJoinVar(ParseState *pstate, JoinType jointype,
/* /*
* transformWhereClause - * transformWhereClause -
* transforms the qualification and make sure it is of type Boolean * Transform the qualification and make sure it is of type boolean.
* Used for WHERE and allied clauses.
*
* constructName does not affect the semantics, but is used in error messages
*/
Node *
transformWhereClause(ParseState *pstate, Node *clause,
const char *constructName)
{
Node *qual;
if (clause == NULL)
return NULL;
qual = transformExpr(pstate, clause);
qual = coerce_to_boolean(pstate, qual, constructName);
return qual;
}
/*
* transformLimitClause -
* Transform the expression and make sure it is of type integer.
* Used for LIMIT and allied clauses.
*
* constructName does not affect the semantics, but is used in error messages
*/ */
Node * Node *
transformWhereClause(ParseState *pstate, Node *clause) transformLimitClause(ParseState *pstate, Node *clause,
const char *constructName)
{ {
Node *qual; Node *qual;
...@@ -957,7 +982,31 @@ transformWhereClause(ParseState *pstate, Node *clause) ...@@ -957,7 +982,31 @@ transformWhereClause(ParseState *pstate, Node *clause)
qual = transformExpr(pstate, clause); qual = transformExpr(pstate, clause);
qual = coerce_to_boolean(pstate, qual, "WHERE"); qual = coerce_to_integer(pstate, qual, constructName);
/*
* LIMIT can't refer to any vars or aggregates of the current query;
* we don't allow subselects either (though that case would at least
* be sensible)
*/
if (contain_vars_of_level(qual, 0))
{
/* translator: %s is name of a SQL construct, eg LIMIT */
elog(ERROR, "argument of %s must not contain variables",
constructName);
}
if (checkExprHasAggs(qual))
{
/* translator: %s is name of a SQL construct, eg LIMIT */
elog(ERROR, "argument of %s must not contain aggregates",
constructName);
}
if (contain_subplans(qual))
{
/* translator: %s is name of a SQL construct, eg LIMIT */
elog(ERROR, "argument of %s must not contain subselects",
constructName);
}
return qual; return qual;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.102 2003/07/01 19:10:53 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.103 2003/07/03 19:07:48 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -564,7 +564,7 @@ coerce_to_boolean(ParseState *pstate, Node *node, ...@@ -564,7 +564,7 @@ coerce_to_boolean(ParseState *pstate, Node *node,
if (node == NULL) if (node == NULL)
{ {
/* translator: first %s is name of a SQL construct, eg WHERE */ /* translator: first %s is name of a SQL construct, eg WHERE */
elog(ERROR, "Argument of %s must be type boolean, not type %s", elog(ERROR, "argument of %s must be type boolean, not type %s",
constructName, format_type_be(inputTypeId)); constructName, format_type_be(inputTypeId));
} }
} }
...@@ -572,7 +572,46 @@ coerce_to_boolean(ParseState *pstate, Node *node, ...@@ -572,7 +572,46 @@ coerce_to_boolean(ParseState *pstate, Node *node,
if (expression_returns_set(node)) if (expression_returns_set(node))
{ {
/* translator: %s is name of a SQL construct, eg WHERE */ /* translator: %s is name of a SQL construct, eg WHERE */
elog(ERROR, "Argument of %s must not be a set function", elog(ERROR, "argument of %s must not be a set function",
constructName);
}
return node;
}
/* coerce_to_integer()
* Coerce an argument of a construct that requires integer input
* (LIMIT, OFFSET, etc). Also check that input is not a set.
*
* Returns the possibly-transformed node tree.
*
* As with coerce_type, pstate may be NULL if no special unknown-Param
* processing is wanted.
*/
Node *
coerce_to_integer(ParseState *pstate, Node *node,
const char *constructName)
{
Oid inputTypeId = exprType(node);
if (inputTypeId != INT4OID)
{
node = coerce_to_target_type(pstate, node, inputTypeId,
INT4OID, -1,
COERCION_ASSIGNMENT,
COERCE_IMPLICIT_CAST);
if (node == NULL)
{
/* translator: first %s is name of a SQL construct, eg LIMIT */
elog(ERROR, "argument of %s must be type integer, not type %s",
constructName, format_type_be(inputTypeId));
}
}
if (expression_returns_set(node))
{
/* translator: %s is name of a SQL construct, eg LIMIT */
elog(ERROR, "argument of %s must not be a set function",
constructName); constructName);
} }
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: parse_clause.h,v 1.32 2003/06/16 02:03:38 tgl Exp $ * $Id: parse_clause.h,v 1.33 2003/07/03 19:07:53 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -21,7 +21,10 @@ extern int setTargetTable(ParseState *pstate, RangeVar *relation, ...@@ -21,7 +21,10 @@ extern int setTargetTable(ParseState *pstate, RangeVar *relation,
bool inh, bool alsoSource); bool inh, bool alsoSource);
extern bool interpretInhOption(InhOption inhOpt); extern bool interpretInhOption(InhOption inhOpt);
extern Node *transformWhereClause(ParseState *pstate, Node *where); extern Node *transformWhereClause(ParseState *pstate, Node *clause,
const char *constructName);
extern Node *transformLimitClause(ParseState *pstate, Node *clause,
const char *constructName);
extern List *transformGroupClause(ParseState *pstate, List *grouplist, extern List *transformGroupClause(ParseState *pstate, List *grouplist,
List *targetlist, List *sortClause); List *targetlist, List *sortClause);
extern List *transformSortClause(ParseState *pstate, List *orderlist, extern List *transformSortClause(ParseState *pstate, List *orderlist,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: parse_coerce.h,v 1.52 2003/07/01 19:10:53 tgl Exp $ * $Id: parse_coerce.h,v 1.53 2003/07/03 19:07:54 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -54,6 +54,8 @@ extern Node *coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId, ...@@ -54,6 +54,8 @@ extern Node *coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId,
extern Node *coerce_to_boolean(ParseState *pstate, Node *node, extern Node *coerce_to_boolean(ParseState *pstate, Node *node,
const char *constructName); const char *constructName);
extern Node *coerce_to_integer(ParseState *pstate, Node *node,
const char *constructName);
extern Oid select_common_type(List *typeids, const char *context); extern Oid select_common_type(List *typeids, const char *context);
extern Node *coerce_to_common_type(ParseState *pstate, Node *node, extern Node *coerce_to_common_type(ParseState *pstate, Node *node,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment