Parsing SQL queries in C ++ using Boost.Spirit

I created a database engine in which I can create and modify tables and add them to the database. To parse SQL queries, I implemented the Boost.Spirit library using the EBNF form. I configured the parser correctly, and it successfully parses each rule.

My problem: now I have no idea how to integrate the two. The Boost.Spirit parser only validates the input, however I really need to do something. I was looking for semantic actions, but they don't seem to cope with what I'm looking for.

For example, if I have a request, for example:
new_table <- SELECT (id < 5) old_table;

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

How do I integrate the parser?

+5
source share
1 answer

Note. I decided to come up with a grammar sample here for demonstration purposes, since your question does not show yours. Using the approach recommended here, it should not be difficult to program a function to fulfill your requests after parsing.

I would suggest creating a parse tree.

I would recommend distributing attributes in preference for semantic actions. See, for example,

The rules for distributing attributes are very flexible in the Spirit. Public default attribute types are well documented with every Parser documentation.

eg. - qi::char_will lead to boost::optional<char>, and qi::double_ | qi::int_will lead to boost::variant<double, int>.

, , AST , :

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, Fusion:

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

:

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_ - ';') ]
    >> ';';

, : http://liveworkspace.org/code/0b525234dbce22cbd8becd69f84065c1

-:

// #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
+14

All Articles