以正交方式向数据库查询添加条件

时间:2014-05-15 13:34:32

标签: sql parsing multi-tenant database

目前的问题是如何以正交方式"为预先存在的应用程序发出的SQL数据库查询添加条件,这意味着此操作应该以应用程序不需要的方式完成关心。如果您感到好奇,实际问题涉及添加多租户相关标准,以防止租户互相访问数据。实际上,这意味着为WHERE子句添加额外的条件(在查询中可能有多个位置)。

我的团队一直致力于一个涉及"拦截"在将查询发送到DBMS之前解析它们。正如你们许多人可能知道的那样,SQL解析并不是最简单的实现,特别是当你需要支持大多数主要的DBMS时。语法扩展。

另一个被抛出的想法是,可以让DBMS自己处理标准注入。毕竟,它是一个完全了解查询语法的元素,并且(希望)在执行它之前应该没有问题。那么问题就是要弄清楚如何将多租户元数据传递给DBMS。这有可行吗?这是个坏主意吗?

关于如何处理此问题的任何其他想法?

感谢您的时间。

2 个答案:

答案 0 :(得分:1)

通过视图处理起来会不容易,每个视图都限制在适用的用户数据中。预先编写的SQL可以使用视图的基本名称,然后在代码中修改该名称以在视图名称中添加前缀或后缀以提供用户的视图。

示例:

tennant_data包含名为tennant_data_user1tennant_data_user2的视图。 你的SQL是select col1, col2 from tennant_data_{view} 您的程序代码获取当前用户的名称(user1或user2),并将{view}替换为SQL中的用户ID,然后执行SQL。

根据您使用的DBMS(和语言?),您可以授予访问权限,以便user1只能使用xxx_user1视图等,因此他们不会错误地使用错误的数据来访问错误的数据查看或直接访问基础表。

答案 1 :(得分:0)

使用服务器端技术

当然,实现此功能的理想之处在于服务器端。有些数据库实现了Oracle's Virtual Private Database之类的功能,可以完全满足您的需求。

您还可以通过使用SYS_CONTEXT(在Oracle中)包含相关列的过滤器的(可能可更新的)视图替换所有直接表访问来模拟该功能。使用这种方法,数据库客户端将永远无法绕过您将在任何地方添加的其他谓词。

使用客户端技术

您没有提到您正在使用的技术堆栈,但在Java中,这可以通过jOOQ完成。 jOOQ附带一个SQL解析器,可以将SQL语句解析为表达式树。其中一个主要用例是将SQL从一种方言转换为另一种方言,如下所示:

E.g。这个语句在SQL Server或PostgreSQL上运行:

try (DSLContext ctx = DSL.using("...");
     Connection c = ctx.parsingConnection(); // Magic here
     Statement s = c.createStatement();
     ResultSet rs = s.executeQuery(
         "SELECT * FROM (VALUES (1), (2), (3)) t(a)")) {

    while (rs.next())
        System.out.println(rs.getInt(1));
}

可以转换为Oracle中的这个等效语句:

select t.a from (
  (select null a from dual where 1 = 0) union all
  (select * from (
    (select 1 from dual) union all
    (select 2 from dual) union all
    (select 3 from dual)
  ) t)
) t

一旦你有了jOOQ表达式树,就可以使用jOOQ的VisitListener对其进行转换,如以下文章中所示:

免责声明:

  • jOOQ只能解析jOOQ API也支持的SQL功能。 The grammar can be seen here。对于您现有的应用程序而言,这可能还不够,具体取决于您使用的特定于供应商的功能。
  • 我为jOOQ背后的公司工作,显然,这个答案是有偏见的。