示例#1
0
        public static IEnumerable <CriteriaSet> GenWhere(IEnumerable <Criteria> criteria)
        {
            CriteriaSet set = new CriteriaSet();

            foreach (var c in criteria)
            {
                set.AddCriteria(c);
            }
            return(new[] { new CriteriaSet() });
        }
示例#2
0
        /// <summary>
        /// Given a SQL-like query, return a a Select object, ready for adding parameters and querying
        /// </summary>
        /// <param name="sql">SQL-like query</param>
        /// <returns>Select object for adding parameters and executing</returns>
        public static Select Parse(string sql)
        {
            string[] tokens = Utils.Tokenize(sql);
            if (tokens.Length == 0 || (tokens.Length == 1 && string.IsNullOrWhiteSpace(tokens[0])))
            {
                throw new SqlException("No tokens", sql);
            }

            Select   select = new Select();
            SqlState state  = SqlState.SELECT;
            int      idx    = 0;

            while (idx < tokens.Length)
            {
                string currentToken = tokens[idx];
                if (state == SqlState.SELECT)
                {
                    // Should start with SELECT
                    if (!currentToken.Equals("SELECT", StringComparison.OrdinalIgnoreCase))
                    {
                        throw new SqlException("No SELECT", sql);
                    }

                    // Slurp up the SELECT columns
                    select.select = new List <string>();
                    while (true)
                    {
                        ++idx;
                        if (idx >= tokens.Length)
                        {
                            throw new SqlException("No SELECT columns", sql);
                        }

                        currentToken = tokens[idx];

                        bool lastColumn = !currentToken.EndsWith(",", StringComparison.Ordinal);
                        if (!lastColumn)
                        {
                            currentToken = currentToken.TrimEnd(',');
                        }

                        Utils.ValidateColumnName(currentToken, sql);
                        select.select.Add(currentToken);

                        if (lastColumn)
                        {
                            break;
                        }
                    }

                    ++idx;
                    state = SqlState.FROM;
                    continue;
                }

                if (state == SqlState.FROM)
                {
                    if (!currentToken.Equals("FROM", StringComparison.OrdinalIgnoreCase))
                    {
                        throw new SqlException("No FROM", sql);
                    }

                    ++idx;
                    if (idx >= tokens.Length)
                    {
                        throw new SqlException("No FROM table", sql);
                    }
                    currentToken = tokens[idx];
                    Utils.ValidateTableName(currentToken, sql);
                    select.from = currentToken;
                    ++idx;
                    state = SqlState.WHERE;
                    continue;
                }

                if (state == SqlState.WHERE)
                {
                    if (!currentToken.Equals("WHERE", StringComparison.OrdinalIgnoreCase))
                    {
                        state = SqlState.ORDER;
                        continue;
                    }

                    // Gobble up WHERE criteria
                    CriteriaSet criteriaSet = new CriteriaSet();
                    select.where = new List <CriteriaSet> {
                        criteriaSet
                    };
                    ++idx;
                    while ((idx + 3) <= tokens.Length)
                    {
                        var criteria =
                            new Criteria()
                        {
                            name      = tokens[idx++],
                            op        = tokens[idx++],
                            paramName = tokens[idx++]
                        };
                        Utils.ValidateColumnName(criteria.name, sql);
                        Utils.ValidateOperator(criteria.op, sql);
                        Utils.ValidateParameterName(criteria.paramName, sql);
                        criteriaSet.AddCriteria(criteria);

                        if
                        (
                            (idx + 3) <= tokens.Length
                            &&
                            tokens[idx].Equals("AND", StringComparison.OrdinalIgnoreCase)
                        )
                        {
                            ++idx;
                            continue;
                        }
                        else
                        {
                            break;
                        }
                    }

                    if (criteriaSet.criteria.Count == 0)
                    {
                        throw new SqlException("No WHERE criteria", sql);
                    }

                    state = SqlState.ORDER;
                    continue;
                }

                if (state == SqlState.ORDER)
                {
                    string nextToken = (idx + 1) < tokens.Length ? tokens[idx + 1] : "";
                    if
                    (
                        (idx + 3) > tokens.Length
                        ||
                        !currentToken.Equals("ORDER", StringComparison.OrdinalIgnoreCase)
                        ||
                        !nextToken.Equals("BY", StringComparison.OrdinalIgnoreCase)
                    )
                    {
                        state = SqlState.LIMIT;
                        continue;
                    }

                    idx += 2;

                    var orders = new List <Order>();
                    select.orderBy = orders;
                    while (idx < tokens.Length)
                    {
                        currentToken = tokens[idx];

                        bool currentEnds = idx == tokens.Length - 1 || currentToken.EndsWith(",", StringComparison.Ordinal);

                        nextToken = "ASC";
                        if (!currentEnds)
                        {
                            if ((idx + 1) < tokens.Length)
                            {
                                nextToken = tokens[++idx];
                            }
                        }

                        bool nextEnds = nextToken.EndsWith(",", StringComparison.Ordinal);

                        bool isLimit = nextToken.Equals("LIMIT", StringComparison.OrdinalIgnoreCase);

                        bool lastColumn = isLimit || !(currentEnds || nextEnds);

                        currentToken = currentToken.TrimEnd(',');
                        nextToken    = nextToken.TrimEnd(',');

                        bool isDescending;
                        {
                            if (nextToken.Equals("ASC", StringComparison.OrdinalIgnoreCase))
                            {
                                isDescending = false;
                            }
                            else if (nextToken.Equals("DESC", StringComparison.OrdinalIgnoreCase))
                            {
                                isDescending = true;
                            }
                            else if (isLimit)
                            {
                                isDescending = false;
                            }
                            else
                            {
                                throw new SqlException("Invalid ORDER BY", sql);
                            }
                        }

                        Utils.ValidateColumnName(currentToken, sql);

                        var orderObj = new Order()
                        {
                            field = currentToken, descending = isDescending
                        };
                        orders.Add(orderObj);

                        if (!isLimit)
                        {
                            ++idx;
                        }

                        if (lastColumn)
                        {
                            break;
                        }
                    }

                    state = SqlState.LIMIT;
                    continue;
                }

                if (state == SqlState.LIMIT)
                {
                    if (currentToken.Equals("LIMIT", StringComparison.OrdinalIgnoreCase))
                    {
                        ++idx;
                        if (idx >= tokens.Length)
                        {
                            throw new SqlException("No LIMIT value", sql);
                        }
                        currentToken = tokens[idx];

                        int limitVal;
                        if (!int.TryParse(currentToken, out limitVal))
                        {
                            throw new SqlException("Invalid LIMIT value", sql);
                        }
                        select.limit = limitVal;

                        ++idx;
                        break;
                    }
                    else
                    {
                        throw new SqlException("Invalid final statement", sql);
                    }
                }

                throw new SqlException($"Invalid SQL parser state: {state}", sql);
            }

            if (idx < tokens.Length - 1)
            {
                throw new SqlException("Not all parsed", sql);
            }

            if (select.select.Count == 0)
            {
                throw new SqlException("No SELECT columns", sql);
            }

            if (string.IsNullOrWhiteSpace(select.from))
            {
                throw new SqlException("No FROM", sql);
            }

            return(select);
        }