本文介绍了使用Boost.Spirit解析C ++中的SQL查询的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我创建了一个数据库引擎,在其中我可以创建和修改表,并将它们添加到数据库。为了解析SQL查询,我已经使用EBNF形式实现了Boost.Spirit库。我正确的解析器设置,它成功地解析每个规则。

I have created a database engine in which I can create and modify tables, and add them to a database. For parsing the SQL queries, I have implemented the Boost.Spirit library using EBNF form. I have the parser setup properly and it successfully parses every rule.

我的问题是我现在不知道如何整合这两个。 Boost.Spirit解析器只验证输入是正确的,但我需要它实际上的东西。我查看了语义动作,但他们似乎不处理我正在寻找。

My problem is I now have no idea how to integrate the two. The Boost.Spirit parser only validates that input is correct, however I need it to actually do something. I looked up semantic actions but they don't seem to handle what I'm looking for.

例如,如果我有一个查询,例如:

new_table< - SELECT(id< 5) old_table;

For example, if I have a query such as:
new_table <- SELECT (id < 5) old_table;

我想让它使用规则验证输入,然后调用函数

Table Database :: Select(Table t,Condition c){...}

并将令牌作为参数传递。

I want it to validate the input using the rules, then call the function
Table Database::Select(Table t , Condition c){ ... }
and pass the tokens as arguments.

如何整合解析器?

推荐答案

解析树。

我会建议属性传播优先于语义动作。参见例如

I would recommend attribute propagation in preference to semantic actions. See e.g.



  • Boost Spirit: "Semantic actions are evil"?

属性传播规则在Spirit 。

Attribute propagation rules are very flexible in Spirit. The default exposed attributes types are well documented right with each Parser's documentation



  • http://www.boost.org/doc/libs/1_48_0/libs/spirit/doc/html/spirit/qi/reference.html

- qi :: char _ 会导致 boost :: optional< char> qi: :double_ | qi :: int _ 会导致 boost :: variant 。

E.g. -qi::char_ would result in boost::optional<char> and qi::double_ | qi::int_ would result in boost::variant<double, int>.

您可能希望在您自己的发明的AST数据类型中累积解析的元素,例如:

You will probably want to accumulate the parsed elements in a AST datatype of your own invention, e.g.:

struct SelectStatement
{
    std::vector<std::string> columns, fromtables;
    std::string whereclause; // TODO model as a vector<WhereCondition> :)

    friend std::ostream& operator<<(std::ostream& os, SelectStatement const& ss)
    {
        return os << "SELECT [" << ss.columns.size() << " columns] from [" << ss.fromtables.size() << " tables]\nWHERE " + ss.whereclause;
    }
};

您可以通过将结构适配为融合序列来将其适用于Spirits属性传播机制:

You could adapt this to Spirits attribute propagation machinery by adapting the struct as a Fusion sequence:

BOOST_FUSION_ADAPT_STRUCT(SelectStatement,
        (std::vector<std::string>, columns)
        (std::vector<std::string>, fromtables)
        (std::string, whereclause)
       )

现在您可以将以下规则解析为该类型:

Now you could parse the following rule into that type:

sqlident = lexeme [ alpha >> *alnum ]; // table or column name

columns  = no_case [ "select" ] >> (sqlident % ',');
tables   = no_case [ "from" ]   >> (sqlident % ',');

start    = columns >> tables
    >> no_case [ "where" ]
    >> lexeme [ +(char_ - ';') ]
    >> ';';

您可以看到此 代码正在运行这里:

完整演示代码:

// #define BOOST_SPIRIT_DEBUG
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/qi.hpp>

namespace qi    = boost::spirit::qi;

struct SelectStatement
{
    std::vector<std::string> columns, fromtables;
    std::string whereclause; // TODO model as a vector<WhereCondition> :)

    friend std::ostream& operator<<(std::ostream& os, SelectStatement const& ss)
    {
        return os << "SELECT [" << ss.columns.size() << " columns] from [" << ss.fromtables.size() << " tables]\nWHERE " + ss.whereclause;
    }
};

BOOST_FUSION_ADAPT_STRUCT(SelectStatement,
        (std::vector<std::string>, columns)
        (std::vector<std::string>, fromtables)
        (std::string, whereclause)
       )

template <typename It, typename Skipper = qi::space_type>
    struct parser : qi::grammar<It, SelectStatement(), Skipper>
{
    parser() : parser::base_type(start)
    {
        using namespace qi;

        sqlident = lexeme [ alpha >> *alnum ]; // table or column name

        columns  = no_case [ "select" ] >> (sqlident % ',');
        tables   = no_case [ "from" ]   >> (sqlident % ',');

        start    = columns >> tables
            >> no_case [ "where" ]
            >> lexeme [ +(char_ - ';') ]
            >> ';';

        BOOST_SPIRIT_DEBUG_NODE(start);
        BOOST_SPIRIT_DEBUG_NODE(sqlident);
        BOOST_SPIRIT_DEBUG_NODE(columns);
        BOOST_SPIRIT_DEBUG_NODE(tables);
    }

  private:
    qi::rule<It, std::string()             , Skipper> sqlident;
    qi::rule<It, std::vector<std::string>(), Skipper> columns  , tables;
    qi::rule<It, SelectStatement()         , Skipper> start;
};

template <typename C, typename Skipper>
    bool doParse(const C& input, const Skipper& skipper)
{
    auto f(std::begin(input)), l(std::end(input));

    parser<decltype(f), Skipper> p;
    SelectStatement query;

    try
    {
        bool ok = qi::phrase_parse(f,l,p,skipper,query);
        if (ok)
        {
            std::cout << "parse success\n";
            std::cout << "query: " << query << "\n";
        }
        else      std::cerr << "parse failed: '" << std::string(f,l) << "'\n";

        if (f!=l) std::cerr << "trailing unparsed: '" << std::string(f,l) << "'\n";
        return ok;
    } catch(const qi::expectation_failure<decltype(f)>& e)
    {
        std::string frag(e.first, e.last);
        std::cerr << e.what() << "'" << frag << "'\n";
    }

    return false;
}

int main()
{
    const std::string input = "select id, name, price from books, authors where books.author_id = authors.id;";
    bool ok = doParse(input, qi::space);

    return ok? 0 : 255;
}

将打印输出:

parse success
query: SELECT [3 columns] from [2 tables]
WHERE books.author_id = authors.id

这篇关于使用Boost.Spirit解析C ++中的SQL查询的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-18 12:47