2008-09-26 21 views
5

选择行我写,我希望由许多不同的人来运行服务器的领域特定语言,不是所有的人,我会直接接触。服务器将在集群中相互通信。部分服务器功能涉及从可能非常大的表中选择一小部分行。选择哪些行的确切选择需要进行一些调整,重要的是运行群集的人员(例如我自己)可以更新选择标准,而不必让每个服务器管理员都部署新版本的服务器。写作从表

只需用Python写的功能是不是一个真正的选择,因为没有人会想安装下载并在运行时执行任意Python代码的服务器。

我需要的是在实现一个领域特定语言来实现这一目标最简单的方法建议。该语言需要能够进行简单的表达式评估,以及查询表索引并迭代返回的行。轻松写作和阅读语言是轻松实施的第二要素。我也不想编写一个完整的查询优化器,所以明确指定要查询的索引将是理想的。

这将需要编译的接口在功能上与App Engine数据存储库导出的接口类似:您可以查询表上任何索引的连续范围(例如,小于,大于,范围和平等查询),然后通过任何布尔表达式过滤返回的行。您还可以将多个独立的结果集连接在一起。

我意识到这个问题听起来很像我要求的SQL。但是,我不想要求支持这个数据的数据存储是一个关系数据库,而且我也不想要尝试自己重新实现SQL的开销。我也只处理具有已知模式的单个表。最后,不需要连接。更简单的东西将是更可取的。

编辑:扩大描述,以澄清一些误解。

+0

这是为了在App Engine中实现GQL吗? :P – wTyeRogers 2018-01-24 19:23:29

回答

4

创建ANTLR calculation engine建设到被Python解释的DSL的一个很好的例子。

第1步。构建运行时类和对象。这些类将拥有所有的游标循环和SQL语句,并将所有算法处理放在他们的方法中。您将大量使用CommandStrategy设计模式来构建这些类。大多数事情都是命令,选项和选择都是插件策略。看看Apache Ant的设计Task API - 这是一个很好的例子。

第2步验证这个系统的对象实际工作。确保设计简单而完整。测试将构建Command和Strategy对象,然后执行顶级Command对象。 Command对象将完成这项工作。

在这一点上你基本上完成了。您的运行时间只是从上述域创建的对象的配置。 [这听起来并不容易。它需要一些注意来定义一组可以实例化的类,然后“在他们自己之间进行交谈”来完成应用程序的工作。]

请注意,您将只需要声明。程序有什么问题?你开始用过程元素编写DSL,你会发现你需要越来越多的功能,直到你用不同的语法编写了Python。不好。

此外,程序语言解释器很难写。执行状态和参考范围很难管理。

您可以使用本机Python并停止担心“离开沙盒”。事实上,这就是你将单元测试所有东西的方式,使用一个简短的Python脚本来创建你的对象。 Python将是DSL。

[“但是等待”,你说,“如果我简单地使用Python作为DSL人可以执行任意事情。”取决于PYTHONPATH和sys.path上的内容。查看site模块,了解如何控制可用的模块。]

说明性DSL最简单。这完全是一种表现形式的练习。一块Python仅仅设置了一些变量的值是很好的。这就是Django使用的。

您可以使用ConfigParser作为表示对象的运行时配置的语言。

您可以使用JSONYAML作为表示对象的运行时配置的语言。现成的解析器完全可用。

也可以使用XML。设计和解析很困难,但工作正常。人们喜欢它。这就是Ant和Maven(以及许多其他工具)如何使用声明性语法来描述过程。我不推荐它,因为这是一个脖子上的罗嗦痛苦。我建议使用Python。或者,你可以去掉深层次,发明你自己的语法并编写你自己的解析器。

0

为什么不创建一个语言,当它“编译”它生成SQL或任何查询语言的数据存储需要?

您将基本建立在你的持久层的抽象。

+0

服务器体系结构允许多种可能的数据存储实现。最简单的一个是简单的内存表,所以我的数据存储所需的查询语言就是Python。 – 2008-09-26 15:06:40

0

你提到了Python。为什么不使用Python?如果有人可以在你的DSL中“输入”一个表达式,他们可以输入Python。

你需要在表达的结构有一定的规则,但是这不是实施新的东西要容易得多。

+0

我已经说过为什么:没有人会希望运行一个服务器来在运行时下载和执行任意代码。而一个足以彻底检查Python以确保它没有任何恶意行为的解析器可能与编写正确的DSL几乎一样复杂。 – 2008-09-26 15:09:08

+0

但这是一个可以管理的安全问题。例如,您的DBA可以在运行时下载任意代码。考虑到这个过程似乎是非常罕见的,没有理由让任何被信任的人完成这项工作。 – 2008-09-26 15:21:41

+0

重点在于它不需要每个服务器管理员的手动干预。我需要一种能够以这种方式查询/过滤的DSL,但是服务器管理员可以信任在没有他们的干预或监督的情况下进行更新。 – 2008-09-26 15:57:32

0

你说没有人会想要安装一个在运行时下载和执行任意代码的服务器。但是,这正是DSL(最终)所要做的,所以可能没有太大区别。除非你对数据做了非常具体的事情,否则我不认为DSL会给你带来太多的收益,并且会让熟悉SQL的用户感到沮丧。不要低估你将要完成的任务的规模。

但是,要回答你的问题,你需要为你的语言提供一个语法,解析文本并遍历树,发送代码或调用你写的API(这就是为什么我的评论你仍然需要运送一些代码)。

在网上可以参考的数学表达式有很多关于语法的教育文本,这是非常简单的。你可能有一个解析器生成器工具,比如ANTLR或Yacc,你可以用它来帮助你生成解析器(或者使用像Lisp/Scheme这样的语言并将它们结合起来)。提出一个合理的SQL语法并不容易。但谷歌'BNF SQL',看看你想出了什么。

祝你好运。

+0

是的,它会下载'任意'代码,但是当它用DSL表示时,它不会有太大的作用。任意Python代码和任意过滤器表达式(或类似的)之间有着天壤之别。 – 2008-09-26 15:52:27

1

我想我们在这里需要更多的信息。让我知道以下任何情况是否基于错误的假设。

首先,正如您自己指出的那样,已经存在用于从任意表中选择行的DSL--它被称为“SQL”。既然你不想重新创建SQL,我假设你只需要从一个固定格式的表中查询。

如果是这样的话,你可能不需要实现DSL(尽管这肯定是一种方法);如果你习惯了对象方向,创建一个Filter对象可能会更容易。

更具体地说,是一个可容纳一个或多个SelectionCriterion对象的“Filter”集合。您可以实现这些以从一个或多个表示选择类型(Range,LessThan,ExactMatch,Like等等)的基类继承。一旦这些基类就位,您就可以创建适合该列的特定于列的继承版本。最后,根据您希望支持的查询的复杂性,您需要实现某种连接粘合剂来处理各种标准之间的AND和OR以及NOT链接。

如果您喜欢它,您可以创建一个简单的GUI来加载集合;如果你没有任何其他的想法,我会把Excel中的过滤看作一个模型。

最后,将此Collection的内容转换为相应的SQL并将其传递到数据库应该是微不足道的。但是:如果你所追求的是简单性,并且你的用户理解SQL,那么你可以简单地要求他们键入WHERE子句的内容,并以编程方式构建查询的其余部分。从安全角度来看,如果您的代码可以控制所选列和FROM子句,并且您的数据库权限设置得当,并且您对来自用户的字符串进行了一些理智的检查,这将是一个相对安全的选项。

+0

大多数声音,但我仍然需要某种方式来指定过滤器集合等。请记住,这里的目标是我能够指定一个更新的函数,并将其传播到群集中的所有服务器。 – 2008-09-26 15:51:14

0

它确实听起来像SQL,但如果你想保持简单,试试使用SQLite可能值得吗?

1

“实现一个领域特定语言”

“没有人会想安装下载并在运行时执行任意Python代码的服务器”

我想要一个DSL,但我不希望的Python成为DSL。好的。你将如何执行这个DSL?什么运行时如果不是Python可接受?

如果我有一个恰好嵌入Python解释器的C程序会怎么样?这可以接受吗?

而且 - 如果Python不是一个可以接受的运行时 - 为什么它有一个Python标签?

+0

我将通过在Python中编写解释器来执行DSL。这就是为什么它有Python标签。在这种情况下使用DSL的关键在于,它不允许写入的代码脱离沙盒并影响整个系统。 – 2008-09-26 21:17:42

0

听起来好像你想创建一个不是DSL的语法。我会研究ANTLR这将允许您创建一个特定的解析器,将解释文本和翻译到特定的命令。 ANTLR为Python,SQL,Java,C++,C,C#等提供库

而且,这里是C#

0

上下文无关语法通常有一个树状结构,功能程序也有一个树状结构。我不主张以下内容能够解决您的所有问题,但如果您确定不想使用类似SQLite3的东西,那么朝着这个方向迈出了一大步。

from functools import partial 
def select_keys(keys, from_): 
    return ({k : fun(v, row) for k, (v, fun) in keys.items()} 
      for row in from_) 

def select_where(from_, where): 
    return (row for row in from_ 
      if where(row)) 

def default_keys_transform(keys, transform=lambda v, row: row[v]): 
    return {k : (k, transform) for k in keys} 

def select(keys=None, from_=None, where=None): 
    """ 
    SELECT v1 AS k1, 2*v2 AS k2 FROM table WHERE v1 = a AND v2 >= b OR v3 = c 

    translates to 

    select(dict(k1=(v1, lambda v1, r: r[v1]), k2=(v2, lambda v2, r: 2*r[v2]) 
     , from_=table 
     , where= lambda r : r[v1] = a and r[v2] >= b or r[v3] = c) 
    """ 
    assert from_ is not None 
    idfunc = lambda k, t : t 
    select_k = idfunc if keys is None else select_keys 
    if isinstance(keys, list): 
     keys = default_keys_transform(keys) 
    idfunc = lambda t, w : t 
    select_w = idfunc if where is None else select_where 
    return select_k(keys, select_w(from_, where)) 

你如何确保你没有赋予用户执行任意代码的能力。这个框架承认所有可能的功能。那么,你可以在它上面打包一个包装,以获得可接受的固定函数对象列表的安全性。

ALLOWED_FUNCS = [ operator.mul, operator.add, ...] # List of allowed funcs 

def select_secure(keys=None, from_=None, where=None): 
    if keys is not None and isinstance(keys, dict): 
     for v, fun keys.values: 
      assert fun in ALLOWED_FUNCS 
    if where is not None: 
     assert_composition_of_allowed_funcs(where, ALLOWED_FUNCS) 
    return select(keys=keys, from_=from_, where=where) 

如何写assert_composition_of_allowed_funcs。在python中执行它非常困难,但在lisp中很容易。让我们假设哪里是一个功能列表,以类似于where=(operator.add, (operator.getitem, row, v1), 2)where=(operator.mul, (operator.add, (opreator.getitem, row, v2), 2), 3)这样的格式进行评估。

这使得可以编写一个apply_lisp函数来确保where函数只由ALLOWED_FUNCS或像float,int,str这样的常量组成。

def apply_lisp(where, rowsym, rowval, ALLOWED_FUNCS): 
    assert where[0] in ALLOWED_FUNCS 
    return apply(where[0], 
      [ (apply_lisp(w, rowsym, rowval, ALLOWED_FUNCS) 
      if isinstance(w, tuple) 
      else rowval if w is rowsym 
      else w if isinstance(w, (float, int, str)) 
      else None) for w in where[1:] ]) 

此外,您还需要检查确切的类型,因为您不希望您的类型被覆盖。所以不要使用isinstance,请使用type in (float, int, str)。哦,孩子,我们遇到了:

Greenspun的程序设计第十条:任何足够复杂 C或Fortran程序包含一个特设非正式指定 充满错误的执行慢的Common Lisp的一半。