【问题标题】:SQL engine for only SELECT statment - hand written parser in c++仅用于 SELECT 语句的 SQL 引擎 - C++ 中的手写解析器
【发布时间】:2011-04-23 16:08:30
【问题描述】:

我是编译器的新手,但我有一个项目 SQL 引擎 - 仅用于 select 语句。为此,我只能使用手写解析器和引擎。我研究了 LL(k) 语法和递归下降技术的样本(stackoverflow 建议手动编写解析器)。但是在任何示例中,都没有找到从函数构造解析树的方法。
谁能告诉我,如何一步一步地完成整个编译过程,只需要 " Select columnname1,columnname2 from table" 示例。还有一件事 - 也不允许使用 boost 库。数据在内存中。我使用结构来存储数据。提前致谢。

【问题讨论】:

  • 这种东西更适合整个大学课程。
  • 是的,我也有同样的想法——但我只有 10 天——我能做什么——我必须做 :(.
  • 不..实际上它不是家庭作业...还有一些其他想法可以扩展这个项目..如果它成功了。
  • 不是答案,但“SELECT”的 Oracle 铁路图可能是一个有用的开始:download.oracle.com/docs/cd/B19306_01/server.102/b14200/…
  • 你们真的有多快......谢谢它真的很有帮助。

标签: c++ sql parsing


【解决方案1】:

您还可以在网上找到BNF grammars for SQL 92, 99 and 2003,它可能会派上用场。

这些是完整的语法,但只隔离 SELECT 分支应该不难。

另外,您可以在http://www.antlr.org/grammar/list 上探索已经可用的语法;那里有一些 SQL 风格。

【讨论】:

    【解决方案2】:

    我想说更简单的方法是像编译器一样处理这个问题:

    • 标记化
    • 解析(创建 AST)

    标记化是关于识别“单词”,对于您的示例,这给出了:

    "Select", "columnname1", ",", "columnanme2", "from", "table"
    

    解析是将这个标记列表解释为抽象语法树。鉴于您的要求:

    • 第一个标记应该是“选择”(不区分大小写)
    • 后跟“*”或逗号分隔的列名列表
    • “from”(不区分大小写)标记列表的结尾
    • 后跟表名

    我将在 Python 中对此进行概述

    class Select:
      def __init__(self):
        self.columnList = None
        self.tableName = None
    
    def Tokenize(statement):
      result = []
      for word in statement.split(" "):
        sub = word.split(",")             # the comma may not be separated by space
        for i in range(len(sub)-1):
          result.append(sub[i].lower())   # case insensitive
          result.append(",")
        result.append(sub[-1])
      return result
    
    def CreateSelect(tokens):
      if len(tokens) == 0: raise NoToken()
      if tokens[0] != "select": raise NotASelectStatement()
    
      select = Select()
    
      i = 1
      while tokens[i] != "from":
        if tokens[i] == "*":
          select.columnList == ALL
          break
        else:
          select.columnList.append(tokens[i])
          i = i + 1
          if tokens[i] != ",": raise ExpectedComma(i)
          i = i + 1
    
       select.tableName = tokens[i+1]
    

    当然,如您所知,这是一个有限的例子:

    • 不考虑多个表
    • 不考虑别名
    • 不考虑嵌套的 select 语句

    但是它工作得很好而且效率很高。通常,如果我们将标记化和解析阶段结合起来,效率会更高,但我发现总的来说,它只会使代码更难阅读。

    还请注意,为了正确报告错误,它会更好:

    • 不更改标记 (.lower()),而只是使用不区分大小写的比较
    • 将正确的源位置连接到每个标记(行/列),以便能够将用户指向正确的位置

    编辑

    一个不那么做作的例子的 AST 会是什么样子?

    "select foo, bar from FooBar where category = 'example' and id < 500;"
    

    我们走吧:

    Select
    |_ Columns (foo, bar)
    |_ Table (FooBar)
    \_ Where
       \_ And
          |_ Equal (category, 'example')
          \_ LessThan (id, 500)
    

    这里你有一个树状结构,这就是你想要生成的。

    【讨论】:

    • 是的...感谢您的回复。是的,我需要编写 select 语句在商业数据库中所做的所有功能。从您的代码中我无法找到 - 如何构建 AST。如果可能的话,你能否描述一下代码生成步骤。谢谢你的时间。
    • 好吧,对于您的简化示例,Select 类是 AST 的唯一类,因此 CreateSelect 方法是 AST 生成。在一个不太人为的示例中,您还将有一个 Where 类、一个 OrderBy 类、一个 Equal 类等......
    • 哦……好的……谢谢……但是这棵树是用于代码生成步骤的输入吗?
    猜你喜欢
    • 1970-01-01
    • 2010-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-18
    • 1970-01-01
    相关资源
    最近更新 更多