2007-03-10 19:04:07 +00:00
|
|
|
/*-
|
|
|
|
* DBSQL - A SQL database engine.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2007 The DBSQL Group, Inc. - All rights reserved.
|
|
|
|
*
|
2007-10-21 01:57:28 +00:00
|
|
|
* This library is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
2007-03-10 19:04:07 +00:00
|
|
|
*
|
|
|
|
* There are special exceptions to the terms and conditions of the GPL as it
|
|
|
|
* is applied to this software. View the full text of the exception in file
|
|
|
|
* LICENSE_EXCEPTIONS in the directory of this software distribution.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* General Public License for more details.
|
|
|
|
*
|
|
|
|
* $Id: cg_select.c 7 2007-02-03 13:34:17Z gburd $
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This file contains C code routines that are called by the parser
|
|
|
|
* to handle SELECT statements.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "dbsql_config.h"
|
|
|
|
#include "dbsql_int.h"
|
|
|
|
|
|
|
|
/* __select_new --
|
|
|
|
* Allocate a new Select structure and return a pointer to that
|
|
|
|
* structure.
|
|
|
|
*
|
|
|
|
* PUBLIC: select_t *__select_new __P((expr_list_t *, src_list_t *, expr_t *,
|
|
|
|
* PUBLIC: expr_list_t *, expr_t *, expr_list_t *,
|
|
|
|
* PUBLIC: int, int, int));
|
|
|
|
*
|
|
|
|
* result_cols Which columns to include in the result
|
|
|
|
* from_clause The FROM clause -- which tables to scan
|
|
|
|
* where_clause The WHERE clause
|
|
|
|
* groupby_clause The GROUP BY clause
|
|
|
|
* having_clause The HAVING clause
|
|
|
|
* orderby_clause The ORDER BY clause
|
|
|
|
* distinct_p True if the DISTINCT keyword is present
|
|
|
|
* limit LIMIT value, -1 means not used
|
|
|
|
* offset OFFSET value, 0 means no offset
|
|
|
|
*/
|
|
|
|
select_t *
|
|
|
|
__select_new(result_cols, from_clause, where_clause, groupby_clause,
|
|
|
|
having_clause, orderby_clause, distinct_p, limit, offset)
|
|
|
|
expr_list_t *result_cols;
|
|
|
|
src_list_t *from_clause;
|
|
|
|
expr_t *where_clause;
|
|
|
|
expr_list_t *groupby_clause;
|
|
|
|
expr_t *having_clause;
|
|
|
|
expr_list_t *orderby_clause;
|
|
|
|
int distinct_p;
|
|
|
|
int limit;
|
|
|
|
int offset;
|
|
|
|
{
|
|
|
|
select_t *new;
|
|
|
|
if (__dbsql_calloc(NULL, 1, sizeof(*new), &new) == ENOMEM) {
|
|
|
|
__expr_list_delete(result_cols);
|
|
|
|
__src_list_delete(from_clause);
|
|
|
|
__expr_delete(where_clause);
|
|
|
|
__expr_list_delete(groupby_clause);
|
|
|
|
__expr_delete(having_clause);
|
|
|
|
__expr_list_delete(orderby_clause);
|
|
|
|
} else {
|
|
|
|
if (result_cols == 0) {
|
|
|
|
result_cols = __expr_list_append(0,
|
|
|
|
__expr(TK_ALL, 0, 0, 0), 0);
|
|
|
|
}
|
|
|
|
new->pEList = result_cols;
|
|
|
|
new->pSrc = from_clause;
|
|
|
|
new->pWhere = where_clause;
|
|
|
|
new->pGroupBy = groupby_clause;
|
|
|
|
new->pHaving = having_clause;
|
|
|
|
new->pOrderBy = orderby_clause;
|
|
|
|
new->isDistinct = distinct_p;
|
|
|
|
new->op = TK_SELECT;
|
|
|
|
new->nLimit = limit;
|
|
|
|
new->nOffset = offset;
|
|
|
|
new->iLimit = -1;
|
|
|
|
new->iOffset = -1;
|
|
|
|
}
|
|
|
|
return new;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* __join_type
|
|
|
|
* Given 1 to 3 identifiers preceeding the JOIN keyword, determine the
|
|
|
|
* type of join. Return an integer constant that expresses that type
|
|
|
|
* in terms of the following bit values:
|
|
|
|
*
|
|
|
|
* JT_INNER
|
|
|
|
* JT_OUTER
|
|
|
|
* JT_NATURAL
|
|
|
|
* JT_LEFT
|
|
|
|
* JT_RIGHT
|
|
|
|
*
|
|
|
|
* A full outer join is the combination of JT_LEFT and JT_RIGHT.
|
|
|
|
*
|
|
|
|
* If an illegal or unsupported join type is seen, then still return
|
|
|
|
* a join type, but put an error in the parser structure.
|
|
|
|
*
|
|
|
|
* PUBLIC: int __join_type __P((parser_t *, token_t *, token_t *, token_t *));
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
__join_type(parser, a, b, c)
|
|
|
|
parser_t *parser;
|
|
|
|
token_t *a;
|
|
|
|
token_t *b;
|
|
|
|
token_t *c;
|
|
|
|
{
|
|
|
|
int jointype = 0;
|
|
|
|
token_t *ap_all[3];
|
|
|
|
token_t *p;
|
|
|
|
static struct {
|
|
|
|
const char *keyword;
|
|
|
|
int len;
|
|
|
|
int code;
|
|
|
|
} keywords[] = {
|
|
|
|
{ "natural", 7, JT_NATURAL },
|
|
|
|
{ "left", 4, JT_LEFT|JT_OUTER },
|
|
|
|
{ "right", 5, JT_RIGHT|JT_OUTER },
|
|
|
|
{ "full", 4, JT_LEFT|JT_RIGHT|JT_OUTER },
|
|
|
|
{ "outer", 5, JT_OUTER },
|
|
|
|
{ "inner", 5, JT_INNER },
|
|
|
|
{ "cross", 5, JT_INNER },
|
|
|
|
};
|
|
|
|
static int num_keywords = sizeof(keywords) / sizeof(keywords[0]);
|
|
|
|
static token_t dummy = { 0, 0 };
|
|
|
|
char *sp1 = " ", *sp2 = " ";
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
ap_all[0] = a;
|
|
|
|
ap_all[1] = b;
|
|
|
|
ap_all[2] = c;
|
|
|
|
for (i = 0; i < 3 && ap_all[i]; i++) {
|
|
|
|
p = ap_all[i];
|
|
|
|
for (j = 0; j < num_keywords; j++) {
|
|
|
|
if (p->n == keywords[j].len &&
|
|
|
|
strncasecmp(p->z, keywords[j].keyword,
|
|
|
|
p->n) == 0) {
|
|
|
|
jointype |= keywords[j].code;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (j >= num_keywords) {
|
|
|
|
jointype |= JT_ERROR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(((jointype & (JT_INNER | JT_OUTER)) == (JT_INNER | JT_OUTER)) ||
|
|
|
|
(jointype & JT_ERROR) != 0) {
|
|
|
|
if (b == 0) {
|
|
|
|
b = &dummy;
|
|
|
|
sp1 = 0;
|
|
|
|
}
|
|
|
|
if (c == 0) {
|
|
|
|
c = &dummy;
|
|
|
|
sp2 = 0;
|
|
|
|
}
|
|
|
|
__str_nappend(&parser->zErrMsg,
|
|
|
|
"unknown or unsupported join type: ", 0,
|
|
|
|
a->z, a->n, sp1, 1, b->z, b->n, sp2, 1, c->z,
|
|
|
|
c->n, NULL);
|
|
|
|
parser->nErr++;
|
|
|
|
jointype = JT_INNER;
|
|
|
|
} else if (jointype & JT_RIGHT) {
|
|
|
|
__error_msg(parser,
|
|
|
|
"RIGHT and FULL OUTER JOINs are not currently supported");
|
|
|
|
jointype = JT_INNER;
|
|
|
|
}
|
|
|
|
return jointype;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* __column_index --
|
|
|
|
* Return the index of a column in a table. Return -1 if the column
|
|
|
|
* is not contained in the table.
|
|
|
|
*
|
|
|
|
* STATIC: static int __column_index __P((table_t *, const char *));
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
__column_index(table, col)
|
|
|
|
table_t *table;
|
|
|
|
const char *col;
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < table->nCol; i++) {
|
|
|
|
if (strcasecmp(table->aCol[i].zName, col) == 0)
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* __add_where_term --
|
|
|
|
* Add a term to the WHERE expression in *expr that requires the
|
|
|
|
* col column to be equal in the two tables table1 and table2.
|
|
|
|
*
|
|
|
|
* STATIC: static void __add_where_term __P((const char *, const table_t *,
|
|
|
|
* STATIC: const table_t *, expr_t **));
|
|
|
|
*
|
|
|
|
* col Name of the column
|
|
|
|
* table1 First table
|
|
|
|
* table2 Second table
|
|
|
|
* expr Add the equality term to this expression
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
__add_where_term(col, table1, table2, expr)
|
|
|
|
const char *col;
|
|
|
|
const table_t *table1;
|
|
|
|
const table_t *table2;
|
|
|
|
expr_t **expr;
|
|
|
|
{
|
|
|
|
token_t dummy;
|
|
|
|
expr_t *e_1a, *e_1b, *e_1c;
|
|
|
|
expr_t *e_2a, *e_2b, *e_2c;
|
|
|
|
expr_t *e;
|
|
|
|
|
|
|
|
dummy.z = col;
|
|
|
|
dummy.n = strlen(col);
|
|
|
|
dummy.dyn = 0;
|
|
|
|
e_1a = __expr(TK_ID, 0, 0, &dummy);
|
|
|
|
e_2a = __expr(TK_ID, 0, 0, &dummy);
|
|
|
|
dummy.z = table1->zName;
|
|
|
|
dummy.n = strlen(dummy.z);
|
|
|
|
e_1b = __expr(TK_ID, 0, 0, &dummy);
|
|
|
|
dummy.z = table2->zName;
|
|
|
|
dummy.n = strlen(dummy.z);
|
|
|
|
e_2b = __expr(TK_ID, 0, 0, &dummy);
|
|
|
|
e_1c = __expr(TK_DOT, e_1b, e_1a, 0);
|
|
|
|
e_2c = __expr(TK_DOT, e_2b, e_2a, 0);
|
|
|
|
e = __expr(TK_EQ, e_1c, e_2c, 0);
|
|
|
|
ExprSetProperty(e, EP_FromJoin);
|
|
|
|
if (*expr) {
|
|
|
|
*expr = __expr(TK_AND, *expr, e, 0);
|
|
|
|
} else {
|
|
|
|
*expr = e;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* __set_join_expr --
|
|
|
|
* Set the EP_FromJoin property on all terms of the given expression.
|
|
|
|
*
|
|
|
|
* The EP_FromJoin property is used on terms of an expression to tell
|
|
|
|
* the LEFT OUTER JOIN processing logic that this term is part of the
|
|
|
|
* join restriction specified in the ON or USING clause and not a part
|
|
|
|
* of the more general WHERE clause. These terms are moved over to the
|
|
|
|
* WHERE clause during join processing but we need to remember that they
|
|
|
|
* originated in the ON or USING clause.
|
|
|
|
*
|
|
|
|
* STATIC: static void __set_join_expr __P((expr_t *));
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
__set_join_expr(p)
|
|
|
|
expr_t *p;
|
|
|
|
{
|
|
|
|
while (p) {
|
|
|
|
ExprSetProperty(p, EP_FromJoin);
|
|
|
|
__set_join_expr(p->pLeft);
|
|
|
|
p = p->pRight;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* __process_join --
|
|
|
|
* This routine processes the join information for a SELECT statement.
|
|
|
|
* ON and USING clauses are converted into extra terms of the WHERE
|
|
|
|
* clause. NATURAL joins also create extra WHERE clause terms.
|
|
|
|
*
|
|
|
|
* This routine returns the number of errors encountered.
|
|
|
|
*
|
|
|
|
* STATIC: static int __process_join __P((parser_t *, select_t *));
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
__process_join(parser, select)
|
|
|
|
parser_t *parser;
|
|
|
|
select_t *select;
|
|
|
|
{
|
|
|
|
int i, j;
|
|
|
|
src_list_t *src;
|
|
|
|
struct src_list_item *term;
|
|
|
|
struct src_list_item *other;
|
|
|
|
table_t *table;
|
|
|
|
id_list_t *list;
|
|
|
|
|
|
|
|
src = select->pSrc;
|
|
|
|
for (i = 0; i < (src->nSrc - 1); i++) {
|
|
|
|
term = &src->a[i];
|
|
|
|
other = &src->a[i+1];
|
|
|
|
|
|
|
|
if (term->pTab == 0 || other->pTab == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* When the NATURAL keyword is present, add WHERE clause
|
|
|
|
* terms for every column that the two tables have in common.
|
|
|
|
*/
|
|
|
|
if (term->jointype & JT_NATURAL) {
|
|
|
|
if (term->pOn || term->pUsing) {
|
|
|
|
__error_msg(parser,
|
|
|
|
"a NATURAL join may not have "
|
|
|
|
"an ON or USING clause", 0);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
table = term->pTab;
|
|
|
|
for (j = 0; j < table->nCol; j++) {
|
|
|
|
if (__column_index(other->pTab,
|
|
|
|
table->aCol[j].zName) >= 0) {
|
|
|
|
__add_where_term(table->aCol[j].zName,
|
|
|
|
table, other->pTab,
|
|
|
|
&select->pWhere);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Disallow both ON and USING clauses in the same join.
|
|
|
|
*/
|
|
|
|
if (term->pOn && term->pUsing) {
|
|
|
|
__error_msg(parser, "cannot have both ON and USING "
|
|
|
|
"clauses in the same join");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add the ON clause to the end of the WHERE clause,
|
|
|
|
* connected by and AND operator.
|
|
|
|
*/
|
|
|
|
if (term->pOn) {
|
|
|
|
__set_join_expr(term->pOn);
|
|
|
|
if (select->pWhere == 0) {
|
|
|
|
select->pWhere = term->pOn;
|
|
|
|
} else {
|
|
|
|
select->pWhere = __expr(TK_AND, select->pWhere,
|
|
|
|
term->pOn, 0);
|
|
|
|
}
|
|
|
|
term->pOn = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create extra terms on the WHERE clause for each column named
|
|
|
|
* in the USING clause. Example: If the two tables to be
|
|
|
|
* joined are A and B and the USING clause names X, Y, and Z,
|
|
|
|
* then add this to the WHERE clause:
|
|
|
|
* A.X=B.X AND A.Y=B.Y AND A.Z=B.Z
|
|
|
|
* Report an error if any column mentioned in the USING clause
|
|
|
|
* is not contained in both tables to be joined.
|
|
|
|
*/
|
|
|
|
if (term->pUsing) {
|
|
|
|
DBSQL_ASSERT(i < (src->nSrc - 1));
|
|
|
|
list = term->pUsing;
|
|
|
|
for (j = 0; j < list->nId; j++) {
|
|
|
|
if (__column_index(term->pTab,
|
|
|
|
list->a[j].zName) < 0 ||
|
|
|
|
__column_index(other->pTab,
|
|
|
|
list->a[j].zName) < 0) {
|
|
|
|
__error_msg(parser,
|
|
|
|
"cannot join using column %s - column "
|
|
|
|
"not present in both tables",
|
|
|
|
list->a[j].zName);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
__add_where_term(list->a[j].zName, term->pTab,
|
|
|
|
other->pTab, &select->pWhere);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* __select_delete --
|
|
|
|
* Delete the given Select structure and all of its substructures.
|
|
|
|
*
|
|
|
|
* PUBLIC: void __select_delete __P((select_t *));
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
__select_delete(select)
|
|
|
|
select_t *select;
|
|
|
|
{
|
|
|
|
if (select == 0)
|
|
|
|
return;
|
|
|
|
__expr_list_delete(select->pEList);
|
|
|
|
__src_list_delete(select->pSrc);
|
|
|
|
__expr_delete(select->pWhere);
|
|
|
|
__expr_list_delete(select->pGroupBy);
|
|
|
|
__expr_delete(select->pHaving);
|
|
|
|
__expr_list_delete(select->pOrderBy);
|
|
|
|
__select_delete(select->pPrior);
|
|
|
|
__dbsql_free(NULL, select->zSelect);
|
|
|
|
__dbsql_free(NULL, select);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* __aggregate_info_reset --
|
|
|
|
* Delete the aggregate information from the parse structure.
|
|
|
|
*
|
|
|
|
* STATIC: static void __aggregage_info_reset __P((parser_t *));
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
__aggregate_info_reset(parser)
|
|
|
|
parser_t *parser;
|
|
|
|
{
|
|
|
|
__dbsql_free(parser->db, parser->aAgg);
|
|
|
|
parser->aAgg = 0;
|
|
|
|
parser->nAgg = 0;
|
|
|
|
parser->useAgg = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* __push_onto_sorter --
|
|
|
|
* Insert code into "v" that will push the record on the top of the
|
|
|
|
* stack into the sorter.
|
|
|
|
*
|
|
|
|
* STATIC: static void __push_onto_sorter __P((parser_t *, vdbe_t *,
|
|
|
|
* STATIC: expr_list_t *));
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
__push_onto_sorter(parser, v, orderby_clause)
|
|
|
|
parser_t *parser;
|
|
|
|
vdbe_t *v;
|
|
|
|
expr_list_t *orderby_clause;
|
|
|
|
{
|
|
|
|
int i, order, type, c;
|
|
|
|
char *sort_order;
|
|
|
|
|
|
|
|
if (__dbsql_calloc(parser->db, 1, orderby_clause->nExpr + 1,
|
|
|
|
&sort_order) == ENOMEM)
|
|
|
|
return;
|
|
|
|
for (i = 0; i < orderby_clause->nExpr; i++) {
|
|
|
|
order = orderby_clause->a[i].sortOrder;
|
|
|
|
if ((order & DBSQL_SO_TYPEMASK) == DBSQL_SO_TEXT) {
|
|
|
|
type = DBSQL_SO_TEXT;
|
|
|
|
} else if ((order & DBSQL_SO_TYPEMASK) == DBSQL_SO_NUM) {
|
|
|
|
type = DBSQL_SO_NUM;
|
|
|
|
} else {
|
|
|
|
type = __expr_type(orderby_clause->a[i].pExpr);
|
|
|
|
}
|
|
|
|
if ((order & DBSQL_SO_DIRMASK) == DBSQL_SO_ASC) {
|
|
|
|
c = (type == DBSQL_SO_TEXT ? 'A' : '+');
|
|
|
|
} else {
|
|
|
|
c = (type == DBSQL_SO_TEXT ? 'D' : '-');
|
|
|
|
}
|
|
|
|
sort_order[i] = c;
|
|
|
|
__expr_code(parser, orderby_clause->a[i].pExpr);
|
|
|
|
}
|
|
|
|
sort_order[orderby_clause->nExpr] = 0;
|
|
|
|
__vdbe_add_op(v, OP_SortMakeKey, orderby_clause->nExpr, 0);
|
|
|
|
__vdbe_change_p3(v, -1, sort_order, strlen(sort_order));
|
|
|
|
__dbsql_free(parser->db, sort_order);
|
|
|
|
__vdbe_add_op(v, OP_SortPut, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* __add_key_type --
|
|
|
|
* This routine adds a P3 argument to the last VDBE opcode that was
|
|
|
|
* inserted. The P3 argument added is a string suitable for the
|
|
|
|
* OP_MakeKey or OP_MakeIdxKey opcodes. The string consists of
|
|
|
|
* characters 't' or 'n' depending on whether or not the various
|
|
|
|
* fields of the key to be generated should be treated as numeric
|
|
|
|
* or as text. See the OP_MakeKey and OP_MakeIdxKey opcode
|
|
|
|
* documentation for additional information about the P3 string.
|
|
|
|
* See also the __add_idx_key_type() routine.
|
|
|
|
*
|
|
|
|
* PUBLIC: void __add_key_type __P((vdbe_t *, expr_list_t *));
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
__add_key_type(v, elist)
|
|
|
|
vdbe_t *v;
|
|
|
|
expr_list_t *elist;
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int col = elist->nExpr;
|
|
|
|
char *type;
|
|
|
|
if (__dbsql_calloc(NULL, 1, col + 1, &type) == ENOMEM)
|
|
|
|
return;
|
|
|
|
for (i = 0; i < col; i++) {
|
|
|
|
type[i] = (__expr_type(elist->a[i].pExpr) == DBSQL_SO_NUM) ?
|
|
|
|
'n' : 't';
|
|
|
|
}
|
|
|
|
type[i] = 0;
|
|
|
|
__vdbe_change_p3(v, -1, type, col);
|
|
|
|
__dbsql_free(NULL, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* __select_inner_loop --
|
|
|
|
* This routine generates the code for the inside of the inner loop
|
|
|
|
* of a SELECT.
|
|
|
|
*
|
|
|
|
* If src_table and num_cols are both zero, then the elist expressions
|
|
|
|
* are evaluated in order to get the data for this row. If num_cols>0
|
|
|
|
* then data is pulled from src_table and elist is used only to get the
|
|
|
|
* datatypes for each column.
|
|
|
|
*
|
|
|
|
* STATIC: static int __select_inner_loop __P((parser_t *, select_t *,
|
|
|
|
* STATIC: expr_list_t *, int, int,
|
|
|
|
* STATIC: expr_list_t *, int, int, int, int,
|
|
|
|
* STATIC: int));
|
|
|
|
*
|
|
|
|
* parser The parser context
|
|
|
|
* select The complete select statement being coded
|
|
|
|
* elist List of values being extracted
|
|
|
|
* src_table Pull data from this table
|
|
|
|
* num_cols Number of columns in the source table
|
|
|
|
* orderby_clause If not NULL, sort results using this key
|
|
|
|
* distinct If >=0, make sure results are distinct
|
|
|
|
* dest How to dispose of the results
|
|
|
|
* param An argument to the disposal method
|
|
|
|
* cont Jump here to continue with next row
|
|
|
|
* brk Jump here to break out of the inner loop
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
__select_inner_loop(parser, select, elist, src_table, num_cols, orderby_clause,
|
|
|
|
distinct, dest, param, cont, brk)
|
|
|
|
parser_t *parser;
|
|
|
|
select_t *select;
|
|
|
|
expr_list_t *elist;
|
|
|
|
int src_table;
|
|
|
|
int num_cols;
|
|
|
|
expr_list_t *orderby_clause;
|
|
|
|
int distinct;
|
|
|
|
int dest;
|
|
|
|
int param;
|
|
|
|
int cont;
|
|
|
|
int brk;
|
|
|
|
{
|
|
|
|
int i, addr, addr1, addr2;
|
|
|
|
vdbe_t *v = parser->pVdbe;
|
|
|
|
|
|
|
|
if (v == 0)
|
|
|
|
return 0;
|
|
|
|
DBSQL_ASSERT(elist != 0);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If there was a LIMIT clause on the SELECT statement, then do the
|
|
|
|
* check to see if this row should be output.
|
|
|
|
*/
|
|
|
|
if (orderby_clause == 0) {
|
|
|
|
if (select->iOffset >= 0) {
|
|
|
|
int addr = __vdbe_current_addr(v);
|
|
|
|
__vdbe_add_op(v, OP_MemIncr, select->iOffset,
|
|
|
|
(addr + 2));
|
|
|
|
__vdbe_add_op(v, OP_Goto, 0, cont);
|
|
|
|
}
|
|
|
|
if (select->iLimit >= 0) {
|
|
|
|
__vdbe_add_op(v, OP_MemIncr, select->iLimit, brk);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Pull the requested columns.
|
|
|
|
*/
|
|
|
|
if (num_cols > 0) {
|
|
|
|
for (i = 0; i < num_cols; i++) {
|
|
|
|
__vdbe_add_op(v, OP_Column, src_table, i);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
num_cols = elist->nExpr;
|
|
|
|
for (i = 0; i < elist->nExpr; i++) {
|
|
|
|
__expr_code(parser, elist->a[i].pExpr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the DISTINCT keyword was present on the SELECT statement
|
|
|
|
* and this row has been seen before, then do not make this row
|
|
|
|
* part of the result.
|
|
|
|
*/
|
|
|
|
if (distinct >= 0 && elist && elist->nExpr > 0) {
|
|
|
|
#if NULL_ALWAYS_DISTINCT
|
|
|
|
__vdbe_add_op(v, OP_IsNull, (- elist->nExpr),
|
|
|
|
(__vdbe_current_addr(v) + 7));
|
|
|
|
#endif
|
|
|
|
__vdbe_add_op(v, OP_MakeKey, elist->nExpr, 1);
|
|
|
|
__add_key_type(v, elist);
|
|
|
|
__vdbe_add_op(v, OP_Distinct, distinct,
|
|
|
|
(__vdbe_current_addr(v) + 3));
|
|
|
|
__vdbe_add_op(v, OP_Pop, (elist->nExpr + 1), 0);
|
|
|
|
__vdbe_add_op(v, OP_Goto, 0, cont);
|
|
|
|
__vdbe_add_op(v, OP_String, 0, 0);
|
|
|
|
__vdbe_add_op(v, OP_PutStrKey, distinct, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch(dest) {
|
|
|
|
/*
|
|
|
|
* In this mode, write each query result to the key of the
|
|
|
|
* temporary table param.
|
|
|
|
*/
|
|
|
|
case SRT_Union:
|
|
|
|
__vdbe_add_op(v, OP_MakeRecord, num_cols,
|
|
|
|
NULL_ALWAYS_DISTINCT);
|
|
|
|
__vdbe_add_op(v, OP_String, 0, 0);
|
|
|
|
__vdbe_add_op(v, OP_PutStrKey, param, 0);
|
|
|
|
break;
|
|
|
|
/*
|
|
|
|
* Store the result as data using a unique key.
|
|
|
|
*/
|
|
|
|
case SRT_Table: /* FALLTHROUGH */
|
|
|
|
case SRT_TempTable:
|
|
|
|
__vdbe_add_op(v, OP_MakeRecord, num_cols, 0);
|
|
|
|
if (orderby_clause) {
|
|
|
|
__push_onto_sorter(parser, v, orderby_clause);
|
|
|
|
} else {
|
|
|
|
__vdbe_add_op(v, OP_NewRecno, param, 0);
|
|
|
|
__vdbe_add_op(v, OP_Pull, 1, 0);
|
|
|
|
__vdbe_add_op(v, OP_PutIntKey, param, 0);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Construct a record from the query result, but instead of
|
|
|
|
* saving that record, use it as a key to delete elements from
|
|
|
|
* the temporary table param.
|
|
|
|
*/
|
|
|
|
case SRT_Except:
|
|
|
|
addr = __vdbe_add_op(v, OP_MakeRecord, num_cols,
|
|
|
|
NULL_ALWAYS_DISTINCT);
|
|
|
|
__vdbe_add_op(v, OP_NotFound, param, (addr + 3));
|
|
|
|
__vdbe_add_op(v, OP_Delete, param, 0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we are creating a set for an "expr IN (SELECT ...)"
|
|
|
|
* construct, then there should be a single item on the
|
|
|
|
* stack. Write this item into the set table with bogus data.
|
|
|
|
*/
|
|
|
|
case SRT_Set:
|
|
|
|
addr1 = __vdbe_current_addr(v);
|
|
|
|
DBSQL_ASSERT(num_cols == 1);
|
|
|
|
__vdbe_add_op(v, OP_NotNull, -1, (addr1 + 3));
|
|
|
|
__vdbe_add_op(v, OP_Pop, 1, 0);
|
|
|
|
addr2 = __vdbe_add_op(v, OP_Goto, 0, 0);
|
|
|
|
if (orderby_clause) {
|
|
|
|
__push_onto_sorter(parser, v, orderby_clause);
|
|
|
|
} else {
|
|
|
|
__vdbe_add_op(v, OP_String, 0, 0);
|
|
|
|
__vdbe_add_op(v, OP_PutStrKey, param, 0);
|
|
|
|
}
|
|
|
|
__vdbe_change_p2(v, addr2, __vdbe_current_addr(v));
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If this is a scalar select that is part of an expression,
|
|
|
|
* then store the results in the appropriate memory cell and
|
|
|
|
* break out of the scan loop.
|
|
|
|
*/
|
|
|
|
case SRT_Mem:
|
|
|
|
DBSQL_ASSERT(num_cols == 1);
|
|
|
|
if (orderby_clause) {
|
|
|
|
__push_onto_sorter(parser, v, orderby_clause);
|
|
|
|
} else {
|
|
|
|
__vdbe_add_op(v, OP_MemStore, param, 1);
|
|
|
|
__vdbe_add_op(v, OP_Goto, 0, brk);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Send the data to the callback function.
|
|
|
|
*/
|
|
|
|
case SRT_Callback: /* FALLTHROUGH */
|
|
|
|
case SRT_Sorter:
|
|
|
|
if (orderby_clause) {
|
|
|
|
__vdbe_add_op(v, OP_SortMakeRec, num_cols, 0);
|
|
|
|
__push_onto_sorter(parser, v, orderby_clause);
|
|
|
|
} else {
|
|
|
|
DBSQL_ASSERT(dest == SRT_Callback);
|
|
|
|
__vdbe_add_op(v, OP_Callback, num_cols, 0);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Invoke a subroutine to handle the results. The subroutine
|
|
|
|
* itself is responsible for popping the results off of the
|
|
|
|
* stack.
|
|
|
|
*/
|
|
|
|
case SRT_Subroutine:
|
|
|
|
if (orderby_clause) {
|
|
|
|
__vdbe_add_op(v, OP_MakeRecord, num_cols, 0);
|
|
|
|
__push_onto_sorter(parser, v, orderby_clause);
|
|
|
|
} else {
|
|
|
|
__vdbe_add_op(v, OP_Gosub, 0, param);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Discard the results. This is used for SELECT statements
|
|
|
|
* inside the body of a TRIGGER. The purpose of such selects
|
|
|
|
* is to call user-defined functions that have side effects.
|
|
|
|
* We do not care about the actual results of the select.
|
|
|
|
*/
|
|
|
|
default:
|
|
|
|
DBSQL_ASSERT(dest == SRT_Discard);
|
|
|
|
__vdbe_add_op(v, OP_Pop, num_cols, 0);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* __generate_sort_tail --
|
|
|
|
* If the inner loop was generated using a non-null orderby_clause
|
|
|
|
* argument, then the results were placed in a sorter. After the
|
|
|
|
* loop is terminated we need to run the sorter and output the results.
|
|
|
|
* The following routine generates the code needed to do that.
|
|
|
|
*
|
|
|
|
* STATIC: static void __generate_sort_tail __P((select_t *, vdbe_t *, int,
|
|
|
|
* STATIC: int, int));
|
|
|
|
*
|
|
|
|
* select The SELECT statement
|
|
|
|
* v Generate code into this VDBE
|
|
|
|
* num_cols Number of columns of data
|
|
|
|
* dest Write the sorted results here
|
|
|
|
* param Optional parameter associated with dest
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
__generate_sort_tail(select, v, num_cols, dest, param)
|
|
|
|
select_t *select;
|
|
|
|
vdbe_t *v;
|
|
|
|
int num_cols;
|
|
|
|
int dest;
|
|
|
|
int param;
|
|
|
|
{
|
|
|
|
int i, addr;
|
|
|
|
int end = __vdbe_make_label(v);
|
|
|
|
if (dest == SRT_Sorter)
|
|
|
|
return;
|
|
|
|
__vdbe_add_op(v, OP_Sort, 0, 0);
|
|
|
|
addr = __vdbe_add_op(v, OP_SortNext, 0, end);
|
|
|
|
if (select->iOffset >= 0) {
|
|
|
|
__vdbe_add_op(v, OP_MemIncr, select->iOffset, (addr + 4));
|
|
|
|
__vdbe_add_op(v, OP_Pop, 1, 0);
|
|
|
|
__vdbe_add_op(v, OP_Goto, 0, addr);
|
|
|
|
}
|
|
|
|
if (select->iLimit >= 0) {
|
|
|
|
__vdbe_add_op(v, OP_MemIncr, select->iLimit, end);
|
|
|
|
}
|
|
|
|
switch(dest) {
|
|
|
|
case SRT_Callback:
|
|
|
|
__vdbe_add_op(v, OP_SortCallback, num_cols, 0);
|
|
|
|
break;
|
|
|
|
case SRT_Table: /* FALLTHROUGH */
|
|
|
|
case SRT_TempTable:
|
|
|
|
__vdbe_add_op(v, OP_NewRecno, param, 0);
|
|
|
|
__vdbe_add_op(v, OP_Pull, 1, 0);
|
|
|
|
__vdbe_add_op(v, OP_PutIntKey, param, 0);
|
|
|
|
break;
|
|
|
|
case SRT_Set:
|
|
|
|
DBSQL_ASSERT(num_cols == 1);
|
|
|
|
__vdbe_add_op(v, OP_NotNull, -1, (__vdbe_current_addr(v) + 3));
|
|
|
|
__vdbe_add_op(v, OP_Pop, 1, 0);
|
|
|
|
__vdbe_add_op(v, OP_Goto, 0, (__vdbe_current_addr(v) + 3));
|
|
|
|
__vdbe_add_op(v, OP_String, 0, 0);
|
|
|
|
__vdbe_add_op(v, OP_PutStrKey, param, 0);
|
|
|
|
break;
|
|
|
|
case SRT_Mem:
|
|
|
|
DBSQL_ASSERT(num_cols == 1);
|
|
|
|
__vdbe_add_op(v, OP_MemStore, param, 1);
|
|
|
|
__vdbe_add_op(v, OP_Goto, 0, end);
|
|
|
|
break;
|
|
|
|
case SRT_Subroutine:
|
|
|
|
for (i = 0; i < num_cols; i++) {
|
|
|
|
__vdbe_add_op(v, OP_Column, (-1 - i), i);
|
|
|
|
}
|
|
|
|
__vdbe_add_op(v, OP_Gosub, 0, param);
|
|
|
|
__vdbe_add_op(v, OP_Pop, 1, 0);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/* Do nothing. */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
__vdbe_add_op(v, OP_Goto, 0, addr);
|
|
|
|
__vdbe_resolve_label(v, end);
|
|
|
|
__vdbe_add_op(v, OP_SortReset, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* __generate_column_types --
|
|
|
|
* Generate code that will tell the VDBE the datatypes of
|
|
|
|
* columns in the result set.
|
|
|
|
*
|
|
|
|
* This routine only generates code if the "PRAGMA show_datatypes=on"
|
|
|
|
* has been executed. The datatypes are reported out in the azCol
|
|
|
|
* parameter to the callback function. The first N azCol[] entries
|
|
|
|
* are the names of the columns, and the second N entries are the
|
|
|
|
* datatypes for the columns.
|
|
|
|
*
|
|
|
|
* The "datatype" for a result that is a column of a type is the
|
|
|
|
* datatype definition extracted from the CREATE TABLE statement.
|
|
|
|
* The datatype for an expression is either TEXT or NUMERIC. The
|
|
|
|
* datatype for a ROWID field is INTEGER.
|
|
|
|
*
|
|
|
|
* STATIC: static void __generate_column_types __P((parser_t *, src_list_t *,
|
|
|
|
* STATIC: expr_list_t *));
|
|
|
|
*
|
|
|
|
* parser Parser context
|
|
|
|
* tables List of tables
|
|
|
|
* elist Expressions defining the result set
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
__generate_column_types(parser, tables, elist)
|
|
|
|
parser_t *parser;
|
|
|
|
src_list_t *tables;
|
|
|
|
expr_list_t *elist;
|
|
|
|
{
|
|
|
|
int i, j, col;
|
|
|
|
expr_t *p;
|
|
|
|
char *type;
|
|
|
|
table_t *table;
|
|
|
|
vdbe_t *v = parser->pVdbe;
|
|
|
|
if (parser->useCallback &&
|
|
|
|
(parser->db->flags & DBSQL_ReportTypes) == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (i = 0; i < elist->nExpr; i++) {
|
|
|
|
p = elist->a[i].pExpr;
|
|
|
|
type = 0;
|
|
|
|
if (p == 0)
|
|
|
|
continue;
|
|
|
|
if (p->op == TK_COLUMN && tables) {
|
|
|
|
col = p->iColumn;
|
|
|
|
j = 0;
|
|
|
|
while (j < tables->nSrc &&
|
|
|
|
tables->a[j].iCursor != p->iTable) {
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
DBSQL_ASSERT(j < tables->nSrc);
|
|
|
|
table = tables->a[j].pTab;
|
|
|
|
if (col < 0)
|
|
|
|
col = table->iPKey;
|
|
|
|
DBSQL_ASSERT(col == -1 || (col >= 0 && col < table->nCol));
|
|
|
|
if (col < 0) {
|
|
|
|
type = "INTEGER";
|
|
|
|
} else {
|
|
|
|
type = table->aCol[col].zType;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (__expr_type(p) == DBSQL_SO_TEXT) {
|
|
|
|
type = "TEXT";
|
|
|
|
} else {
|
|
|
|
type = "NUMERIC";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
__vdbe_add_op(v, OP_ColumnName, (i + elist->nExpr), 0);
|
|
|
|
__vdbe_change_p3(v, -1, type, P3_STATIC);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* __generate_column_names --
|
|
|
|
* Generate code that will tell the VDBE the names of columns
|
|
|
|
* in the result set. This information is used to provide the
|
|
|
|
* azCol[] values in the callback.
|
|
|
|
*
|
|
|
|
* STATIC: static void __generate_column_names __P((parser_t *, src_list_t *,
|
|
|
|
* STATIC: expr_list_t *));
|
|
|
|
*
|
|
|
|
* parser Parser context
|
|
|
|
* tables List of tables
|
|
|
|
* elist Expressions defining the result set
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
__generate_column_names(parser, tables, elist)
|
|
|
|
parser_t *parser;
|
|
|
|
src_list_t *tables;
|
|
|
|
expr_list_t *elist;
|
|
|
|
{
|
|
|
|
int i, j, show_full_names, icol, addr;
|
|
|
|
char *type, *name, *col, *tab;
|
|
|
|
char namebuf[30]; /* TODO consider eliminating this */
|
|
|
|
expr_t *p;
|
|
|
|
table_t *table;
|
|
|
|
vdbe_t *v = parser->pVdbe;
|
|
|
|
if (parser->colNamesSet || v == 0 || parser->rc == ENOMEM)
|
|
|
|
return;
|
|
|
|
parser->colNamesSet = 1;
|
|
|
|
for (i = 0; i < elist->nExpr; i++) {
|
|
|
|
type = 0;
|
|
|
|
p = elist->a[i].pExpr;
|
|
|
|
if (p == 0)
|
|
|
|
continue;
|
|
|
|
if (elist->a[i].zName) {
|
|
|
|
name = elist->a[i].zName;
|
|
|
|
__vdbe_add_op(v, OP_ColumnName, i, 0);
|
|
|
|
__vdbe_change_p3(v, -1, name, strlen(name));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
show_full_names = (parser->db->flags & DBSQL_FullColNames) !=0;
|
|
|
|
if (p->op == TK_COLUMN && tables) {
|
|
|
|
icol = p->iColumn;
|
|
|
|
j = 0;
|
|
|
|
while (j < tables->nSrc &&
|
|
|
|
tables->a[j].iCursor != p->iTable) {
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
DBSQL_ASSERT(j < tables->nSrc);
|
|
|
|
table = tables->a[j].pTab;
|
|
|
|
if (icol < 0)
|
|
|
|
icol = table->iPKey;
|
|
|
|
DBSQL_ASSERT(icol == -1 ||(icol >= 0 && icol < table->nCol));
|
|
|
|
if (icol < 0) {
|
|
|
|
col = "_ROWID_";
|
|
|
|
type = "INTEGER";
|
|
|
|
} else {
|
|
|
|
col = table->aCol[icol].zName;
|
|
|
|
type = table->aCol[icol].zType;
|
|
|
|
}
|
|
|
|
if (p->span.z && p->span.z[0] && !show_full_names) {
|
|
|
|
addr = __vdbe_add_op(v,OP_ColumnName, i, 0);
|
|
|
|
__vdbe_change_p3(v, -1, p->span.z, p->span.n);
|
|
|
|
__vdbe_compress_space(v, addr);
|
|
|
|
} else if (tables->nSrc > 1 || show_full_names) {
|
|
|
|
name = 0;
|
|
|
|
tab = 0;
|
|
|
|
tab = tables->a[j].zAlias;
|
|
|
|
if (show_full_names || tab == 0)
|
|
|
|
tab = table->zName;
|
|
|
|
__str_append(&name, tab, ".", col, 0);
|
|
|
|
__vdbe_add_op(v, OP_ColumnName, i, 0);
|
|
|
|
__vdbe_change_p3(v, -1, name, strlen(name));
|
|
|
|
__dbsql_free(NULL, name);
|
|
|
|
} else {
|
|
|
|
__vdbe_add_op(v, OP_ColumnName, i, 0);
|
|
|
|
__vdbe_change_p3(v, -1, col, 0);
|
|
|
|
}
|
|
|
|
} else if (p->span.z && p->span.z[0]) {
|
|
|
|
addr = __vdbe_add_op(v, OP_ColumnName, i, 0);
|
|
|
|
__vdbe_change_p3(v, -1, p->span.z, p->span.n);
|
|
|
|
__vdbe_compress_space(v, addr);
|
|
|
|
} else {
|
|
|
|
DBSQL_ASSERT( p->op!=TK_COLUMN || tables==0 );
|
|
|
|
sprintf(namebuf, "column%d", i+1);
|
|
|
|
__vdbe_add_op(v, OP_ColumnName, i, 0);
|
|
|
|
__vdbe_change_p3(v, -1, namebuf, strlen(namebuf));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* __select_op_name --
|
|
|
|
* Name of the connection operator, used for error messages.
|
|
|
|
*
|
|
|
|
* STATIC: static const char *__select_op_name __P((int));
|
|
|
|
*/
|
|
|
|
static const char *
|
|
|
|
__select_op_name(id)
|
|
|
|
int id;
|
|
|
|
{
|
|
|
|
char *z;
|
|
|
|
switch(id) {
|
|
|
|
case TK_ALL: z = "UNION ALL"; break;
|
|
|
|
case TK_INTERSECT: z = "INTERSECT"; break;
|
|
|
|
case TK_EXCEPT: z = "EXCEPT"; break;
|
|
|
|
default: z = "UNION"; break;
|
|
|
|
}
|
|
|
|
return z;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* __fill_in_column_list --
|
|
|
|
* For the given SELECT statement, do three things.
|
|
|
|
*
|
|
|
|
* (1) Fill in the tables->a[].pTab fields in the SrcList that
|
|
|
|
* defines the set of tables that should be scanned. For views,
|
|
|
|
* fill tables->a[].pSelect with a copy of the SELECT statement
|
|
|
|
* that implements the view. A copy is made of the view's SELECT
|
|
|
|
* statement so that we can freely modify or delete that statement
|
|
|
|
* without worrying about messing up the presistent representation
|
|
|
|
* of the view.
|
|
|
|
*
|
|
|
|
* (2) Add terms to the WHERE clause to accomodate the NATURAL keyword
|
|
|
|
* on joins and the ON and USING clause of joins.
|
|
|
|
*
|
|
|
|
* (3) Scan the list of columns in the result set (pEList) looking
|
|
|
|
* for instances of the "*" operator or the TABLE.* operator.
|
|
|
|
* If found, expand each "*" to be every column in every table
|
|
|
|
* and TABLE.* to be every column in TABLE.
|
|
|
|
*
|
|
|
|
* Return 0 on success. If there are problems, leave an error message
|
|
|
|
* in parser and return non-zero.
|
|
|
|
*
|
|
|
|
* STATIC: static int __file_in_column_list __P((parser_t *, select_t *));
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
__fill_in_column_list(parser, select)
|
|
|
|
parser_t *parser;
|
|
|
|
select_t *select;
|
|
|
|
{
|
|
|
|
int i, j, k, rc;
|
|
|
|
src_list_t *tables;
|
|
|
|
expr_list_t *elist;
|
|
|
|
table_t *table;
|
|
|
|
expr_t *e;
|
|
|
|
struct expr_list_item *a;
|
|
|
|
expr_list_t *new;
|
|
|
|
int table_seen;
|
|
|
|
token_t *name;
|
|
|
|
table_t *tab;
|
|
|
|
char *tab_name, *jname;
|
|
|
|
expr_t *expr, *left, *right;
|
|
|
|
char fake_name[60]; /* TODO consider replacing this */
|
|
|
|
|
|
|
|
if (select == 0 || select->pSrc == 0)
|
|
|
|
return 1;
|
|
|
|
tables = select->pSrc;
|
|
|
|
elist = select->pEList;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Look up every table in the table list.
|
|
|
|
*/
|
|
|
|
for(i = 0; i < tables->nSrc; i++) {
|
|
|
|
if (tables->a[i].pTab) {
|
|
|
|
/* This routine has run before! No need to continue. */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (tables->a[i].zName == 0) {
|
|
|
|
/* A sub-query in the FROM clause of a SELECT. */
|
|
|
|
DBSQL_ASSERT(tables->a[i].pSelect != 0);
|
|
|
|
if (tables->a[i].zAlias == 0) {
|
|
|
|
sprintf(fake_name, "___subquery_%p_",
|
|
|
|
(void*)tables->a[i].pSelect);
|
|
|
|
__str_append(&tables->a[i].zAlias,
|
|
|
|
fake_name, 0);
|
|
|
|
}
|
|
|
|
tables->a[i].pTab = table =
|
|
|
|
__select_result_set(parser,
|
|
|
|
tables->a[i].zAlias,
|
|
|
|
tables->a[i].pSelect);
|
|
|
|
if (table == 0) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* The isTransient flag indicates that the table_t
|
|
|
|
* structure has been dynamically allocated and may
|
|
|
|
* be freed at any time. In other words, table is
|
|
|
|
* not pointing to a persistent table structure that
|
|
|
|
* defines part of the schema.
|
|
|
|
*/
|
|
|
|
table->isTransient = 1;
|
|
|
|
} else {
|
|
|
|
/* An ordinary table or view name in the FROM clause.*/
|
|
|
|
tables->a[i].pTab = table =
|
|
|
|
__locate_table(parser, tables->a[i].zName,
|
|
|
|
tables->a[i].zDatabase);
|
|
|
|
if (table == 0) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (table->pSelect) {
|
|
|
|
/*
|
|
|
|
* We reach here if the named table is a
|
|
|
|
* really a view.
|
|
|
|
*/
|
|
|
|
if (__view_get_column_names(parser, table)) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* If tables->a[i].pSelect!=0 it means we are
|
|
|
|
* dealing with a view within a view. The
|
|
|
|
* SELECT structure has already been copied by
|
|
|
|
* the outer view so we can skip the copy step
|
|
|
|
* here in the inner view.
|
|
|
|
*/
|
|
|
|
if (tables->a[i].pSelect == 0) {
|
|
|
|
tables->a[i].pSelect =
|
|
|
|
__select_dup(table->pSelect);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Process NATURAL keywords, and ON and USING clauses of joins.
|
|
|
|
*/
|
|
|
|
if (__process_join(parser, select))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For every "*" that occurs in the column list, insert the names of
|
|
|
|
* all columns in all tables. And for every TABLE.* insert the names
|
|
|
|
* of all columns in TABLE. The parser inserted a special expression
|
|
|
|
* with the TK_ALL operator for each "*" that it found in the column
|
|
|
|
* list. The following code just has to locate the TK_ALL expressions
|
|
|
|
* and expand each one to the list of all columns in all tables.
|
|
|
|
*
|
|
|
|
* The first loop just checks to see if there are any "*" operators
|
|
|
|
* that need expanding.
|
|
|
|
*/
|
|
|
|
for (k = 0; k < elist->nExpr; k++) {
|
|
|
|
e = elist->a[k].pExpr;
|
|
|
|
if (e->op == TK_ALL)
|
|
|
|
break;
|
|
|
|
if (e->op == TK_DOT && e->pRight && e->pRight->op == TK_ALL
|
|
|
|
&& e->pLeft && e->pLeft->op == TK_ID)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
rc = 0;
|
|
|
|
if (k < elist->nExpr) {
|
|
|
|
/*
|
|
|
|
* If we get here it means the result set contains one or
|
|
|
|
* more "*" operators that need to be expanded. Loop through
|
|
|
|
* each expression in the result set and expand them one by
|
|
|
|
* one.
|
|
|
|
*/
|
|
|
|
a = elist->a;
|
|
|
|
new = 0;
|
|
|
|
for (k = 0; k < elist->nExpr; k++) {
|
|
|
|
e = a[k].pExpr;
|
|
|
|
if (e->op != TK_ALL && (e->op != TK_DOT ||
|
|
|
|
e->pRight == 0 ||
|
|
|
|
e->pRight->op != TK_ALL)) {
|
|
|
|
/*
|
|
|
|
* This particular expression does not need
|
|
|
|
* to be expanded.
|
|
|
|
*/
|
|
|
|
new = __expr_list_append(new, a[k].pExpr, 0);
|
|
|
|
new->a[new->nExpr-1].zName = a[k].zName;
|
|
|
|
a[k].pExpr = 0;
|
|
|
|
a[k].zName = 0;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* This expression is a "*" or a "TABLE.*" and
|
|
|
|
* needs to be expanded.
|
|
|
|
*/
|
|
|
|
/* 'name' is the text of name of TABLE */
|
|
|
|
table_seen = 0; /* 1 when TABLE matches */
|
|
|
|
if (e->op == TK_DOT && e->pLeft) {
|
|
|
|
name = &e->pLeft->token;
|
|
|
|
} else {
|
|
|
|
name = 0;
|
|
|
|
}
|
|
|
|
for (i = 0; i < tables->nSrc; i++) {
|
|
|
|
tab = tables->a[i].pTab;
|
|
|
|
tab_name = tables->a[i].zAlias;
|
|
|
|
if (tab_name==0 || tab_name[0]==0 ){
|
|
|
|
tab_name = tab->zName;
|
|
|
|
}
|
|
|
|
if (name &&
|
|
|
|
(tab_name == 0 ||
|
|
|
|
tab_name[0] == 0 ||
|
|
|
|
strncasecmp(name->z,
|
|
|
|
tab_name,
|
|
|
|
name->n) !=
|
|
|
|
0 || tab_name[name->n] != 0)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
table_seen = 1;
|
|
|
|
for (j = 0; j < tab->nCol; j++) {
|
|
|
|
jname = tab->aCol[j].zName;
|
|
|
|
|
|
|
|
if (i > 0 &&
|
|
|
|
(tables->a[i-1].jointype &
|
|
|
|
JT_NATURAL) != 0 &&
|
|
|
|
__column_index(
|
|
|
|
tables->a[i-1].pTab,
|
|
|
|
jname) >= 0) {
|
|
|
|
/*
|
|
|
|
* In a NATURAL join, omit
|
|
|
|
* the join columns from
|
|
|
|
* the table on the right.
|
|
|
|
*/
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if(i > 0 &&
|
|
|
|
__id_list_index(
|
|
|
|
tables->a[i-1].pUsing,
|
|
|
|
jname) >= 0) {
|
|
|
|
/*
|
|
|
|
* In a join with a USING
|
|
|
|
* clause, omit columns in
|
|
|
|
* the using clause from
|
|
|
|
* the table on the right.
|
|
|
|
*/
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
right = __expr(TK_ID, 0, 0, 0);
|
|
|
|
if (right == 0)
|
|
|
|
break;
|
|
|
|
right->token.z = jname;
|
|
|
|
right->token.n = strlen(jname);
|
|
|
|
right->token.dyn = 0;
|
|
|
|
if (tab_name && tables->nSrc > 1) {
|
|
|
|
left = __expr(TK_ID, 0,
|
|
|
|
0, 0);
|
|
|
|
expr = __expr(TK_DOT, left,
|
|
|
|
right, 0);
|
|
|
|
if (expr == 0)
|
|
|
|
break;
|
|
|
|
left->token.z = tab_name;
|
|
|
|
left->token.n =
|
|
|
|
strlen(tab_name);
|
|
|
|
left->token.dyn = 0;
|
|
|
|
__str_append((char**)&expr->span.z, tab_name, ".", jname, 0);
|
|
|
|
expr->span.n = strlen(expr->span.z);
|
|
|
|
expr->span.dyn = 1;
|
|
|
|
expr->token.z = 0;
|
|
|
|
expr->token.n = 0;
|
|
|
|
expr->token.dyn = 0;
|
|
|
|
} else {
|
|
|
|
expr = right;
|
|
|
|
expr->span = expr->token;
|
|
|
|
}
|
|
|
|
new = __expr_list_append(new, expr,
|
|
|
|
0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!table_seen) {
|
|
|
|
if (name) {
|
|
|
|
__error_msg(parser,
|
|
|
|
"no such table: %T",
|
|
|
|
name);
|
|
|
|
} else {
|
|
|
|
__error_msg(parser,
|
|
|
|
"no tables specified");
|
|
|
|
}
|
|
|
|
rc = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
__expr_list_delete(elist);
|
|
|
|
select->pEList = new;
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* __select_unbind --
|
|
|
|
* This routine recursively unlinks the select_t.pSrc.a[].pTab pointers
|
|
|
|
* in a select structure. It just sets the pointers to NULL. This
|
|
|
|
* routine is recursive in the sense that if the select_t.pSrc.a[].pSelect
|
|
|
|
* pointer is not NULL, this routine is called recursively on that
|
|
|
|
* pointer.
|
|
|
|
*
|
|
|
|
* This routine is called on the select_t structure that defines a
|
|
|
|
* VIEW in order to undo any bindings to tables. This is necessary
|
|
|
|
* because those tables might be DROPed by a subsequent SQL command.
|
|
|
|
* If the bindings are not removed, then the select_t.pSrc->a[].pTab
|
|
|
|
* field will be left pointing to a deallocated table_t structure after
|
|
|
|
* the DROP and a coredump will occur the next time the VIEW is used.
|
|
|
|
*
|
|
|
|
* PUBLIC: void __select_unbind __P((select_t *));
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
__select_unbind(select)
|
|
|
|
select_t *select;
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
table_t *table;
|
|
|
|
src_list_t *src = select->pSrc;
|
|
|
|
|
|
|
|
if (select == 0)
|
|
|
|
return;
|
|
|
|
for (i = 0; i < src->nSrc; i++) {
|
|
|
|
if ((table = src->a[i].pTab) != 0) {
|
|
|
|
if (table->isTransient) {
|
|
|
|
__vdbe_delete_table(0, table);
|
|
|
|
}
|
|
|
|
src->a[i].pTab = 0;
|
|
|
|
if (src->a[i].pSelect) {
|
|
|
|
__select_unbind(src->a[i].pSelect);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* __match_orderby_to_column --
|
|
|
|
* This routine associates entries in an ORDER BY expression list with
|
|
|
|
* columns in a result. For each ORDER BY expression, the opcode of
|
|
|
|
* the top-level node is changed to TK_COLUMN and the iColumn value of
|
|
|
|
* the top-level node is filled in with column number and the iTable
|
|
|
|
* value of the top-level node is filled with iTable parameter.
|
|
|
|
*
|
|
|
|
* If there are prior SELECT clauses, they are processed first. A match
|
|
|
|
* in an earlier SELECT takes precedence over a later SELECT.
|
|
|
|
*
|
|
|
|
* Any entry that does not match is flagged as an error. The number
|
|
|
|
* of errors is returned.
|
|
|
|
*
|
|
|
|
* This routine does NOT correctly initialize the expr_t.dataType field
|
|
|
|
* of the ORDER BY expressions. The __multi_select_sort_order() routine
|
|
|
|
* must be called to do that after the individual select statements
|
|
|
|
* have all been analyzed. This routine is unable to compute
|
|
|
|
* expr_t.dataType because it must be called before the individual
|
|
|
|
* select statements have been analyzed.
|
|
|
|
*
|
|
|
|
* STATIC: static int __match_orderby_to_column __P((parser_t *, select_t *,
|
|
|
|
* STATIC: expr_list_t *, int, int));
|
|
|
|
*
|
|
|
|
* parser A place to leave error messages
|
|
|
|
* select Match to result columns of this SELECT
|
|
|
|
* orderby_clause The ORDER BY values to match against columns
|
|
|
|
* table_idx Insert this value in table_idx
|
|
|
|
* must_complete_p If TRUE all ORDER BYs must match
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
__match_orderby_to_column(parser, select, orderby_clause, table_idx,
|
|
|
|
must_complete_p)
|
|
|
|
parser_t *parser;
|
|
|
|
select_t *select;
|
|
|
|
expr_list_t *orderby_clause;
|
|
|
|
int table_idx;
|
|
|
|
int must_complete_p;
|
|
|
|
{
|
|
|
|
int i, j, col;
|
|
|
|
int num_err = 0;
|
|
|
|
expr_list_t *elist;
|
|
|
|
expr_t *e;
|
|
|
|
char *name, *label;
|
|
|
|
|
|
|
|
if (select == 0 || orderby_clause == 0)
|
|
|
|
return 1;
|
|
|
|
if (must_complete_p) {
|
|
|
|
for (i = 0; i < orderby_clause->nExpr; i++) {
|
|
|
|
orderby_clause->a[i].done = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (__fill_in_column_list(parser, select)) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (select->pPrior) {
|
|
|
|
if (__match_orderby_to_column(parser, select->pPrior,
|
|
|
|
orderby_clause, table_idx, 0)) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
elist = select->pEList;
|
|
|
|
for (i = 0; i < orderby_clause->nExpr; i++) {
|
|
|
|
e = orderby_clause->a[i].pExpr;
|
|
|
|
col = -1;
|
|
|
|
if (orderby_clause->a[i].done)
|
|
|
|
continue;
|
|
|
|
if (__expr_is_integer(e, &col)) {
|
|
|
|
if (col <= 0 || col > elist->nExpr) {
|
|
|
|
__error_msg(parser, "ORDER BY position %d "
|
|
|
|
"should be between 1 and %d",
|
|
|
|
col, elist->nExpr);
|
|
|
|
num_err++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!must_complete_p)
|
|
|
|
continue;
|
|
|
|
col--;
|
|
|
|
}
|
|
|
|
for (j = 0; col < 0 && j < elist->nExpr; j++) {
|
|
|
|
if (elist->a[j].zName &&
|
|
|
|
(e->op == TK_ID || e->op == TK_STRING)) {
|
|
|
|
name = elist->a[j].zName;
|
|
|
|
DBSQL_ASSERT(e->token.z);
|
|
|
|
__dbsql_strndup(NULL, e->token.z, &label,
|
|
|
|
e->token.n);
|
|
|
|
__str_unquote(label);
|
|
|
|
if (strcasecmp(name, label) == 0) {
|
|
|
|
col = j;
|
|
|
|
}
|
|
|
|
__dbsql_free(NULL, label);
|
|
|
|
}
|
|
|
|
if (col < 0 && __expr_compare(e, elist->a[j].pExpr)) {
|
|
|
|
col = j;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (col >= 0) {
|
|
|
|
e->op = TK_COLUMN;
|
|
|
|
e->iColumn = col;
|
|
|
|
e->iTable = table_idx;
|
|
|
|
orderby_clause->a[i].done = 1;
|
|
|
|
}
|
|
|
|
if (col < 0 && must_complete_p) {
|
|
|
|
__error_msg(parser, "ORDER BY term number %d does not "
|
|
|
|
"match any result column", (i + 1));
|
|
|
|
num_err++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return num_err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* __parser_get_vdbe --
|
|
|
|
* Get a VDBE for the given parser context. Create a new one if
|
|
|
|
* necessary. If an error occurs, return NULL and leave a message
|
|
|
|
* in parser.
|
|
|
|
*
|
|
|
|
* PUBLIC: vdbe_t *__parser_get_vdbe __P((parser_t *));
|
|
|
|
*/
|
|
|
|
vdbe_t *
|
|
|
|
__parser_get_vdbe(parser)
|
|
|
|
parser_t *parser;
|
|
|
|
{
|
|
|
|
vdbe_t *v = parser->pVdbe;
|
|
|
|
if (v == 0) {
|
|
|
|
v = parser->pVdbe = __vdbe_create(parser->db);
|
|
|
|
}
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* __multi_select_sort_order --
|
|
|
|
* This routine sets the expr_t.dataType field on all elements of
|
|
|
|
* the orderby_clause expression list. The orderby_clause list will
|
|
|
|
* have been set up by __match_orderby_to_column(). Hence each
|
|
|
|
* expression has a TK_COLUMN as its root node. The expr_t.iColumn
|
|
|
|
* refers to a column in the result set. The datatype is set to
|
|
|
|
* DBSQL_SO_TEXT if the corresponding column in p and every SELECT to
|
|
|
|
* the left of 'select' has a datatype of DBSQL_SO_TEXT. If the
|
|
|
|
* cooressponding column in 'select' or any of the left SELECTs is
|
|
|
|
* DBSQL_SO_NUM, then the datatype of the order-by expression is set
|
|
|
|
* to DBSQL_SO_NUM.
|
|
|
|
*
|
|
|
|
* Examples:
|
|
|
|
*
|
|
|
|
* CREATE TABLE one(a INTEGER, b TEXT);
|
|
|
|
* CREATE TABLE two(c VARCHAR(5), d FLOAT);
|
|
|
|
*
|
|
|
|
* SELECT b, b FROM one UNION SELECT d, c FROM two ORDER BY 1, 2;
|
|
|
|
*
|
|
|
|
* The primary sort key will use DBSQL_SO_NUM because the "d" in
|
|
|
|
* the second SELECT is numeric. The 1st column of the first SELECT
|
|
|
|
* is text but that does not matter because a numeric always overrides
|
|
|
|
* a text.
|
|
|
|
*
|
|
|
|
* The secondary key will use the DBSQL_SO_TEXT sort order because
|
|
|
|
* both the (second) "b" in the first SELECT and the "c" in the second
|
|
|
|
* SELECT have a datatype of text.
|
|
|
|
*
|
|
|
|
* STATIC: static void __multi_select_sort_order __P((select_t *,
|
|
|
|
* STATIC: expr_list_t *));
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
__multi_select_sort_order(select, orderby_clause)
|
|
|
|
select_t *select;
|
|
|
|
expr_list_t *orderby_clause;
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
expr_list_t *elist;
|
|
|
|
expr_t *e;
|
|
|
|
|
|
|
|
if (orderby_clause == 0)
|
|
|
|
return;
|
|
|
|
if (select == 0) {
|
|
|
|
for (i = 0; i < orderby_clause->nExpr; i++) {
|
|
|
|
orderby_clause->a[i].pExpr->dataType = DBSQL_SO_TEXT;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
__multi_select_sort_order(select->pPrior, orderby_clause);
|
|
|
|
elist = select->pEList;
|
|
|
|
for (i = 0; i < orderby_clause->nExpr; i++) {
|
|
|
|
e = orderby_clause->a[i].pExpr;
|
|
|
|
if (e->dataType == DBSQL_SO_NUM)
|
|
|
|
continue;
|
|
|
|
DBSQL_ASSERT(e->iColumn >= 0);
|
|
|
|
if (elist->nExpr > e->iColumn) {
|
|
|
|
e->dataType = __expr_type(elist->a[e->iColumn].pExpr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* __compute_limit_registers --
|
|
|
|
* Compute the iLimit and iOffset fields of the SELECT based on the
|
|
|
|
* nLimit and nOffset fields. nLimit and nOffset hold the integers
|
|
|
|
* that appear in the original SQL statement after the LIMIT and OFFSET
|
|
|
|
* keywords. Or that hold -1 and 0 if those keywords are omitted.
|
|
|
|
* iLimit and iOffset are the integer memory register numbers for
|
|
|
|
* counters used to compute the limit and offset. If there is no
|
|
|
|
* limit and/or offset, then iLimit and iOffset are negative.
|
|
|
|
*
|
|
|
|
* This routine changes the values if iLimit and iOffset only if
|
|
|
|
* a limit or offset is defined by nLimit and nOffset. iLimit and
|
|
|
|
* iOffset should have been preset to appropriate default values
|
|
|
|
* (usually but not always -1) prior to calling this routine.
|
|
|
|
* Only if nLimit>=0 or nOffset>0 do the limit registers get
|
|
|
|
* redefined. The UNION ALL operator uses this property to force
|
|
|
|
* the reuse of the same limit and offset registers across multiple
|
|
|
|
* SELECT statements.
|
|
|
|
*
|
|
|
|
* STATIC: static void __compute_limit_registers __P((parser_t *, select_t *));
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
__compute_limit_registers(parser, select)
|
|
|
|
parser_t *parser;
|
|
|
|
select_t *select;
|
|
|
|
{
|
|
|
|
int mem;
|
|
|
|
vdbe_t *v;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the comparison is select->nLimit>0 then "LIMIT 0" shows
|
|
|
|
* all rows. It is the same as no limit. If the comparision is
|
|
|
|
* select->nLimit>=0 then "LIMIT 0" show no rows at all.
|
|
|
|
* "LIMIT -1" always shows all rows. There is some
|
|
|
|
* contraversy about what the correct behavior should be.
|
|
|
|
* The current implementation interprets "LIMIT 0" to mean
|
|
|
|
* no rows.
|
|
|
|
*/
|
|
|
|
if (select->nLimit >= 0) {
|
|
|
|
mem = parser->nMem++;
|
|
|
|
v = __parser_get_vdbe(parser);
|
|
|
|
if (v == 0)
|
|
|
|
return;
|
|
|
|
__vdbe_add_op(v, OP_Integer, (-select->nLimit), 0);
|
|
|
|
__vdbe_add_op(v, OP_MemStore, mem, 1);
|
|
|
|
select->iLimit = mem;
|
|
|
|
}
|
|
|
|
if (select->nOffset > 0) {
|
|
|
|
mem = parser->nMem++;
|
|
|
|
v = __parser_get_vdbe(parser);
|
|
|
|
if (v == 0)
|
|
|
|
return;
|
|
|
|
__vdbe_add_op(v, OP_Integer, (- select->nOffset), 0);
|
|
|
|
__vdbe_add_op(v, OP_MemStore, mem, 1);
|
|
|
|
select->iOffset = mem;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* __multi_select --
|
|
|
|
* This routine is called to process a query that is really the union
|
|
|
|
* or intersection of two or more separate queries.
|
|
|
|
*
|
|
|
|
* 'select' points to the right-most of the two queries. The query on the
|
|
|
|
* left is select->pPrior. The left query could also be a compound query
|
|
|
|
* in which case this routine will be called recursively.
|
|
|
|
*
|
|
|
|
* The results of the total query are to be written into a destination
|
|
|
|
* of type eDest with parameter iParm.
|
|
|
|
*
|
|
|
|
* Example 1: Consider a three-way compound SQL statement.
|
|
|
|
*
|
|
|
|
* SELECT a FROM t1 UNION SELECT b FROM t2 UNION SELECT c FROM t3
|
|
|
|
*
|
|
|
|
* This statement is parsed up as follows:
|
|
|
|
*
|
|
|
|
* SELECT c FROM t3
|
|
|
|
* |
|
|
|
|
* `-----> SELECT b FROM t2
|
|
|
|
* |
|
|
|
|
* `------> SELECT a FROM t1
|
|
|
|
*
|
|
|
|
* The arrows in the diagram above represent the select_t.pPrior pointer.
|
|
|
|
* So if this routine is called with p equal to the t3 query, then
|
|
|
|
* pPrior will be the t2 query. select->op will be TK_UNION in this case.
|
|
|
|
*
|
|
|
|
* Notice that because of the way we parse compound SELECTs, the
|
|
|
|
* individual selects always group from left to right.
|
|
|
|
*
|
|
|
|
* STATIC: static int __multi_select __P((parser_t *, select_t *, int, int));
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
__multi_select(parser, select, dest, param)
|
|
|
|
parser_t *parser;
|
|
|
|
select_t *select;
|
|
|
|
int dest;
|
|
|
|
int param;
|
|
|
|
{
|
|
|
|
int tab1, tab2;
|
|
|
|
int cont, brk, start;
|
|
|
|
int rc; /* Success code from a subroutine */
|
|
|
|
select_t *prior; /* Another SELECT immediately to our left */
|
|
|
|
vdbe_t *v; /* Generate code to this VDBE */
|
|
|
|
int union_tab; /* Cursor number of the temporary table
|
|
|
|
holding result */
|
|
|
|
int op; /* One of the SRT_ operations to apply to self */
|
|
|
|
int prior_op; /* The SRT_ operation to apply to prior selects */
|
|
|
|
int limit, offset; /* Saved values of select->nLimit and
|
|
|
|
select->nOffset */
|
|
|
|
expr_list_t *orderby_clause; /* The ORDER BY clause for the right
|
|
|
|
SELECT */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure there is no ORDER BY or LIMIT clause on prior SELECTs.
|
|
|
|
* Only the last SELECT in the series may have an ORDER BY or LIMIT.
|
|
|
|
*/
|
|
|
|
if (select == 0 || select->pPrior == 0)
|
|
|
|
return 1;
|
|
|
|
prior = select->pPrior;
|
|
|
|
if (prior->pOrderBy) {
|
|
|
|
__error_msg(parser,
|
|
|
|
"ORDER BY clause should come after %s not before",
|
|
|
|
__select_op_name(select->op));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (prior->nLimit >= 0 || prior->nOffset > 0) {
|
|
|
|
__error_msg(parser,
|
|
|
|
"LIMIT clause should come after %s not before",
|
|
|
|
__select_op_name(select->op));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure we have a valid query engine. If not, create a new one.
|
|
|
|
*/
|
|
|
|
v = __parser_get_vdbe(parser);
|
|
|
|
if (v == 0)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create the destination temporary table if necessary.
|
|
|
|
*/
|
|
|
|
if (dest == SRT_TempTable) {
|
|
|
|
__vdbe_add_op(v, OP_OpenTemp, param, 0);
|
|
|
|
dest = SRT_Table;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generate code for the left and right SELECT statements.
|
|
|
|
*/
|
|
|
|
switch(select->op) {
|
|
|
|
case TK_ALL:
|
|
|
|
if (select->pOrderBy == 0) {
|
|
|
|
prior->nLimit = select->nLimit;
|
|
|
|
prior->nOffset = select->nOffset;
|
|
|
|
rc = __select(parser, prior, dest, param, 0, 0, 0);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
select->pPrior = 0;
|
|
|
|
select->iLimit = prior->iLimit;
|
|
|
|
select->iOffset = prior->iOffset;
|
|
|
|
select->nLimit = -1;
|
|
|
|
select->nOffset = 0;
|
|
|
|
rc = __select(parser, select, dest, param, 0, 0, 0);
|
|
|
|
select->pPrior = prior;
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* For UNION ALL ... ORDER BY fall through to the next case. */
|
|
|
|
case TK_EXCEPT: /* FALLTHROUGH */
|
|
|
|
case TK_UNION:
|
|
|
|
prior_op = (select->op == TK_ALL) ? SRT_Table : SRT_Union;
|
|
|
|
if (dest == prior_op && select->pOrderBy == 0 &&
|
|
|
|
select->nLimit < 0 && select->nOffset == 0) {
|
|
|
|
/*
|
|
|
|
* We can reuse a temporary table generated by a
|
|
|
|
* SELECT to our right.
|
|
|
|
*/
|
|
|
|
union_tab = param;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* We will need to create our own temporary table
|
|
|
|
* to hold the intermediate results.
|
|
|
|
*/
|
|
|
|
union_tab = parser->nTab++;
|
|
|
|
if (select->pOrderBy &&
|
|
|
|
__match_orderby_to_column(parser, select,
|
|
|
|
select->pOrderBy,
|
|
|
|
union_tab, 1)) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (select->op != TK_ALL) {
|
|
|
|
__vdbe_add_op(v, OP_OpenTemp, union_tab, 1);
|
|
|
|
__vdbe_add_op(v, OP_KeyAsData, union_tab, 1);
|
|
|
|
} else {
|
|
|
|
__vdbe_add_op(v, OP_OpenTemp, union_tab, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Code the SELECT statements to our left.
|
|
|
|
*/
|
|
|
|
rc = __select(parser, prior, prior_op, union_tab, 0, 0, 0);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Code the current SELECT statement.
|
|
|
|
*/
|
|
|
|
switch(select->op) {
|
|
|
|
case TK_EXCEPT: op = SRT_Except; break;
|
|
|
|
case TK_UNION: op = SRT_Union; break;
|
|
|
|
case TK_ALL: op = SRT_Table; break;
|
|
|
|
}
|
|
|
|
select->pPrior = 0;
|
|
|
|
orderby_clause = select->pOrderBy;
|
|
|
|
select->pOrderBy = 0;
|
|
|
|
limit = select->nLimit;
|
|
|
|
select->nLimit = -1;
|
|
|
|
offset = select->nOffset;
|
|
|
|
select->nOffset = 0;
|
|
|
|
rc = __select(parser, select, op, union_tab, 0, 0, 0);
|
|
|
|
select->pPrior = prior;
|
|
|
|
select->pOrderBy = orderby_clause;
|
|
|
|
select->nLimit = limit;
|
|
|
|
select->nOffset = offset;
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert the data in the temporary table into whatever form
|
|
|
|
* it is that we currently need.
|
|
|
|
*/
|
|
|
|
if (dest != prior_op || union_tab != param) {
|
|
|
|
DBSQL_ASSERT(select->pEList);
|
|
|
|
if (dest == SRT_Callback) {
|
|
|
|
__generate_column_names(parser, 0,
|
|
|
|
select->pEList);
|
|
|
|
__generate_column_types(parser,
|
|
|
|
select->pSrc,
|
|
|
|
select->pEList);
|
|
|
|
}
|
|
|
|
brk = __vdbe_make_label(v);
|
|
|
|
cont = __vdbe_make_label(v);
|
|
|
|
__vdbe_add_op(v, OP_Rewind, union_tab, brk);
|
|
|
|
__compute_limit_registers(parser, select);
|
|
|
|
start = __vdbe_current_addr(v);
|
|
|
|
__multi_select_sort_order(select, select->pOrderBy);
|
|
|
|
rc = __select_inner_loop(parser, select,
|
|
|
|
select->pEList, union_tab,
|
|
|
|
select->pEList->nExpr,
|
|
|
|
select->pOrderBy, -1,
|
|
|
|
dest, param, cont, brk);
|
|
|
|
if (rc)
|
|
|
|
return 1;
|
|
|
|
__vdbe_resolve_label(v, cont);
|
|
|
|
__vdbe_add_op(v, OP_Next, union_tab, start);
|
|
|
|
__vdbe_resolve_label(v, brk);
|
|
|
|
__vdbe_add_op(v, OP_Close, union_tab, 0);
|
|
|
|
if (select->pOrderBy) {
|
|
|
|
__generate_sort_tail(select, v,
|
|
|
|
select->pEList->nExpr,
|
|
|
|
dest, param);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case TK_INTERSECT:
|
|
|
|
|
|
|
|
/*
|
|
|
|
* INTERSECT is different from the others since it requires
|
|
|
|
* two temporary tables. Hence it has its own case. Begin
|
|
|
|
* by allocating the tables we will need.
|
|
|
|
*/
|
|
|
|
tab1 = parser->nTab++;
|
|
|
|
tab2 = parser->nTab++;
|
|
|
|
if (select->pOrderBy &&
|
|
|
|
__match_orderby_to_column(parser, select,
|
|
|
|
select->pOrderBy, tab1, 1)) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
__vdbe_add_op(v, OP_OpenTemp, tab1, 1);
|
|
|
|
__vdbe_add_op(v, OP_KeyAsData, tab1, 1);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Code the SELECTs to our left into temporary table "tab1".
|
|
|
|
*/
|
|
|
|
rc = __select(parser, prior, SRT_Union, tab1, 0, 0, 0);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Code the current SELECT into temporary table "tab2".
|
|
|
|
*/
|
|
|
|
__vdbe_add_op(v, OP_OpenTemp, tab2, 1);
|
|
|
|
__vdbe_add_op(v, OP_KeyAsData, tab2, 1);
|
|
|
|
select->pPrior = 0;
|
|
|
|
limit = select->nLimit;
|
|
|
|
select->nLimit = -1;
|
|
|
|
offset = select->nOffset;
|
|
|
|
select->nOffset = 0;
|
|
|
|
rc = __select(parser, select, SRT_Union, tab2, 0, 0, 0);
|
|
|
|
select->pPrior = prior;
|
|
|
|
select->nLimit = limit;
|
|
|
|
select->nOffset = offset;
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generate code to take the intersection of the two temporary
|
|
|
|
* tables.
|
|
|
|
*/
|
|
|
|
DBSQL_ASSERT(select->pEList);
|
|
|
|
if (dest == SRT_Callback) {
|
|
|
|
__generate_column_names(parser, 0, select->pEList);
|
|
|
|
__generate_column_types(parser, select->pSrc,
|
|
|
|
select->pEList);
|
|
|
|
}
|
|
|
|
brk = __vdbe_make_label(v);
|
|
|
|
cont = __vdbe_make_label(v);
|
|
|
|
__vdbe_add_op(v, OP_Rewind, tab1, brk);
|
|
|
|
__compute_limit_registers(parser, select);
|
|
|
|
start = __vdbe_add_op(v, OP_FullKey, tab1, 0);
|
|
|
|
__vdbe_add_op(v, OP_NotFound, tab2, cont);
|
|
|
|
__multi_select_sort_order(select, select->pOrderBy);
|
|
|
|
rc = __select_inner_loop(parser, select, select->pEList,
|
|
|
|
tab1, select->pEList->nExpr,
|
|
|
|
select->pOrderBy, -1, dest, param,
|
|
|
|
cont, brk);
|
|
|
|
if (rc)
|
|
|
|
return 1;
|
|
|
|
__vdbe_resolve_label(v, cont);
|
|
|
|
__vdbe_add_op(v, OP_Next, tab1, start);
|
|
|
|
__vdbe_resolve_label(v, brk);
|
|
|
|
__vdbe_add_op(v, OP_Close, tab2, 0);
|
|
|
|
__vdbe_add_op(v, OP_Close, tab1, 0);
|
|
|
|
if (select->pOrderBy) {
|
|
|
|
__generate_sort_tail(select, v, select->pEList->nExpr,
|
|
|
|
dest, param);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
DBSQL_ASSERT(select->pEList && prior->pEList);
|
|
|
|
if (select->pEList->nExpr != prior->pEList->nExpr) {
|
|
|
|
__error_msg(parser, "SELECTs to the left and right of %s"
|
|
|
|
" do not have the same number of result columns",
|
|
|
|
__select_op_name(select->op));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Issue a null callback if that is what the user wants.
|
|
|
|
*/
|
|
|
|
if (dest == SRT_Callback &&
|
|
|
|
(parser->useCallback==0 ||
|
|
|
|
(parser->db->flags & DBSQL_NullCallback) != 0)) {
|
|
|
|
__vdbe_add_op(v, OP_NullCallback, select->pEList->nExpr, 0);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __subst_expr_list(expr_list_t*,int,expr_list_t*);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* __subst_expr --
|
|
|
|
* Scan through the expression expr. Replace every reference to
|
|
|
|
* a column in table number table with a copy of the iColumn-th
|
|
|
|
* entry in elist. (But leave references to the ROWID column
|
|
|
|
* unchanged.)
|
|
|
|
*
|
|
|
|
* This routine is part of the flattening procedure. A subquery
|
|
|
|
* whose result set is defined by pEList appears as entry in the
|
|
|
|
* FROM clause of a SELECT such that the VDBE cursor assigned to that
|
|
|
|
* FORM clause entry is iTable. This routine make the necessary
|
|
|
|
* changes to pExpr so that it refers directly to the source table
|
|
|
|
* of the subquery rather the result set of the subquery.
|
|
|
|
*
|
|
|
|
* STATIC: void __subst_expr __P((expr_t *, int, expr_list_t *));
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
__subst_expr(expr, table, elist)
|
|
|
|
expr_t *expr;
|
|
|
|
int table;
|
|
|
|
expr_list_t *elist;
|
|
|
|
{
|
|
|
|
expr_t *new;
|
|
|
|
|
|
|
|
if (expr == 0)
|
|
|
|
return;
|
|
|
|
if (expr->op == TK_COLUMN &&
|
|
|
|
expr->iTable == table &&
|
|
|
|
expr->iColumn >= 0) {
|
|
|
|
DBSQL_ASSERT(elist != 0 && expr->iColumn < elist->nExpr);
|
|
|
|
DBSQL_ASSERT(expr->pLeft == 0 &&
|
|
|
|
expr->pRight == 0 &&
|
|
|
|
expr->pList == 0);
|
|
|
|
new = elist->a[expr->iColumn].pExpr;
|
|
|
|
DBSQL_ASSERT(new != 0);
|
|
|
|
expr->op = new->op;
|
|
|
|
expr->dataType = new->dataType;
|
|
|
|
DBSQL_ASSERT(expr->pLeft == 0);
|
|
|
|
expr->pLeft = __expr_dup(new->pLeft);
|
|
|
|
DBSQL_ASSERT(expr->pRight == 0);
|
|
|
|
expr->pRight = __expr_dup(new->pRight);
|
|
|
|
DBSQL_ASSERT(expr->pList == 0);
|
|
|
|
expr->pList = __expr_list_dup(new->pList);
|
|
|
|
expr->iTable = new->iTable;
|
|
|
|
expr->iColumn = new->iColumn;
|
|
|
|
expr->iAgg = new->iAgg;
|
|
|
|
__token_copy(&expr->token, &new->token);
|
|
|
|
__token_copy(&expr->span, &new->span);
|
|
|
|
} else {
|
|
|
|
__subst_expr(expr->pLeft, table, elist);
|
|
|
|
__subst_expr(expr->pRight, table, elist);
|
|
|
|
__subst_expr_list(expr->pList, table, elist);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* __subst_expr_list --
|
|
|
|
*
|
|
|
|
* STATIC: static void __subst_expr_list __P((expr_list_t *, int,
|
|
|
|
* STATIC: expr_list_t *));
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
__subst_expr_list(list, table, elist)
|
|
|
|
expr_list_t *list;
|
|
|
|
int table;
|
|
|
|
expr_list_t *elist;
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
if (list == 0)
|
|
|
|
return;
|
|
|
|
for (i = 0; i < list->nExpr; i++) {
|
|
|
|
__subst_expr(list->a[i].pExpr, table, elist);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* __flatten_subquery --
|
|
|
|
* This routine attempts to flatten subqueries in order to speed
|
|
|
|
* execution. It returns 1 if it makes changes and 0 if no flattening
|
|
|
|
* occurs.
|
|
|
|
*
|
|
|
|
* To understand the concept of flattening, consider the following
|
|
|
|
* query:
|
|
|
|
*
|
|
|
|
* SELECT a FROM (SELECT x+y AS a FROM t1 WHERE z<100) WHERE a>5
|
|
|
|
*
|
|
|
|
* The default way of implementing this query is to execute the
|
|
|
|
* subquery first and store the results in a temporary table, then
|
|
|
|
* run the outer query on that temporary table. This requires two
|
|
|
|
* passes over the data. Furthermore, because the temporary table
|
|
|
|
* has no indices, the WHERE clause on the outer query cannot be
|
|
|
|
* optimized.
|
|
|
|
*
|
|
|
|
* This routine attempts to rewrite queries such as the above into
|
|
|
|
* a single flat select, like this:
|
|
|
|
*
|
|
|
|
* SELECT x+y AS a FROM t1 WHERE z<100 AND a>5
|
|
|
|
*
|
|
|
|
* The code generated for this simpification gives the same result
|
|
|
|
* but only has to scan the data once. And because indices might
|
|
|
|
* exist on the table t1, a complete scan of the data might be
|
|
|
|
* avoided.
|
|
|
|
*
|
|
|
|
* Flattening is only attempted if all of the following are true:
|
|
|
|
*
|
|
|
|
* (1) The subquery and the outer query do not both use aggregates.
|
|
|
|
*
|
|
|
|
* (2) The subquery is not an aggregate or the outer query is not a join.
|
|
|
|
*
|
|
|
|
* (3) The subquery is not the right operand of a left outer join, or
|
|
|
|
* the subquery is not itself a join. (Ticket #306)
|
|
|
|
*
|
|
|
|
* (4) The subquery is not DISTINCT or the outer query is not a join.
|
|
|
|
*
|
|
|
|
* (5) The subquery is not DISTINCT or the outer query does not use
|
|
|
|
* aggregates.
|
|
|
|
*
|
|
|
|
* (6) The subquery does not use aggregates or the outer query is not
|
|
|
|
* DISTINCT.
|
|
|
|
*
|
|
|
|
* (7) The subquery has a FROM clause.
|
|
|
|
*
|
|
|
|
* (8) The subquery does not use LIMIT or the outer query is not a join.
|
|
|
|
*
|
|
|
|
* (9) The subquery does not use LIMIT or the outer query does not use
|
|
|
|
* aggregates.
|
|
|
|
*
|
|
|
|
* (10) The subquery does not use aggregates or the outer query does not
|
|
|
|
* use LIMIT.
|
|
|
|
*
|
|
|
|
* (11) The subquery and the outer query do not both have ORDER BY clauses.
|
|
|
|
*
|
|
|
|
* (12) The subquery is not the right term of a LEFT OUTER JOIN or the
|
|
|
|
* subquery has no WHERE clause. (added by ticket #350)
|
|
|
|
*
|
|
|
|
* In this routine, the 'select' parameter is a pointer to the outer
|
|
|
|
* query. The subquery is p->pSrc->a[iFrom]. isAgg is true if the
|
|
|
|
* outer query uses aggregates and subqueryIsAgg is true if the subquery
|
|
|
|
* uses aggregates.
|
|
|
|
*
|
|
|
|
* If flattening is not attempted, this routine is a no-op and returns 0.
|
|
|
|
* If flattening is attempted this routine returns 1.
|
|
|
|
*
|
|
|
|
* All of the expression analysis must occur on both the outer query and
|
|
|
|
* the subquery before this routine runs.
|
|
|
|
*
|
|
|
|
* STATIC: static int __flatten_subquery __P((parser_t *, select_t *, int,
|
|
|
|
* STATIC: int, int));
|
|
|
|
*
|
|
|
|
* parser The parsing context
|
|
|
|
* select The parent or outer SELECT statement
|
|
|
|
* from Index in p->pSrc->a[] of the inner subquery
|
|
|
|
* agg_p True if outer SELECT uses aggregate functions
|
|
|
|
* subquery_agg_p True if the subquery uses aggregate functions
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
__flatten_subquery(parser, select, from, agg_p, subquery_agg_p)
|
|
|
|
parser_t *parser;
|
|
|
|
select_t *select;
|
|
|
|
int from;
|
|
|
|
int agg_p;
|
|
|
|
int subquery_agg_p;
|
|
|
|
{
|
|
|
|
select_t *sub_select; /* The inner query or "subquery" */
|
|
|
|
src_list_t *outer_from_clause; /* The FROM clause of the outer query */
|
|
|
|
src_list_t *sub_from_clause; /* The FROM clause of the subquery */
|
|
|
|
expr_list_t *outer_query_results;/* The result set of the outer query*/
|
|
|
|
int parent; /* VDBE cursor number of the pSub
|
|
|
|
result set temp table */
|
|
|
|
int i, nsub_src, jointype, extra;
|
|
|
|
expr_t *where, *expr, *having;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check to see if flattening is permitted. Return 0 if not.
|
|
|
|
*/
|
|
|
|
if (select == 0)
|
|
|
|
return 0;
|
|
|
|
outer_from_clause = select->pSrc;
|
|
|
|
DBSQL_ASSERT(outer_from_clause &&
|
|
|
|
from >= 0 &&
|
|
|
|
from < outer_from_clause->nSrc);
|
|
|
|
sub_select = outer_from_clause->a[from].pSelect;
|
|
|
|
DBSQL_ASSERT(sub_select != 0);
|
|
|
|
if (agg_p && subquery_agg_p)
|
|
|
|
return 0;
|
|
|
|
if (subquery_agg_p && outer_from_clause->nSrc > 1)
|
|
|
|
return 0;
|
|
|
|
sub_from_clause = sub_select->pSrc;
|
|
|
|
DBSQL_ASSERT(sub_from_clause);
|
|
|
|
if (sub_from_clause->nSrc == 0)
|
|
|
|
return 0;
|
|
|
|
if ((sub_select->isDistinct || sub_select->nLimit >= 0) &&
|
|
|
|
(outer_from_clause->nSrc > 1 || agg_p)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if ((select->isDistinct || select->nLimit >= 0)
|
|
|
|
&& subquery_agg_p )
|
|
|
|
return 0;
|
|
|
|
if (select->pOrderBy && sub_select->pOrderBy)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Restriction 3: If the subquery is a join, make sure the subquery
|
|
|
|
* is not used as the right operand of an outer join. Examples of
|
|
|
|
* why this is not allowed:
|
|
|
|
*
|
|
|
|
* t1 LEFT OUTER JOIN (t2 JOIN t3)
|
|
|
|
*
|
|
|
|
* If we flatten the above, we would get
|
|
|
|
*
|
|
|
|
* (t1 LEFT OUTER JOIN t2) JOIN t3
|
|
|
|
*
|
|
|
|
* which is not at all the same thing.
|
|
|
|
*/
|
|
|
|
if (sub_from_clause->nSrc > 1 && from > 0 &&
|
|
|
|
(outer_from_clause->a[from-1].jointype & JT_OUTER) != 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Restriction 12: If the subquery is the right operand of a left
|
|
|
|
* outer join, make sure the subquery has no WHERE clause.
|
|
|
|
* An examples of why this is not allowed:
|
|
|
|
*
|
|
|
|
* t1 LEFT OUTER JOIN (SELECT * FROM t2 WHERE t2.x>0)
|
|
|
|
*
|
|
|
|
* If we flatten the above, we would get
|
|
|
|
*
|
|
|
|
* (t1 LEFT OUTER JOIN t2) WHERE t2.x>0
|
|
|
|
*
|
|
|
|
* But the t2.x>0 test will always fail on a NULL row of t2, which
|
|
|
|
* effectively converts the OUTER JOIN into an INNER JOIN.
|
|
|
|
*/
|
|
|
|
if (from > 0 &&
|
|
|
|
(outer_from_clause->a[from-1].jointype & JT_OUTER) != 0 &&
|
|
|
|
sub_select->pWhere != 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we reach this point, it means flattening is permitted for the
|
|
|
|
* from-th entry of the FROM clause in the outer query.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Move all of the FROM elements of the subquery into the
|
|
|
|
* the FROM clause of the outer query. Before doing this, remember
|
|
|
|
* the cursor number for the original outer query FROM element in
|
|
|
|
* parent. The parent cursor will never be used. Subsequent code
|
|
|
|
* will scan expressions looking for parent references and replace
|
|
|
|
* those references with expressions that resolve to the subquery FROM
|
|
|
|
* elements we are now copying in.
|
|
|
|
*/
|
|
|
|
parent = outer_from_clause->a[from].iCursor;
|
|
|
|
nsub_src = sub_from_clause->nSrc;
|
|
|
|
jointype = outer_from_clause->a[from].jointype;
|
|
|
|
|
|
|
|
if (outer_from_clause->a[from].pTab &&
|
|
|
|
outer_from_clause->a[from].pTab->isTransient) {
|
|
|
|
__vdbe_delete_table(0, outer_from_clause->a[from].pTab);
|
|
|
|
}
|
|
|
|
__dbsql_free(NULL, outer_from_clause->a[from].zDatabase);
|
|
|
|
__dbsql_free(NULL, outer_from_clause->a[from].zName);
|
|
|
|
__dbsql_free(NULL, outer_from_clause->a[from].zAlias);
|
|
|
|
if (nsub_src > 1) {
|
|
|
|
extra = nsub_src - 1;
|
|
|
|
for (i = 1; i < nsub_src; i++) {
|
|
|
|
outer_from_clause =
|
|
|
|
__src_list_append(outer_from_clause, 0, 0);
|
|
|
|
}
|
|
|
|
select->pSrc = outer_from_clause;
|
|
|
|
for (i = (outer_from_clause->nSrc - 1);
|
|
|
|
(i - extra) >= from; i--) {
|
|
|
|
outer_from_clause->a[i] =outer_from_clause->a[i-extra];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for(i = 0; i < nsub_src; i++) {
|
|
|
|
outer_from_clause->a[i+from] = sub_from_clause->a[i];
|
|
|
|
memset(&sub_from_clause->a[i], 0,
|
|
|
|
sizeof(sub_from_clause->a[i]));
|
|
|
|
}
|
|
|
|
outer_from_clause->a[(from + nsub_src - 1)].jointype = jointype;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Now begin substituting subquery result set expressions for
|
|
|
|
* references to the parent in the outer query.
|
|
|
|
*
|
|
|
|
* Example:
|
|
|
|
*
|
|
|
|
*SELECT a+5, b*10 FROM (SELECT x*3 AS a, y+10 AS b FROM t1) WHERE a>b;
|
|
|
|
*\ \_____________ subquery __________/ /
|
|
|
|
* \_____________________ outer query ______________________________/
|
|
|
|
*
|
|
|
|
* We look at every expression in the outer query and every place we
|
|
|
|
* see "a" we substitute "x*3" and every place we see "b" we
|
|
|
|
* substitute "y+10".
|
|
|
|
*/
|
|
|
|
__subst_expr_list(select->pEList, parent, sub_select->pEList);
|
|
|
|
outer_query_results = select->pEList;
|
|
|
|
for(i = 0; i < outer_query_results->nExpr; i++) {
|
|
|
|
if (outer_query_results->a[i].zName == 0 &&
|
|
|
|
(expr = outer_query_results->a[i].pExpr)->span.z != 0) {
|
|
|
|
__dbsql_strndup(NULL, expr->span.z,
|
|
|
|
&outer_query_results->a[i].zName,
|
|
|
|
expr->span.n);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (agg_p) {
|
|
|
|
__subst_expr_list(select->pGroupBy, parent,
|
|
|
|
sub_select->pEList);
|
|
|
|
__subst_expr(select->pHaving, parent, sub_select->pEList);
|
|
|
|
}
|
|
|
|
if (sub_select->pOrderBy) {
|
|
|
|
DBSQL_ASSERT(select->pOrderBy == 0);
|
|
|
|
select->pOrderBy = sub_select->pOrderBy;
|
|
|
|
sub_select->pOrderBy = 0;
|
|
|
|
} else if (select->pOrderBy) {
|
|
|
|
__subst_expr_list(select->pOrderBy, parent,
|
|
|
|
sub_select->pEList);
|
|
|
|
}
|
|
|
|
if (sub_select->pWhere) {
|
|
|
|
where = __expr_dup(sub_select->pWhere);
|
|
|
|
} else {
|
|
|
|
where = 0;
|
|
|
|
}
|
|
|
|
if (subquery_agg_p) {
|
|
|
|
DBSQL_ASSERT(select->pHaving == 0);
|
|
|
|
select->pHaving = select->pWhere;
|
|
|
|
select->pWhere = where;
|
|
|
|
__subst_expr(select->pHaving, parent, sub_select->pEList);
|
|
|
|
if (sub_select->pHaving) {
|
|
|
|
having = __expr_dup(sub_select->pHaving);
|
|
|
|
if (select->pHaving) {
|
|
|
|
select->pHaving = __expr(TK_AND,
|
|
|
|
select->pHaving,
|
|
|
|
having, 0);
|
|
|
|
} else {
|
|
|
|
select->pHaving = having;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
DBSQL_ASSERT(select->pGroupBy == 0);
|
|
|
|
select->pGroupBy = __expr_list_dup(sub_select->pGroupBy);
|
|
|
|
} else if (select->pWhere == 0) {
|
|
|
|
select->pWhere = where;
|
|
|
|
} else {
|
|
|
|
__subst_expr(select->pWhere, parent, sub_select->pEList);
|
|
|
|
if (where) {
|
|
|
|
select->pWhere = __expr(TK_AND, select->pWhere,
|
|
|
|
where, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The flattened query is distinct if either the inner or the
|
|
|
|
* outer query is distinct.
|
|
|
|
*/
|
|
|
|
select->isDistinct = select->isDistinct || sub_select->isDistinct;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Transfer the limit expression from the subquery to the outer
|
|
|
|
* query.
|
|
|
|
*/
|
|
|
|
if (sub_select->nLimit >= 0) {
|
|
|
|
if (select->nLimit < 0) {
|
|
|
|
select->nLimit = sub_select->nLimit;
|
|
|
|
} else if ((select->nLimit + select->nOffset) >
|
|
|
|
(sub_select->nLimit + sub_select->nOffset)) {
|
|
|
|
select->nLimit = sub_select->nLimit +
|
|
|
|
sub_select->nOffset - select->nOffset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
select->nOffset += sub_select->nOffset;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Finially, delete what is left of the subquery and return success.
|
|
|
|
*/
|
|
|
|
__select_delete(sub_select);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* __min_max_query --
|
|
|
|
* Analyze the SELECT statement passed in as an argument to see if it
|
|
|
|
* is a simple min() or max() query. If it is and this query can be
|
|
|
|
* satisfied using a single seek to the beginning or end of an index,
|
|
|
|
* then generate the code for this SELECT and return 1. If this is not a
|
|
|
|
* simple min() or max() query, then return 0;
|
|
|
|
*
|
|
|
|
* A simply min() or max() query looks like this:
|
|
|
|
*
|
|
|
|
* SELECT min(a) FROM table;
|
|
|
|
* SELECT max(a) FROM table;
|
|
|
|
*
|
|
|
|
* The query may have only a single table in its FROM argument. There
|
|
|
|
* can be no GROUP BY or HAVING or WHERE clauses. The result set must
|
|
|
|
* be the min() or max() of a single column of the table. The column
|
|
|
|
* in the min() or max() function must be indexed.
|
|
|
|
*
|
|
|
|
* The parameters to this routine are the same as for __select().
|
|
|
|
* See the header comment on that routine for additional information.
|
|
|
|
*
|
|
|
|
* STATIC: static int __min_max_query __P((parser_t *, select_t *, int, int));
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
__min_max_query(parser, select, dest, param)
|
|
|
|
parser_t *parser;
|
|
|
|
select_t *select;
|
|
|
|
int dest;
|
|
|
|
int param;
|
|
|
|
{
|
|
|
|
expr_t *expr;
|
|
|
|
int col;
|
|
|
|
table_t *table;
|
|
|
|
index_t *index;
|
|
|
|
int base;
|
|
|
|
vdbe_t *v;
|
|
|
|
int seek_op;
|
|
|
|
int cont;
|
|
|
|
expr_list_t elist;
|
|
|
|
struct expr_list_item list_item;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check to see if this query is a simple min() or max() query. Return
|
|
|
|
* zero if it is not.
|
|
|
|
*/
|
|
|
|
if (select->pGroupBy || select->pHaving || select->pWhere)
|
|
|
|
return 0;
|
|
|
|
if (select->pSrc->nSrc != 1)
|
|
|
|
return 0;
|
|
|
|
if (select->pEList->nExpr != 1)
|
|
|
|
return 0;
|
|
|
|
expr = select->pEList->a[0].pExpr;
|
|
|
|
if (expr->op != TK_AGG_FUNCTION)
|
|
|
|
return 0;
|
|
|
|
if (expr->pList == 0 || expr->pList->nExpr != 1)
|
|
|
|
return 0;
|
|
|
|
if (expr->token.n != 3)
|
|
|
|
return 0;
|
|
|
|
if (strncasecmp(expr->token.z, "min", 3) == 0) {
|
|
|
|
seek_op = OP_Rewind;
|
|
|
|
} else if (strncasecmp(expr->token.z, "max", 3) == 0) {
|
|
|
|
seek_op = OP_Last;
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
expr = expr->pList->a[0].pExpr;
|
|
|
|
if (expr->op != TK_COLUMN)
|
|
|
|
return 0;
|
|
|
|
col = expr->iColumn;
|
|
|
|
table = select->pSrc->a[0].pTab;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we get to here, it means the query is of the correct form.
|
|
|
|
* Check to make sure we have an index and make index point to the
|
|
|
|
* appropriate index. If the min() or max() is on an INTEGER PRIMARY
|
|
|
|
* key column, no index is necessary so set index to NULL. If no
|
|
|
|
* usable index is found, return 0.
|
|
|
|
*/
|
|
|
|
if (col < 0) {
|
|
|
|
index = 0;
|
|
|
|
} else {
|
|
|
|
for (index = table->pIndex; index; index = index->pNext) {
|
|
|
|
DBSQL_ASSERT(index->nColumn >= 1);
|
|
|
|
if (index->aiColumn[0] == col)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (index == 0)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Identify column types if we will be using the callback. This
|
|
|
|
* step is skipped if the output is going to a table or a memory cell.
|
|
|
|
* The column names have already been generated in the calling
|
|
|
|
* function.
|
|
|
|
*/
|
|
|
|
v = __parser_get_vdbe(parser);
|
|
|
|
if (v == 0)
|
|
|
|
return 0;
|
|
|
|
if (dest == SRT_Callback) {
|
|
|
|
__generate_column_types(parser, select->pSrc, select->pEList);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the output is destined for a temporary table, open that table.
|
|
|
|
*/
|
|
|
|
if (dest == SRT_TempTable) {
|
|
|
|
__vdbe_add_op(v, OP_OpenTemp, param, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generating code to find the min or the max. Basically all we have
|
|
|
|
* to do is find the first or the last entry in the chosen index. If
|
|
|
|
* the min() or max() is on the INTEGER PRIMARY KEY, then find the
|
|
|
|
* first or last entry in the main table.
|
|
|
|
*/
|
|
|
|
__code_verify_schema(parser, table->iDb);
|
|
|
|
base = select->pSrc->a[0].iCursor;
|
|
|
|
__compute_limit_registers(parser, select);
|
|
|
|
__vdbe_add_op(v, OP_Integer, table->iDb, 0);
|
|
|
|
__vdbe_add_op(v, OP_OpenRead, base, table->tnum);
|
|
|
|
__vdbe_change_p3(v, -1, table->zName, P3_STATIC);
|
|
|
|
cont = __vdbe_make_label(v);
|
|
|
|
if (index == 0) {
|
|
|
|
__vdbe_add_op(v, seek_op, base, 0);
|
|
|
|
} else {
|
|
|
|
__vdbe_add_op(v, OP_Integer, index->iDb, 0);
|
|
|
|
__vdbe_add_op(v, OP_OpenRead, (base + 1), index->tnum);
|
|
|
|
__vdbe_change_p3(v, -1, index->zName, P3_STATIC);
|
|
|
|
__vdbe_add_op(v, seek_op, (base + 1), 0);
|
|
|
|
__vdbe_add_op(v, OP_IdxRecno, (base + 1), 0);
|
|
|
|
__vdbe_add_op(v, OP_Close, (base + 1), 0);
|
|
|
|
__vdbe_add_op(v, OP_MoveTo, base, 0);
|
|
|
|
}
|
|
|
|
elist.nExpr = 1;
|
|
|
|
memset(&list_item, 0, sizeof(list_item));
|
|
|
|
elist.a = &list_item;
|
|
|
|
elist.a[0].pExpr = expr;
|
|
|
|
__select_inner_loop(parser, select, &elist, 0, 0, 0, -1, dest, param,
|
|
|
|
cont, cont);
|
|
|
|
__vdbe_resolve_label(v, cont);
|
|
|
|
__vdbe_add_op(v, OP_Close, base, 0);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* __select --
|
|
|
|
* Generate code for the given SELECT statement.
|
|
|
|
*
|
|
|
|
* The results are distributed in various ways depending on the
|
|
|
|
* value of dest and param.
|
|
|
|
*
|
|
|
|
* dest Value Result
|
|
|
|
* ------------ -------------------------------------------
|
|
|
|
* SRT_Callback Invoke the callback for each row of the result.
|
|
|
|
*
|
|
|
|
* SRT_Mem Store first result in memory cell param
|
|
|
|
*
|
|
|
|
* SRT_Set Store results as keys of a table with cursor param
|
|
|
|
*
|
|
|
|
* SRT_Union Store results as a key in a temporary table param
|
|
|
|
*
|
|
|
|
* SRT_Except Remove results from the temporary table param.
|
|
|
|
*
|
|
|
|
* SRT_Table Store results in temporary table param
|
|
|
|
*
|
|
|
|
* NOTE: The table above is incomplete. Additional dist value have be
|
|
|
|
* added since this comment was written. See the __select_inner_loop()
|
|
|
|
* function for a complete listing of the allowed values of dest and
|
|
|
|
* their meanings.
|
|
|
|
*
|
|
|
|
* This routine returns the number of errors. If any errors are
|
|
|
|
* encountered, then an appropriate error message is left in
|
|
|
|
* parser->zErrMsg.
|
|
|
|
*
|
|
|
|
* This routine does NOT free the select_t structure passed in. The
|
|
|
|
* calling function needs to do that.
|
|
|
|
*
|
|
|
|
* The pParent, parentTab, and *pParentAgg fields are filled in if this
|
|
|
|
* SELECT is a subquery. This routine may try to combine this SELECT
|
|
|
|
* with its parent to form a single flat query. In so doing, it might
|
|
|
|
* change the parent query from a non-aggregate to an aggregate query.
|
|
|
|
* For that reason, the pParentAgg flag is passed as a pointer, so it
|
|
|
|
* can be changed.
|
|
|
|
*
|
|
|
|
* Example 1: The meaning of the pParent parameter.
|
|
|
|
*
|
|
|
|
* SELECT * FROM t1 JOIN (SELECT x, count(*) FROM t2) JOIN t3;
|
|
|
|
* \ \_______ subquery _______/ /
|
|
|
|
* \ /
|
|
|
|
* \____________________ outer query ___________________/
|
|
|
|
*
|
|
|
|
* This routine is called for the outer query first. For that call,
|
|
|
|
* pParent will be NULL. During the processing of the outer query, this
|
|
|
|
* routine is called recursively to handle the subquery. For the
|
|
|
|
* recursive call, pParent will point to the outer query. Because the
|
|
|
|
* subquery is the second element in a three-way join, the parentTab
|
|
|
|
* parameter will be 1 (the 2nd value of a 0-indexed array.)
|
|
|
|
*
|
|
|
|
* PUBLIC: int __select __P((parser_t *, select_t *, int, int, select_t *,
|
|
|
|
* PUBLIC: int, int *));
|
|
|
|
*
|
|
|
|
* parser The parser context
|
|
|
|
* select The SELECT statement being coded
|
|
|
|
* dest How to dispose of the results
|
|
|
|
* param A parameter used by the dest disposal method
|
|
|
|
* parent Another SELECT for which this is a sub-query
|
|
|
|
* parent_tab Index in parent->pSrc of this query
|
|
|
|
* parent_agg_p True if parent uses aggregate functions
|
|
|
|
*/
|
|
|
|
int __select(parser, select, dest, param, parent, parent_tab, parent_agg_p)
|
|
|
|
parser_t *parser;
|
|
|
|
select_t *select;
|
|
|
|
int dest;
|
|
|
|
int param;
|
|
|
|
select_t *parent;
|
|
|
|
int parent_tab;
|
|
|
|
int *parent_agg_p;
|
|
|
|
{
|
|
|
|
int i, j, col;
|
|
|
|
vdbe_t *v;
|
|
|
|
where_info_t *where_info;
|
|
|
|
int agg_p = 0; /* True for select lists like "count(*)" */
|
|
|
|
expr_list_t *elist; /* List of columns to extract. */
|
|
|
|
src_list_t *tables; /* List of tables to select from */
|
|
|
|
expr_t *where; /* The WHERE clause. May be NULL */
|
|
|
|
expr_list_t *orderby_clause;/* The ORDER BY clause. May be NULL */
|
|
|
|
expr_list_t *groupby_clause;/* The GROUP BY clause. May be NULL */
|
|
|
|
expr_t *having_clause; /* The HAVING clause. May be NULL */
|
|
|
|
int distinct_p; /* True if DISTINCT keyword is present */
|
|
|
|
int distinct; /* Table to use for the distinct set */
|
|
|
|
int rc = 1; /* Value to return from this function */
|
|
|
|
expr_t *e;
|
|
|
|
const char *saveauth_context;
|
|
|
|
int need_restore_context_p;
|
|
|
|
func_def_t *func;
|
|
|
|
int lbl1;
|
|
|
|
int endagg, startagg;
|
|
|
|
|
|
|
|
if (parser->rc == ENOMEM || parser->nErr || select == 0)
|
|
|
|
return 1;
|
|
|
|
if (__auth_check(parser, DBSQL_SELECT, 0, 0, 0))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If there is are a sequence of queries, do the earlier ones first.
|
|
|
|
*/
|
|
|
|
if (select->pPrior) {
|
|
|
|
return __multi_select(parser, select, dest, param);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make local copies of the parameters for this query.
|
|
|
|
*/
|
|
|
|
tables = select->pSrc;
|
|
|
|
where = select->pWhere;
|
|
|
|
orderby_clause = select->pOrderBy;
|
|
|
|
groupby_clause = select->pGroupBy;
|
|
|
|
having_clause = select->pHaving;
|
|
|
|
distinct_p = select->isDistinct;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate VDBE cursors for each table in the FROM clause.
|
|
|
|
*/
|
|
|
|
__src_list_assign_cursors(parser, tables);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do not even attempt to generate any code if we have already seen
|
|
|
|
* errors before this routine starts.
|
|
|
|
*/
|
|
|
|
if (parser->nErr > 0)
|
|
|
|
goto select_end;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Expand any "*" terms in the result set. (For example the "*" in
|
|
|
|
* "SELECT * FROM t1") The fill_in_column_list() routine also does
|
|
|
|
* some other housekeeping - see the header comment for details.
|
|
|
|
*/
|
|
|
|
if (__fill_in_column_list(parser, select)) {
|
|
|
|
goto select_end;
|
|
|
|
}
|
|
|
|
where = select->pWhere;
|
|
|
|
elist = select->pEList;
|
|
|
|
if (elist == 0)
|
|
|
|
goto select_end;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If writing to memory or generating a set
|
|
|
|
* only a single column may be output.
|
|
|
|
*/
|
|
|
|
if ((dest == SRT_Mem || dest == SRT_Set) && elist->nExpr > 1) {
|
|
|
|
__error_msg(parser, "only a single result allowed for "
|
|
|
|
"a SELECT that is part of an expression");
|
|
|
|
goto select_end;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ORDER BY is ignored for some destinations.
|
|
|
|
*/
|
|
|
|
switch(dest) {
|
|
|
|
case SRT_Union: /* FALLTHROUGH */
|
|
|
|
case SRT_Except: /* FALLTHROUGH */
|
|
|
|
case SRT_Discard:
|
|
|
|
orderby_clause = 0;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* At this point, we should have allocated all the cursors that we
|
|
|
|
* need to handle subquerys and temporary tables.
|
|
|
|
*
|
|
|
|
* Resolve the column names and do a semantics check on all the
|
|
|
|
* expressions.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < elist->nExpr; i++) {
|
|
|
|
if (__expr_resolve_ids(parser, tables, 0, elist->a[i].pExpr)) {
|
|
|
|
goto select_end;
|
|
|
|
}
|
|
|
|
if (__expr_check(parser, elist->a[i].pExpr, 1, &agg_p)) {
|
|
|
|
goto select_end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (where) {
|
|
|
|
if (__expr_resolve_ids(parser, tables, elist, where)) {
|
|
|
|
goto select_end;
|
|
|
|
}
|
|
|
|
if (__expr_check(parser, where, 0, 0)) {
|
|
|
|
goto select_end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (having_clause) {
|
|
|
|
if (groupby_clause == 0) {
|
|
|
|
__error_msg(parser,
|
|
|
|
"a GROUP BY clause is required before HAVING");
|
|
|
|
goto select_end;
|
|
|
|
}
|
|
|
|
if (__expr_resolve_ids(parser, tables, elist, having_clause)) {
|
|
|
|
goto select_end;
|
|
|
|
}
|
|
|
|
if (__expr_check(parser, having_clause, 1, &agg_p)) {
|
|
|
|
goto select_end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (orderby_clause) {
|
|
|
|
for (i = 0; i < orderby_clause->nExpr; i++) {
|
|
|
|
e = orderby_clause->a[i].pExpr;
|
|
|
|
if (__expr_is_integer(e, &col) && col > 0 &&
|
|
|
|
col <= elist->nExpr) {
|
|
|
|
__expr_delete(e);
|
|
|
|
e = orderby_clause->a[i].pExpr =
|
|
|
|
__expr_dup(elist->a[col-1].pExpr);
|
|
|
|
}
|
|
|
|
if (__expr_resolve_ids(parser, tables, elist, e)) {
|
|
|
|
goto select_end;
|
|
|
|
}
|
|
|
|
if (__expr_check(parser, e, agg_p, 0)) {
|
|
|
|
goto select_end;
|
|
|
|
}
|
|
|
|
if (__expr_is_constant(e)) {
|
|
|
|
if (__expr_is_integer(e, &col) == 0) {
|
|
|
|
__error_msg(parser,
|
|
|
|
"ORDER BY terms must not "
|
|
|
|
"be non-integer constants");
|
|
|
|
goto select_end;
|
|
|
|
} else if (col <= 0 || col > elist->nExpr) {
|
|
|
|
__error_msg(parser,
|
|
|
|
"ORDER BY column number "
|
|
|
|
"%d out of range - should "
|
|
|
|
"be between 1 and %d",
|
|
|
|
col, elist->nExpr);
|
|
|
|
goto select_end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (groupby_clause) {
|
|
|
|
for (i = 0; i < groupby_clause->nExpr; i++) {
|
|
|
|
e = groupby_clause->a[i].pExpr;
|
|
|
|
if (__expr_is_integer(e, &col) && col > 0 &&
|
|
|
|
col <= elist->nExpr) {
|
|
|
|
__expr_delete(e);
|
|
|
|
e = groupby_clause->a[i].pExpr =
|
|
|
|
__expr_dup(elist->a[col-1].pExpr);
|
|
|
|
}
|
|
|
|
if (__expr_resolve_ids(parser, tables, elist, e)) {
|
|
|
|
goto select_end;
|
|
|
|
}
|
|
|
|
if (__expr_check(parser, e, agg_p, 0)) {
|
|
|
|
goto select_end;
|
|
|
|
}
|
|
|
|
if (__expr_is_constant(e)) {
|
|
|
|
if (__expr_is_integer(e, &col) == 0) {
|
|
|
|
__error_msg(parser,
|
|
|
|
"GROUP BY terms must not "
|
|
|
|
"be non-integer constants");
|
|
|
|
goto select_end;
|
|
|
|
} else if (col <= 0 || col > elist->nExpr) {
|
|
|
|
__error_msg(parser,
|
|
|
|
"GROUP BY column number "
|
|
|
|
"%d out of range - should "
|
|
|
|
"be between 1 and %d",
|
|
|
|
col, elist->nExpr);
|
|
|
|
goto select_end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Begin generating code.
|
|
|
|
*/
|
|
|
|
v = __parser_get_vdbe(parser);
|
|
|
|
if (v == 0)
|
|
|
|
goto select_end;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Identify column names if we will be using them in a callback. This
|
|
|
|
* step is skipped if the output is going to some other destination.
|
|
|
|
*/
|
|
|
|
if (dest == SRT_Callback) {
|
|
|
|
__generate_column_names(parser, tables, elist);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check for the special case of a min() or max() function by itself
|
|
|
|
* in the result set.
|
|
|
|
*/
|
|
|
|
if (__min_max_query(parser, select, dest, param)) {
|
|
|
|
rc = 0;
|
|
|
|
goto select_end;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generate code for all sub-queries in the FROM clause
|
|
|
|
*/
|
|
|
|
for (i = 0; i < tables->nSrc; i++) {
|
|
|
|
if (tables->a[i].pSelect == 0)
|
|
|
|
continue;
|
|
|
|
if (tables->a[i].zName != 0) {
|
|
|
|
saveauth_context = parser->zAuthContext;
|
|
|
|
parser->zAuthContext = tables->a[i].zName;
|
|
|
|
need_restore_context_p = 1;
|
|
|
|
} else {
|
|
|
|
need_restore_context_p = 0;
|
|
|
|
}
|
|
|
|
__select(parser, tables->a[i].pSelect, SRT_TempTable,
|
|
|
|
tables->a[i].iCursor, select, i, &agg_p);
|
|
|
|
if (need_restore_context_p) {
|
|
|
|
parser->zAuthContext = saveauth_context;
|
|
|
|
}
|
|
|
|
tables = select->pSrc;
|
|
|
|
where = select->pWhere;
|
|
|
|
if (dest != SRT_Union && dest != SRT_Except &&
|
|
|
|
dest != SRT_Discard) {
|
|
|
|
orderby_clause = select->pOrderBy;
|
|
|
|
}
|
|
|
|
groupby_clause = select->pGroupBy;
|
|
|
|
having_clause = select->pHaving;
|
|
|
|
distinct_p = select->isDistinct;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check to see if this is a subquery that can be "flattened" into
|
|
|
|
* its parent. If flattening is a possiblity, do so and return
|
|
|
|
* immediately.
|
|
|
|
*/
|
|
|
|
if (parent && parent_agg_p &&
|
|
|
|
__flatten_subquery(parser, parent, parent_tab,
|
|
|
|
*parent_agg_p, agg_p)) {
|
|
|
|
if (agg_p)
|
|
|
|
*parent_agg_p = 1;
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the limiter.
|
|
|
|
*/
|
|
|
|
__compute_limit_registers(parser, select);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Identify column types if we will be using a callback. This
|
|
|
|
* step is skipped if the output is going to a destination other
|
|
|
|
* than a callback.
|
|
|
|
*
|
|
|
|
* We have to do this separately from the creation of column names
|
|
|
|
* above because if the tables contains views then they will not
|
|
|
|
* have been resolved and we will not know the column types until
|
|
|
|
* now.
|
|
|
|
*/
|
|
|
|
if (dest == SRT_Callback) {
|
|
|
|
__generate_column_types(parser, tables, elist);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the output is destined for a temporary table, open that table.
|
|
|
|
*/
|
|
|
|
if (dest == SRT_TempTable) {
|
|
|
|
__vdbe_add_op(v, OP_OpenTemp, param, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do an analysis of aggregate expressions.
|
|
|
|
*/
|
|
|
|
__aggregate_info_reset(parser);
|
|
|
|
if (agg_p || groupby_clause) {
|
|
|
|
DBSQL_ASSERT(parser->nAgg == 0);
|
|
|
|
agg_p = 1;
|
|
|
|
for(i = 0; i < elist->nExpr; i++) {
|
|
|
|
if (__expr_analyze_aggregates(parser,
|
|
|
|
elist->a[i].pExpr)) {
|
|
|
|
goto select_end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (groupby_clause) {
|
|
|
|
for (i = 0; i < groupby_clause->nExpr; i++) {
|
|
|
|
if (__expr_analyze_aggregates(parser,
|
|
|
|
groupby_clause->a[i].pExpr)) {
|
|
|
|
goto select_end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (having_clause && __expr_analyze_aggregates(parser,
|
|
|
|
having_clause)) {
|
|
|
|
goto select_end;
|
|
|
|
}
|
|
|
|
if (orderby_clause) {
|
|
|
|
for (i = 0; i < orderby_clause->nExpr; i++) {
|
|
|
|
if (__expr_analyze_aggregates(parser,
|
|
|
|
orderby_clause->a[i].pExpr)) {
|
|
|
|
goto select_end;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reset the aggregator.
|
|
|
|
*/
|
|
|
|
if (agg_p) {
|
|
|
|
__vdbe_add_op(v, OP_AggReset, 0, parser->nAgg);
|
|
|
|
for (i = 0; i < parser->nAgg; i++) {
|
|
|
|
if ((func = parser->aAgg[i].pFunc) != 0 &&
|
|
|
|
func->xFinalize != 0) {
|
|
|
|
__vdbe_add_op(v, OP_AggInit, 0, i);
|
|
|
|
__vdbe_change_p3(v, -1, (char*)func,
|
|
|
|
P3_POINTER);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (groupby_clause == 0) {
|
|
|
|
__vdbe_add_op(v, OP_String, 0, 0);
|
|
|
|
__vdbe_add_op(v, OP_AggFocus, 0, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize the memory cell to NULL.
|
|
|
|
*/
|
|
|
|
if (dest == SRT_Mem) {
|
|
|
|
__vdbe_add_op(v, OP_String, 0, 0);
|
|
|
|
__vdbe_add_op(v, OP_MemStore, param, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Open a temporary table to use for the distinct set.
|
|
|
|
*/
|
|
|
|
if (distinct_p) {
|
|
|
|
distinct = parser->nTab++;
|
|
|
|
__vdbe_add_op(v, OP_OpenTemp, distinct, 1);
|
|
|
|
} else {
|
|
|
|
distinct = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Begin the database scan.
|
|
|
|
*/
|
|
|
|
where_info = __where_begin(parser, tables, where, 0,
|
|
|
|
(groupby_clause ? 0 : &orderby_clause));
|
|
|
|
if (where_info == 0)
|
|
|
|
goto select_end;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Use the standard inner loop if we are not dealing with aggregates.
|
|
|
|
*/
|
|
|
|
if (!agg_p) {
|
|
|
|
if (__select_inner_loop(parser, select, elist, 0, 0,
|
|
|
|
orderby_clause, distinct, dest,
|
|
|
|
param, where_info->iContinue,
|
|
|
|
where_info->iBreak)) {
|
|
|
|
goto select_end;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* If we are dealing with aggregates, then do the special
|
|
|
|
* aggregate processing.
|
|
|
|
*/
|
|
|
|
if (groupby_clause) {
|
|
|
|
for (i = 0; i < groupby_clause->nExpr; i++) {
|
|
|
|
__expr_code(parser,
|
|
|
|
groupby_clause->a[i].pExpr);
|
|
|
|
}
|
|
|
|
__vdbe_add_op(v, OP_MakeKey, groupby_clause->nExpr, 0);
|
|
|
|
__add_key_type(v, groupby_clause);
|
|
|
|
lbl1 = __vdbe_make_label(v);
|
|
|
|
__vdbe_add_op(v, OP_AggFocus, 0, lbl1);
|
|
|
|
for (i = 0; i < parser->nAgg; i++) {
|
|
|
|
if (parser->aAgg[i].isAgg)
|
|
|
|
continue;
|
|
|
|
__expr_code(parser, parser->aAgg[i].pExpr);
|
|
|
|
__vdbe_add_op(v, OP_AggSet, 0, i);
|
|
|
|
}
|
|
|
|
__vdbe_resolve_label(v, lbl1);
|
|
|
|
}
|
|
|
|
for (i = 0; i < parser->nAgg; i++) {
|
|
|
|
if (!parser->aAgg[i].isAgg)
|
|
|
|
continue;
|
|
|
|
e = parser->aAgg[i].pExpr;
|
|
|
|
DBSQL_ASSERT(e->op == TK_AGG_FUNCTION);
|
|
|
|
if (e->pList) {
|
|
|
|
for(j = 0; j < e->pList->nExpr; j++) {
|
|
|
|
__expr_code(parser,
|
|
|
|
e->pList->a[j].pExpr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
__vdbe_add_op(v, OP_Integer, i, 0);
|
|
|
|
__vdbe_add_op(v, OP_AggFunc, 0,
|
|
|
|
(e->pList ? e->pList->nExpr : 0));
|
|
|
|
DBSQL_ASSERT(parser->aAgg[i].pFunc != 0);
|
|
|
|
DBSQL_ASSERT(parser->aAgg[i].pFunc->xStep != 0);
|
|
|
|
__vdbe_change_p3(v, -1, (char*)parser->aAgg[i].pFunc,
|
|
|
|
P3_POINTER);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* End the database scan loop.
|
|
|
|
*/
|
|
|
|
__where_end(where_info);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we are processing aggregates, we need to set up a second loop
|
|
|
|
* over all of the aggregate values and process them.
|
|
|
|
*/
|
|
|
|
if (agg_p) {
|
|
|
|
endagg = __vdbe_make_label(v);
|
|
|
|
startagg = __vdbe_add_op(v, OP_AggNext, 0, endagg);
|
|
|
|
parser->useAgg = 1;
|
|
|
|
if (having_clause) {
|
|
|
|
__expr_if_false(parser, having_clause, startagg, 1);
|
|
|
|
}
|
|
|
|
if (__select_inner_loop(parser, select, elist, 0, 0,
|
|
|
|
orderby_clause, distinct, dest,
|
|
|
|
param, startagg, endagg)) {
|
|
|
|
goto select_end;
|
|
|
|
}
|
|
|
|
__vdbe_add_op(v, OP_Goto, 0, startagg);
|
|
|
|
__vdbe_resolve_label(v, endagg);
|
|
|
|
__vdbe_add_op(v, OP_Noop, 0, 0);
|
|
|
|
parser->useAgg = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If there is an ORDER BY clause, then we need to sort the results
|
|
|
|
* and send them to the callback one by one.
|
|
|
|
*/
|
|
|
|
if (orderby_clause) {
|
|
|
|
__generate_sort_tail(select, v, elist->nExpr, dest, param);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Issue a null callback if that is what the user wants.
|
|
|
|
*/
|
|
|
|
if (dest == SRT_Callback &&
|
|
|
|
(parser->useCallback == 0 ||
|
|
|
|
(parser->db->flags & DBSQL_NullCallback) != 0)) {
|
|
|
|
__vdbe_add_op(v, OP_NullCallback, elist->nExpr, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The SELECT was successfully coded. Set the return code to 0
|
|
|
|
* to indicate no errors.
|
|
|
|
*/
|
|
|
|
rc = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Control jumps to here if an error is encountered above, or upon
|
|
|
|
* successful coding of the SELECT.
|
|
|
|
*/
|
|
|
|
select_end:
|
|
|
|
__aggregate_info_reset(parser);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* __select_result_set --
|
|
|
|
* Given a SELECT statement, generate a table_t structure that describes
|
|
|
|
* the result set of that SELECT.
|
|
|
|
*
|
|
|
|
* PUBLIC: table_t *__select_result_set __P((parser_t *, char *, select_t *));
|
|
|
|
*/
|
|
|
|
table_t *
|
|
|
|
__select_result_set(parser, tab_name, select)
|
|
|
|
parser_t *parser;
|
|
|
|
char *tab_name;
|
|
|
|
select_t *select;
|
|
|
|
{
|
|
|
|
int i, j, n, cnt;
|
|
|
|
table_t *table;
|
|
|
|
expr_list_t *elist;
|
|
|
|
column_t *column;
|
|
|
|
expr_t *p, *pr;
|
|
|
|
char buf[30]; /* TODO, consider replacing */
|
|
|
|
|
|
|
|
if (__fill_in_column_list(parser, select)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (__dbsql_calloc(NULL, 1, sizeof(table_t), &table) == ENOMEM) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
table->zName = 0;
|
|
|
|
if (tab_name)
|
|
|
|
if (__dbsql_strdup(NULL, tab_name, &table->zName) == ENOMEM) {
|
|
|
|
__dbsql_free(NULL, table);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
elist = select->pEList;
|
|
|
|
table->nCol = elist->nExpr;
|
|
|
|
DBSQL_ASSERT(table->nCol > 0);
|
|
|
|
__dbsql_calloc(NULL, table->nCol, sizeof(table->aCol[0]),
|
|
|
|
&table->aCol);
|
|
|
|
column = table->aCol;
|
|
|
|
for (i = 0; i < table->nCol; i++) {
|
|
|
|
if (elist->a[i].zName) {
|
|
|
|
__dbsql_strdup(NULL, elist->a[i].zName, &column[i].zName);
|
|
|
|
} else if ((p = elist->a[i].pExpr)->op == TK_DOT &&
|
|
|
|
(pr = p->pRight) != 0 &&
|
|
|
|
pr->token.z && pr->token.z[0]) {
|
|
|
|
__str_nappend(&column[i].zName, pr->token.z,
|
|
|
|
pr->token.n, NULL);
|
|
|
|
for (j = cnt = 0; j < i; j++) {
|
|
|
|
if (strcasecmp(column[j].zName,
|
|
|
|
column[i].zName) == 0) {
|
|
|
|
sprintf(buf, "_%d", ++cnt);
|
|
|
|
n = strlen(buf);
|
|
|
|
__str_nappend(&column[i].zName,
|
|
|
|
pr->token.z,
|
|
|
|
pr->token.n, buf,
|
|
|
|
n, NULL);
|
|
|
|
j = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (p->span.z && p->span.z[0]) {
|
|
|
|
__str_nappend(&table->aCol[i].zName, p->span.z,
|
|
|
|
p->span.n, NULL);
|
|
|
|
} else {
|
|
|
|
sprintf(buf, "column%d", i+1);
|
|
|
|
__dbsql_strdup(NULL, buf, &table->aCol[i].zName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
table->iPKey = -1;
|
|
|
|
return table;
|
|
|
|
}
|
|
|
|
|