Exemplo n.º 1
0
        public static ITable[] Execute(IDatabaseConnection connection, SqlQuery query)
        {
            // StatementTree caching

            // Substitute all parameter substitutions in the statement tree.
            // TODO: IExpressionPreparer preparer = new QueryPreparer(query);

            // Create a new parser and set the parameters...
            IEnumerable<Statement> statements;

            string commandText = query.Text;

            try {
                lock (SqlParser) {
                    SqlParser.ReInit(new StreamReader(new MemoryStream(Encoding.Unicode.GetBytes(commandText)), Encoding.Unicode));
                    SqlParser.Reset();
                    // Parse the statement.
                    statements = SqlParser.SequenceOfStatements();
                }
            } catch (ParseException e) {
                var tokens = SqlParser.token_source.tokenHistory;
                throw new SqlParseException(e, commandText, tokens);
            }

            List<ITable> results = new List<ITable>();
            foreach (Statement parsedStatement in statements) {
                var statement = parsedStatement;
                // TODO: statement = statement.PrepareExpressions(preparer);

                // Convert the StatementTree to a statement object
                statement.Query = query;

                DatabaseQueryContext context = new DatabaseQueryContext(connection);

                // Prepare the statement
                statement = statement.PrepareStatement(context);

                // Evaluate the SQL statement.
                results.Add(statement.Evaluate(context));
            }

            return results.ToArray();
        }
Exemplo n.º 2
0
        public static ITable All(this ITable table, IQueryContext context, Expression expression, Operator op, ITable other)
        {
            // Check the table only has 1 column
            if (other.TableInfo.ColumnCount != 1)
            {
                throw new ApplicationException("Input table <> 1 columns.");
            }

            // Handle trivial case of no entries to select from
            if (table.RowCount == 0)
            {
                return(table);
            }

            var theTable = table as Table;

            if (theTable == null)
            {
                return(table);
            }

            // If 'table' is empty then we return the complete set.  ALL { empty set }
            // is always true.
            if (other.RowCount == 0)
            {
                return(table);
            }

            // Is the lhs expression a constant?
            if (expression.IsConstant())
            {
                // We know lhs is a constant so no point passing arguments,
                DataObject value = expression.Evaluate(context);
                bool       comparedToTrue;

                // The various operators
                if (op.IsEquivalent(Operator.Greater) ||
                    op.IsEquivalent(Operator.GreaterOrEqual))
                {
                    // Find the maximum value in the table
                    DataObject cell = other.GetLastCell(0);
                    comparedToTrue = CompareCells(value, cell, op);
                }
                else if (op.IsEquivalent(Operator.Smaller) ||
                         op.IsEquivalent(Operator.SmallerOrEqual))
                {
                    // Find the minimum value in the table
                    DataObject cell = other.GetFirstCell(0);
                    comparedToTrue = CompareCells(value, cell, op);
                }
                else if (op.IsEquivalent(Operator.Equal))
                {
                    // Only true if rhs is a single value
                    DataObject cell = other.GetSingleCell(0);
                    comparedToTrue = (cell != null && CompareCells(value, cell, op));
                }
                else if (op.IsEquivalent(Operator.NotEqual))
                {
                    // true only if lhs_cell is not found in column.
                    comparedToTrue = !other.ColumnContainsValue(0, value);
                }
                else
                {
                    throw new ApplicationException("Don't understand operator '" + op + "' in ALL.");
                }

                // If matched return this table
                if (comparedToTrue)
                {
                    return(table);
                }

                // No entries matches so return an empty table.
                return(table.EmptySelect());
            }

            Table sourceTable;
            int   colIndex;
            // Is the lhs expression a single variable?
            ObjectName expVar = expression.AsVariable();

            // NOTE: It'll be less common for this part to be called.
            if (expVar == null)
            {
                // This is a complex expression so make a FunctionTable as our new
                // source.
                DatabaseQueryContext dbContext = (DatabaseQueryContext)context;
                FunctionTable        funTable  = new FunctionTable((Table)table, new[] { expression }, new [] { "1" }, dbContext);
                sourceTable = funTable;
                colIndex    = 0;
            }
            else
            {
                // The expression is an easy to resolve reference in this table.
                sourceTable = (Table)table;

                colIndex = sourceTable.FindFieldName(expVar);
                if (colIndex == -1)
                {
                    throw new ApplicationException("Can't find column '" + expVar + "'.");
                }
            }

            // Check that the first column of 'table' is of a compatible type with
            // source table column (lhs_col_index).
            // ISSUE: Should we convert to the correct type via a FunctionTable?
            DataColumnInfo sourceCol = sourceTable.TableInfo[colIndex];
            DataColumnInfo destCol   = other.TableInfo[0];

            if (!sourceCol.DataType.IsComparable(destCol.DataType))
            {
                throw new ApplicationException("The type of the sub-query expression " +
                                               sourceCol.DataType + " is incompatible " +
                                               "with the sub-query " + destCol.DataType +
                                               ".");
            }

            // We now have all the information to solve this query.
            // We work output as follows:
            //   For >, >= type ALL we find the highest value in 'table' and
            //   select from 'source' all the rows that are >, >= than the
            //   highest value.
            //   For <, <= type ALL we find the lowest value in 'table' and
            //   select from 'source' all the rows that are <, <= than the
            //   lowest value.
            //   For = type ALL we see if 'table' contains a single value.  If it
            //   does we select all from 'source' that equals the value, otherwise an
            //   empty table.
            //   For <> type ALL we use the 'not in' algorithm.

            IList <long> selectList;

            if (op.IsEquivalent(Operator.Greater) ||
                op.IsEquivalent(Operator.GreaterOrEqual))
            {
                // Select the last from the set (the highest value),
                DataObject highestCell = other.GetLastCell(0);
                // Select from the source table all rows that are > or >= to the
                // highest cell,
                selectList = sourceTable.SelectRows(colIndex, op, highestCell).ToList();
            }
            else if (op.IsEquivalent(Operator.Smaller) ||
                     op.IsEquivalent(Operator.SmallerOrEqual))
            {
                // Select the first from the set (the lowest value),
                DataObject lowestCell = other.GetFirstCell(0);
                // Select from the source table all rows that are < or <= to the
                // lowest cell,
                selectList = sourceTable.SelectRows(colIndex, op, lowestCell).ToList();
            }
            else if (op.IsEquivalent(Operator.Equal))
            {
                // Select the single value from the set (if there is one).
                DataObject singleCell = other.GetSingleCell(0);
                if (singleCell != null)
                {
                    // Select all from source_table all values that = this cell
                    selectList = sourceTable.SelectRows(colIndex, op, singleCell).ToList();
                }
                else
                {
                    // No single value so return empty set (no value in LHS will equal
                    // a value in RHS).
                    return(table.EmptySelect());
                }
            }
            else if (op.IsEquivalent(Operator.NotEqual))
            {
                // Equiv. to NOT IN
                selectList = sourceTable.NotIn(other, colIndex, 0).ToList();
            }
            else
            {
                throw new ApplicationException("Don't understand operator '" + op + "' in ALL.");
            }

            // Make into a table to return.
            VirtualTable rtable = new VirtualTable(theTable);

            rtable.Set((Table)table, selectList);
            return(rtable);
        }
Exemplo n.º 3
0
        /// <summary>
        /// Forms a command plan <see cref="IQueryPlanNode"/> from the given 
        /// <see cref="TableSelectExpression"/> and <see cref="TableExpressionFromSet"/>.
        /// </summary>
        /// <param name="db"></param>
        /// <param name="expression">Describes the <i>SELECT</i> command 
        /// (or sub-command).</param>
        /// <param name="fromSet">Used to resolve expression references.</param>
        /// <param name="orderBy">A list of <see cref="ByColumn"/> objects 
        /// that represent an optional <i>ORDER BY</i> clause. If this is null 
        /// or the list is empty, no ordering is done.</param>
        /// <returns></returns>
        public static IQueryPlanNode FormQueryPlan(IDatabaseConnection db, TableSelectExpression expression, TableExpressionFromSet fromSet, IList<ByColumn> orderBy)
        {
            IQueryContext context = new DatabaseQueryContext(db);

            // ----- Resolve the SELECT list
            // If there are 0 columns selected, then we assume the result should
            // show all of the columns in the result.
            bool doSubsetColumn = (expression.Columns.Count != 0);

            // What we are selecting
            var columnSet = BuildColumnSet(expression, fromSet);

            // Prepare the column_set,
            columnSet.Prepare(context);

            ResolveOrderByRefs(columnSet, orderBy);

            // -----

            // Set up plans for each table in the from clause of the command.  For
            // sub-queries, we recurse.

            var tablePlanner = SetupPlanners(db, fromSet);

            // -----

            // The WHERE and HAVING clauses
            FilterExpression whereClause = expression.Where;
            FilterExpression havingClause = expression.Having;

            whereClause = PrepareJoins(tablePlanner, expression, fromSet, whereClause);

            // Prepare the WHERE and HAVING clause, qualifies all variables and
            // prepares sub-queries.
            whereClause = PrepareSearchExpression(db, fromSet, whereClause);
            havingClause = PrepareSearchExpression(db, fromSet, havingClause);

            // Any extra Aggregate functions that are part of the HAVING clause that
            // we need to add.  This is a list of a name followed by the expression
            // that contains the aggregate function.
            var extraAggregateFunctions = new List<Expression>();
            if (havingClause != null && havingClause.Expression != null) {
                Expression newHavingClause = FilterHavingClause(havingClause.Expression, extraAggregateFunctions, context);
                havingClause = new FilterExpression(newHavingClause);
            }

            // Any GROUP BY functions,
            ObjectName[] groupByList;
            IList<Expression> groupByFunctions;
            var gsz = ResolveGroupBy(expression, fromSet, context, out groupByList, out groupByFunctions);

            // Resolve GROUP MAX variable to a reference in this from set
            ObjectName groupmaxColumn = ResolveGroupMax(expression, fromSet);

            // -----

            // Now all the variables should be resolved and correlated variables set
            // up as appropriate.

            // If nothing in the FROM clause then simply evaluate the result of the
            // select
            if (fromSet.SetCount == 0)
                return EvaluateSingle(columnSet);

            // Plan the where clause.  The returned node is the plan to evaluate the
            // WHERE clause.
            IQueryPlanNode node = tablePlanner.PlanSearchExpression(whereClause);

            Expression[] defFunList;
            string[] defFunNames;
            var fsz = MakeupFunctions(columnSet, extraAggregateFunctions, out defFunList, out defFunNames);

            node = PlanGroup(node, columnSet, groupmaxColumn, gsz, groupByList, groupByFunctions, fsz, defFunNames, defFunList);

            // The result column list
            List<SelectColumn> selectColumns = columnSet.SelectedColumns;
            int sz = selectColumns.Count;

            // Evaluate the having clause if necessary
            if (havingClause != null && havingClause.Expression != null) {
                // Before we evaluate the having expression we must substitute all the
                // aliased variables.
                Expression havingExpr = havingClause.Expression;
                havingExpr = SubstituteAliasedVariables(havingExpr, selectColumns);
                havingClause = new FilterExpression(havingExpr);

                PlanTableSource source = tablePlanner.SingleTableSource;
                source.UpdatePlan(node);
                node = tablePlanner.PlanSearchExpression(havingClause);
            }

            // Do we have a composite select expression to process?
            IQueryPlanNode rightComposite = null;
            if (expression.NextComposite != null) {
                TableSelectExpression compositeExpr = expression.NextComposite;
                // Generate the TableExpressionFromSet hierarchy for the expression,
                TableExpressionFromSet compositeFromSet = GenerateFromSet(compositeExpr, db);

                // Form the right plan
                rightComposite = FormQueryPlan(db, compositeExpr, compositeFromSet, null);
            }

            // Do we do a final subset column?
            ObjectName[] aliases = null;
            if (doSubsetColumn) {
                // Make up the lists
                ObjectName[] subsetVars = new ObjectName[sz];
                aliases = new ObjectName[sz];
                for (int i = 0; i < sz; ++i) {
                    SelectColumn scol = selectColumns[i];
                    subsetVars[i] = scol.InternalName.Clone();
                    aliases[i] = scol.Alias.Clone();
                }

                // If we are distinct then add the DistinctNode here
                if (expression.Distinct)
                    node = new DistinctNode(node, subsetVars);

                // Process the ORDER BY?
                // Note that the ORDER BY has to occur before the subset call, but
                // after the distinct because distinct can affect the ordering of the
                // result.
                if (rightComposite == null && orderBy != null)
                    node = PlanForOrderBy(node, orderBy, fromSet, selectColumns);

                // Rename the columns as specified in the SELECT
                node = new SubsetNode(node, subsetVars, aliases);
            } else {
                // Process the ORDER BY?
                if (rightComposite == null && orderBy != null)
                    node = PlanForOrderBy(node, orderBy, fromSet, selectColumns);
            }

            // Do we have a composite to merge in?
            if (rightComposite != null) {
                // For the composite
                node = new CompositeNode(node, rightComposite,
                            expression.CompositeFunction, expression.IsCompositeAll);
                // Final order by?
                if (orderBy != null) {
                    node = PlanForOrderBy(node, orderBy, fromSet, selectColumns);
                }
                // Ensure a final subset node
                if (!(node is SubsetNode) && aliases != null) {
                    node = new SubsetNode(node, aliases, aliases);
                }
            }

            return node;
        }