当前位置: 首页 > 知识库问答 >
问题:

SQL解析器访问者+元数据库+Presto

许安邦
2023-03-14

我面临的似乎是一个相当容易的问题,但我不能把我的头脑围绕这个问题找到一个合适的解决办法

FROM "SCHEMA".tableB tableB
LEFT JOIN "SCHEMA".tableC tableC
SELECT tableA.*
     , (tableA.valorfaturado + tableA.valorcortado) valorpedido       
  FROM (SELECT from_unixtime(tableB.datacorte / 1000) datacorte
             , COALESCE((tableB.quantidadecortada * tableC.preco), 0) valorcortado
             , COALESCE((tableB.quantidade * tableC.preco), 0) valorfaturado
             , tableB.quantidadecortada
          FROM tableB tableB
          LEFT JOIN tableC tableC
            ON tableC.numeropedido = tableB.numeropedido
           AND tableC.codigoproduto = tableB.codigoproduto
           AND tableC.codigofilial = tableB.codigofilial
          LEFT JOIN tableD tableD
            ON tableD.numero = tableB.numeropedido
         WHERE (CASE
                  WHEN COALESCE(tableB.codigofilial, '') = '' THEN
                    tableD.codigofilial
                  ELSE
                    tableB.codigofilial
                END) = '10'
           AND from_unixtime(tableB.datacorte / 1000) BETWEEN from_iso8601_timestamp('2020-07-01T03:00:00.000Z') AND from_iso8601_timestamp('2020-08-01T02:59:59.999Z')) tableA
 ORDER BY datacorte
SELECT tableA.*
     , (tableA.valorfaturado + tableA.valorcortado) valorpedido       
  FROM (SELECT from_unixtime(tableB.datacorte / 1000) datacorte
             , COALESCE((tableB.quantidadecortada * tableC.preco), 0) valorcortado
             , COALESCE((tableB.quantidade * tableC.preco), 0) valorfaturado
             , tableB.quantidadecortada
          FROM "SCHEMA".tableB tableB
          LEFT JOIN "SCHEMA".tableC tableC
            ON tableC.numeropedido = tableB.numeropedido
           AND tableC.codigoproduto = tableB.codigoproduto
           AND tableC.codigofilial = tableB.codigofilial
          LEFT JOIN "SCHEMA".tableD tableD
            ON tableD.numero = tableB.numeropedido
         WHERE (CASE
                  WHEN COALESCE(tableB.codigofilial, '') = '' THEN
                    tableD.codigofilial
                  ELSE
                    tableB.codigofilial
                END) = '10'
           AND from_unixtime(tableB.datacorte / 1000) BETWEEN from_iso8601_timestamp('2020-07-01T03:00:00.000Z') AND from_iso8601_timestamp('2020-08-01T02:59:59.999Z')) tableA
 ORDER BY datacorte
import java.util.Optional;

import com.facebook.presto.sql.SqlFormatter;
import com.facebook.presto.sql.parser.ParsingOptions;
import com.facebook.presto.sql.parser.SqlParser;

public class SchemaAwareQueryAdapter {
    // Inspired from
    // https://github.com/prestodb/presto/tree/master/presto-parser/src/test/java/com/facebook/presto/sql/parser

    private static final SqlParser SQL_PARSER = new SqlParser();

    public String rewriteSql(String sqlStatement, String schemaId) {
        com.facebook.presto.sql.tree.Statement statement = SQL_PARSER.createStatement(sqlStatement, ParsingOptions.builder().build());
        SchemaAwareQueryVisitor visitor = new SchemaAwareQueryVisitor(schemaId);
        statement.accept(visitor, null);
        return SqlFormatter.formatSql(statement, Optional.empty());
    }
}
public class SchemaAwareQueryVisitor extends DefaultTraversalVisitor<Void, Void> {
    private String schemaId;

    public SchemaAwareQueryVisitor(String schemaId) {
        super();
        this.schemaId = schemaId;
    }

    /**
     * The customer can type:
     * [table name]
     * [schema].[table name]
     * [catalog].[schema].[table name]
     */
    @Override
    protected Void visitTable(Table node, Void context) {
        List<String> parts = node.getName().getParts();
        // [table name] -> is the only one we need to modify, so let's check by parts.size() ==1
        if (parts.size() == 1) {
            try {
                Field privateStringField = Table.class.getDeclaredField("name");
                privateStringField.setAccessible(true);
                QualifiedName qualifiedName = QualifiedName.of("\""+schemaId+"\"",node.getName().getParts().get(0));
                privateStringField.set(node, qualifiedName);
            } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
                throw new SecurityException("Unable to execute query");
            }
        }
        return null;
        
    }
}
import static org.testng.Assert.assertEquals;

import org.gherrera.prestosqlparser.SchemaAwareQueryAdapter;
import org.testng.annotations.Test;

public class SchemaAwareTest {
        private static final String schemaId = "SCHEMA";
        private SchemaAwareQueryAdapter adapter = new SchemaAwareQueryAdapter();

        @Test
        public void testAppendSchemaA() {
            String sql = "select * from tableA";
            String bound = adapter.rewriteSql(sql, schemaId);
            assertEqualsFormattingStripped(bound,
                         "select * from \"SCHEMA\".tableA");
        }
        
        private void assertEqualsFormattingStripped(String sql1, String sql2) {
            
            assertEquals(sql1.replace("\n", " ").toLowerCase().replace("\r", " ").replaceAll(" +", " ").trim(),
                         sql2.replace("\n", " ").toLowerCase().replace("\r", " ").replaceAll(" +", " ").trim());
            
        }
}
<dependencies>
        <dependency>
            <groupId>com.facebook.presto</groupId>
            <artifactId>presto-parser</artifactId>
            <version>0.229</version>
        </dependency>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>6.10</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

ps:我可以添加没有双引号的模式,但是我将它们添加到标识符中,标识符不能以数字开头;用双引号包围标识符错误。基本上,此错误来自SQLParser$PostProcessor.ExitDigitIdentifier(...)方法。

谢谢

共有1个答案

宋原
2023-03-14

我能够为我的案例找到一个解决方案,无论哪种方式都将分享我的发现,看看这是否是预期的行为。

因此,如果您想用双引号追加模式,您将需要创建自己的Vistor类,并且需要重写visittable方法,并且当您用schema限定表的名称时(下面是刻度),将模式作为大写传递,这样它将不匹配formatname方法上的类sqlformatter上的regex模式,并且它将添加双引号。

public class SchemaAwareQueryVisitor extends DefaultTraversalVisitor<Void, Void> {
  private String schemaId;

  public SchemaAwareQueryVisitor(String schemaId) {
    super();
    this.schemaId = schemaId;
  }

  @Override
  protected Void visitTable(Table node, Void context) {
      try {
        Field privateStringField = Table.class.getDeclaredField("name");
        privateStringField.setAccessible(true);
        QualifiedName qualifiedName = QualifiedName.of(schemaId, node.getName().getParts().get(0));
        privateStringField.set(node, qualifiedName);
      } catch (NoSuchFieldException
          | SecurityException
          | IllegalArgumentException
          | IllegalAccessException e) {
        throw new SecurityException("Unable to execute query");
      }
    return null;
  }
}
 
 类似资料:
  • 我正在通过自定义语法处理输入文件,提取标记,并在中返回它们。在ANTLR 3中,我能够通过调用解析器上的方法来解析文件,并获得类似于。 这在ANTLR 4中似乎不起作用。我参考了这本书,似乎我必须调用开始解析,但我在解析器中没有看到任何方法。 我使用ANTLRWorks 2生成我的词法分析器和解析器文件。我没有生成侦听器类。

  • 问题内容: 我是PHP和SQL领域的一名新开发人员。到目前为止,我只完成了Objective- C编程。但是,我的一个项目要求我拥有一个在线数据库,我需要从我的应用程序访问该数据库。我打算使用该框架来远程访问数据库,如下所示: 但这是行不通的,因为我联系了我的托管服务提供商,并且他们已经对其进行了设置,因此我无法从外部主机访问我的数据库(出于安全性考虑)。因此,我将不得不寻找替代方案。我唯一想到的

  • 一个ASP.NET web项目加载了解决方案,但我得到了这个错误

  • ActiveX Data Objects (ADO) 是一项容易使用并且可扩展的将数据库访问添加到 Web 页的技术。可以使用 ADO 去编写紧凑简明的脚本以便连接到 Open Database Connectivity (ODBC) 兼容的数据库和 OLE DB 兼容的数据源。如果您是一个对数据库连接有一定了解的脚本编写人员,那么您将发现 ADO 命令语句并不复杂而且容易掌握。同样地,如果您是一

  • 对许多Web应用程序而言,数据库都是其核心所在。数据库几乎可以用来存储你想查询和修改的任何信息,比如用户信息、产品目录或者新闻列表等。 Go没有内置的驱动支持任何的数据库,但是Go定义了database/sql接口,用户可以基于驱动接口开发相应数据库的驱动,5.1小节里面介绍Go设计的一些驱动,介绍Go是如何设计数据库驱动接口的。5.2至5.4小节介绍目前使用的比较多的一些关系型数据驱动以及如何使

  • 程序运行的时候,数据都是在内存中的。当程序终止的时候,通常都需要将数据保存到磁盘上,无论是保存到本地磁盘,还是通过网络保存到服务器上,最终都会将数据写入磁盘文件。 而如何定义数据的存储格式就是一个大问题。如果我们自己来定义存储格式,比如保存一个班级所有学生的成绩单: 名字 成绩 Michael 99 Bob 85 Bart 59 Lisa 87 你可以用一个文本文件保存,一行保存一个学生,用,隔开