summaryrefslogtreecommitdiff
path: root/ndb/src/client/odbc/codegen/Code_select.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ndb/src/client/odbc/codegen/Code_select.cpp')
-rw-r--r--ndb/src/client/odbc/codegen/Code_select.cpp406
1 files changed, 406 insertions, 0 deletions
diff --git a/ndb/src/client/odbc/codegen/Code_select.cpp b/ndb/src/client/odbc/codegen/Code_select.cpp
new file mode 100644
index 00000000000..611b491968d
--- /dev/null
+++ b/ndb/src/client/odbc/codegen/Code_select.cpp
@@ -0,0 +1,406 @@
+/* Copyright (C) 2003 MySQL AB
+
+ This program 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 2 of the License, or
+ (at your option) any later version.
+
+ This program 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <algorithm>
+#include <common/StmtArea.hpp>
+#include <dictionary/DictTable.hpp>
+#include "Code_select.hpp"
+#include "Code_query_lookup.hpp"
+#include "Code_query_index.hpp"
+#include "Code_query_scan.hpp"
+#include "Code_query_range.hpp"
+#include "Code_query_sys.hpp"
+#include "Code_query_project.hpp"
+#include "Code_query_filter.hpp"
+#include "Code_query_join.hpp"
+#include "Code_query_count.hpp"
+#include "Code_query_sort.hpp"
+#include "Code_query_group.hpp"
+#include "Code_query_distinct.hpp"
+#include "Code_expr_column.hpp"
+#include "Code_expr_const.hpp"
+#include "Code_pred_op.hpp"
+#include "Code_root.hpp"
+
+Plan_select::~Plan_select()
+{
+}
+
+Plan_base*
+Plan_select::analyze(Ctx& ctx, Ctl& ctl)
+{
+ stmtArea().stmtInfo().setName(Stmt_name_select);
+ // analyze tables
+ ctx_assert(m_tableList != 0);
+ for (unsigned i = 1; i <= m_tableList->countTable(); i++) {
+ Plan_table* table = m_tableList->getTable(i);
+ table->analyze(ctx, ctl);
+ if (! ctx.ok())
+ return 0;
+ }
+ ctx_assert(m_exprRow != 0);
+ if (m_exprRow->getAsterisk()) {
+ // expand unqualified asterisk to table-qualified columns
+ setRow(new Plan_expr_row(m_root));
+ m_root->saveNode(m_exprRow);
+ for (unsigned i = 1; i <= m_tableList->countTable(); i++) {
+ const Plan_table* table = m_tableList->getTable(i);
+ const DictTable& dictTable = table->dictTable();
+ for (unsigned i = 1; i <= dictTable.getSize(); i++) {
+ DictColumn* dictColumn = dictTable.getColumn(i);
+ Plan_expr_column* column = new Plan_expr_column(m_root, dictColumn->getName());
+ m_root->saveNode(column);
+ column->setCname(table->getCname());
+ m_exprRow->addExpr(column);
+ }
+ }
+ }
+ // set name resolution scope
+ ctl.m_tableList = m_tableList->m_tableList;
+ ctx_assert(ctl.m_tableList.size() >= 1 + 1);
+ ctl.m_aggrin = false;
+ // analyze select row
+ ctl.m_aggrok = true;
+ ctx_assert(m_exprRow != 0);
+ m_exprRow = static_cast<Plan_expr_row*>(m_exprRow->analyze(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(m_exprRow != 0);
+ // analyze group by row
+ ctl.m_aggrok = false;
+ if (m_groupRow != 0) {
+ m_groupRow = static_cast<Plan_expr_row*>(m_groupRow->analyze(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(m_groupRow != 0);
+ }
+ // analyze having predicate
+ ctl.m_aggrok = true;
+ if (m_havingPred != 0) {
+ m_havingPred = static_cast<Plan_pred*>(m_havingPred->analyze(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(m_havingPred != 0);
+ }
+ // ana|yze order by row
+ ctl.m_aggrok = true;
+ if (m_sortRow != 0) {
+ m_sortRow = static_cast<Plan_expr_row*>(m_sortRow->analyze(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(m_sortRow != 0);
+ }
+ // analyze the predicate
+ ctl.m_aggrok = false;
+ ctl.m_topand = true;
+ ctl.m_extra = false;
+ if (m_pred != 0) {
+ m_pred = static_cast<Plan_pred*>(m_pred->analyze(ctx, ctl));
+ if (! ctx.ok())
+ return 0;
+ ctx_assert(m_pred != 0);
+ }
+ // check if group by required
+ if (m_exprRow->anyAggr() && ! m_exprRow->allBound() && m_groupRow == 0) {
+ ctx.pushStatus(Error::Gen, "missing GROUP BY clause");
+ return 0;
+ }
+ // in special cases add "group by 1"
+ if (m_groupRow == 0) {
+ bool addgb = false;
+ if (m_havingPred != 0) {
+ // allowed by oracle but nearly useless
+ addgb = true;
+ } else if (m_exprRow->anyAggr() && m_sortRow != 0) {
+ // allowed by oracle but useless
+ ctx_assert(m_exprRow->allBound());
+ addgb = true;
+ }
+ if (addgb) {
+ ctx_log2(("adding 'group by 1'"));
+ m_groupRow = new Plan_expr_row(m_root);
+ m_root->saveNode(m_groupRow);
+ LexType type(LexType::Integer);
+ Plan_expr* expr = new Plan_expr_const(m_root, type, "1");
+ m_root->saveNode(expr);
+ m_groupRow->addExpr(expr);
+ m_groupRow = static_cast<Plan_expr_row*>(m_groupRow->analyze(ctx, ctl));
+ ctx_assert(ctx.ok());
+ ctx_assert(m_groupRow != 0);
+ }
+ }
+ // check group by allowed
+ if (m_groupRow != 0) {
+ if (! m_exprRow->isAllGroupBy(m_groupRow)) {
+ ctx.pushStatus(Error::Gen, "invalid GROUP BY expression in SELECT list");
+ return 0;
+ }
+ if (m_havingPred != 0) {
+ if (! m_havingPred->isGroupBy(m_groupRow)) {
+ ctx.pushStatus(Error::Gen, "invalid GROUP BY expression in HAVING clause");
+ return 0;
+ }
+ }
+ if (m_sortRow != 0) {
+ if (! m_sortRow->isAllGroupBy(m_groupRow)) {
+ ctx.pushStatus(Error::Gen, "invalid GROUP BY expression in ORDER BY clause");
+ return 0;
+ }
+ }
+ }
+ // log top level predicate
+ {
+ unsigned n = 0;
+ for (PredList::iterator i = ctl.m_topcomp.begin(); i != ctl.m_topcomp.end(); i++)
+ ctx_log2(("top level pred %u: count tables = %u, not interp = %u",
+ ++n,
+ (unsigned)(*i)->tableSet().size(),
+ (unsigned)(*i)->noInterp().size()));
+ }
+ // compose the raw query from lookups and scans
+ Plan_query* queryRaw = 0;
+ TableVector tableVector(1);
+ TableSet tsDone;
+ while (tableVector.size() < ctl.m_tableList.size()) {
+ Plan_table* tableBest = 0;
+ Plan_table::Index* indexBest = 0;
+ for (unsigned n = 1; n < ctl.m_tableList.size(); n++) {
+ Plan_table* table = ctl.m_tableList[n];
+ if (tsDone.find(table) != tsDone.end())
+ continue;
+ // get system table out of the way
+ if (table->dictTable().sysId()) {
+ tableBest = table;
+ break;
+ }
+ // find best match for primary key or index
+ for (unsigned i = 0; i <= table->indexCount(); i++) {
+ Plan_table::Index& index = table->m_indexList[i];
+ table->resolveSet(ctx, index, tsDone);
+ if (! ctx.ok())
+ return 0;
+ if (! index.m_keyFound)
+ continue;
+ // prefer smaller dependency set, smaller rank, less unused keys
+ int k;
+ (k = (indexBest == 0)) ||
+ (k = (indexBest->m_keySet.size() - index.m_keySet.size())) ||
+ (k = (indexBest->m_rank - index.m_rank)) ||
+ (k = (indexBest->m_keyCountUnused - index.m_keyCountUnused));
+ if (k > 0) {
+ tableBest = table;
+ indexBest = &index;
+ }
+ }
+ }
+ Plan_query* queryNext = 0;
+ Plan_table* tableNext = 0;
+ Plan_query_scan* queryScan = 0; // for pushing interpreted program
+ Plan_query_range* queryRange = 0; // ditto
+ if (tableBest == 0) {
+ // scan first unprocessed table
+ for (unsigned n = 1; n < ctl.m_tableList.size(); n++) {
+ Plan_table* table = ctl.m_tableList[n];
+ if (tsDone.find(table) != tsDone.end())
+ continue;
+ tableNext = table;
+ break;
+ }
+ ctx_assert(tableNext != 0);
+ queryScan = new Plan_query_scan(m_root);
+ m_root->saveNode(queryScan);
+ queryScan->setTable(tableNext);
+ queryNext = queryScan;
+ ctx_log2(("optim: scan %s", tableNext->getPrintName()));
+ } else if (tableBest->dictTable().sysId()) {
+ // "scan" system table
+ tableNext = tableBest;
+ Plan_query_sys* querySys = new Plan_query_sys(m_root);
+ m_root->saveNode(querySys);
+ querySys->setTable(tableNext);
+ queryNext = querySys;
+ ctx_log2(("optim: scan %s", tableNext->getPrintName()));
+ } else if (indexBest->m_keySet.size() > 0) {
+ // scan first table this one depends on
+ const TableSet& keySet = indexBest->m_keySet;
+ for (unsigned n = 1; n < ctl.m_tableList.size(); n++) {
+ Plan_table* table = ctl.m_tableList[n];
+ if (keySet.find(table) == keySet.end())
+ continue;
+ ctx_assert(tsDone.find(table) == tsDone.end());
+ tableNext = table;
+ break;
+ }
+ ctx_assert(tableNext != 0);
+ queryScan = new Plan_query_scan(m_root);
+ m_root->saveNode(queryScan);
+ queryScan->setTable(tableNext);
+ queryNext = queryScan;
+ ctx_log2(("optim: scan %s for %s", tableNext->getPrintName(), tableBest->getPrintName()));
+ } else if (indexBest->m_rank == 0) {
+ // primary key depends only on processed tables
+ tableNext = tableBest;
+ Plan_query_lookup* queryLookup = new Plan_query_lookup(m_root);
+ m_root->saveNode(queryLookup);
+ queryLookup->setTable(tableNext);
+ queryNext = queryLookup;
+ ctx_log2(("optim: lookup %s", tableNext->getPrintName()));
+ } else if (indexBest->m_rank == 1) {
+ // hash index key depends only on processed tables
+ tableNext = tableBest;
+ Plan_query_index* queryIndex = new Plan_query_index(m_root);
+ m_root->saveNode(queryIndex);
+ queryIndex->setTable(tableNext, indexBest);
+ queryNext = queryIndex;
+ ctx_log2(("optim: lookup %s via index %s", tableNext->getPrintName(), indexBest->m_dictIndex->getName().c_str()));
+ } else if (indexBest->m_rank == 2) {
+ // ordered index key depends only on processed tables
+ tableNext = tableBest;
+ queryRange = new Plan_query_range(m_root);
+ m_root->saveNode(queryRange);
+ queryRange->setTable(tableNext, indexBest);
+ queryNext = queryRange;
+ ctx_log2(("optim: range scan %s via index %s", tableNext->getPrintName(), indexBest->m_dictIndex->getName().c_str()));
+ } else {
+ ctx_assert(false);
+ }
+ if (queryRaw == 0) {
+ queryRaw = queryNext;
+ } else {
+ Plan_query_join* queryJoin = new Plan_query_join(m_root);
+ m_root->saveNode(queryJoin);
+ queryJoin->setInner(queryRaw);
+ queryJoin->setOuter(queryNext);
+ queryRaw = queryJoin;
+ }
+ tableVector.push_back(tableNext);
+ tsDone.insert(tableNext);
+ // push down part of top level predicate to table scan or range scan
+ Plan_pred* predPush = 0;
+ Plan_pred* predInterp = 0;
+ PredList::iterator i = ctl.m_topcomp.begin();
+ while (i != ctl.m_topcomp.end()) {
+ const TableSet& ts = (*i)->tableSet();
+ if (! std::includes(tsDone.begin(), tsDone.end(), ts.begin(), ts.end())) {
+ i++;
+ continue;
+ }
+ predPush = predPush == 0 ? *i : predPush->opAnd(*i);
+ if (queryScan != 0) {
+ const TableSet& ts2 = (*i)->noInterp();
+ if (ts2.find(tableNext) == ts2.end())
+ predInterp = predInterp == 0 ? *i : predInterp->opAnd(*i);
+ }
+ if (queryRange != 0) {
+ const TableSet& ts2 = (*i)->noInterp();
+ if (ts2.find(tableNext) == ts2.end())
+ predInterp = predInterp == 0 ? *i : predInterp->opAnd(*i);
+ }
+ // remove it from top level predicate
+ PredList::iterator j = i;
+ i++;
+ ctl.m_topcomp.erase(j);
+ }
+ if (predPush != 0) {
+ Plan_query_filter* queryPush = new Plan_query_filter(m_root);
+ m_root->saveNode(queryPush);
+ queryPush->setQuery(queryRaw);
+ queryPush->setPred(predPush);
+ queryPush->m_topTable = tableNext;
+ queryRaw = queryPush;
+ }
+ if (predInterp != 0) {
+ if (queryScan != 0)
+ queryScan->setInterp(predInterp);
+ else if (queryRange != 0)
+ queryRange->setInterp(predInterp);
+ else
+ ctx_assert(false);
+ }
+ }
+ ctx_assert(ctl.m_topcomp.empty());
+ // set base for column position offsets
+ for (unsigned n = 1; n < tableVector.size(); n++) {
+ Plan_table* table = tableVector[n];
+ if (n == 1) {
+ table->m_resOff = 1;
+ } else {
+ Plan_table* tablePrev = tableVector[n - 1];
+ table->m_resOff = tablePrev->m_resOff + tablePrev->m_exprColumns.size() - 1;
+ }
+ }
+ // next level up is one of project, count, group by
+ Plan_query* queryTop;
+ if (m_groupRow == 0) {
+ if (! m_exprRow->anyAggr()) {
+ Plan_query_project* queryProject = new Plan_query_project(m_root);
+ m_root->saveNode(queryProject);
+ queryProject->setQuery(queryRaw);
+ queryProject->setRow(m_exprRow);
+ queryProject->setLimit(m_limitOff, m_limitCnt);
+ queryTop = queryProject;
+ } else {
+ ctx_assert(m_exprRow->allBound());
+ Plan_query_count* queryCount = new Plan_query_count(m_root);
+ m_root->saveNode(queryCount);
+ queryCount->setQuery(queryRaw);
+ queryCount->setRow(m_exprRow);
+ queryTop = queryCount;
+ }
+ } else {
+ Plan_query_group* queryGroup = new Plan_query_group(m_root);
+ m_root->saveNode(queryGroup);
+ queryGroup->setQuery(queryRaw);
+ queryGroup->setDataRow(m_exprRow);
+ queryGroup->setGroupRow(m_groupRow);
+ if (m_havingPred != 0)
+ queryGroup->setHavingPred(m_havingPred);
+ queryTop = queryGroup;
+ }
+ // optional sort becomes new top level
+ if (m_sortRow != 0) {
+ Plan_query_sort* querySort = new Plan_query_sort(m_root);
+ m_root->saveNode(querySort);
+ querySort->setQuery(queryTop);
+ querySort->setRow(m_sortRow);
+ queryTop = querySort;
+ }
+ // optional distinct becomes new top level
+ if (m_distinct) {
+ Plan_query_distinct* queryDistinct = new Plan_query_distinct(m_root);
+ m_root->saveNode(queryDistinct);
+ queryDistinct->setQuery(queryTop);
+ queryTop = queryDistinct;
+ }
+ // return top node
+ return queryTop;
+}
+
+Exec_base*
+Plan_select::codegen(Ctx& ctx, Ctl& ctl)
+{
+ ctx_assert(false);
+ return 0;
+}
+
+void
+Plan_select::print(Ctx& ctx)
+{
+ ctx.print(" [select");
+ Plan_base* a[] = { m_tableList, m_exprRow, m_pred, m_groupRow, m_havingPred };
+ printList(ctx, a, 5);
+ ctx.print("]");
+}