dbsql/src/cg_select.c

2974 lines
84 KiB
C
Raw Normal View History

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;
}