Calcite SQL解析介绍

2020/08/18 Calcite 共 2993 字,约 9 分钟

fmpp

FMPP是一种使用FreeMarker模板的通用文本文件预处理工具 主要用来 通过 config.fmpp配置文件,xxx.ftl 附加模板和Parser.jj 模板文件 来生成最后的Parser.jj

javaCC

JavaCC本身并不是词法分析器和语法分析器,它是一个生成器!JavaCC通过读取一个后缀为.jj的描述文件中的特定描述, 来生成词法分析器和语法分析器,并且生成的词法分析器和语法分析器使用Java书写的。 用来 通过 Parser.jj来生成 SqlParserImpl等java类.

fmpp和javacc 生成过程

增加SQL解析

举例:

让calcite可以解析一下语句

String sql = "create function `jessica_udf` AS 'name.jessica.udf.SplitUdtf'";

增加附加模板

需要增加 createFunction.ftl附加模板 用来解析SQL

createFunction.ftl

boolean IfNotExistsOpt() :
{
}
{
<IF> <NOT> <EXISTS> { return true; }
            |
{ return false; }
}

SqlCreate SqlCreateFunction(Span s, boolean replace) :
{
final boolean ifNotExists;
final SqlIdentifier functionName;
final SqlNode className;
}
{
<FUNCTION> ifNotExists = IfNotExistsOpt()
    functionName = CompoundIdentifier()
<AS> className = StringLiteral() {

return new SqlCreateFunction(s.end(this), replace, ifNotExists,functionName, className);
}
}

增加SqlFunction的SqlNode

public class SqlCreateFunction extends SqlCreate {
  // function name
	private final SqlIdentifier name;
  //function class
	private final SqlNode className;

	private static final SqlSpecialOperator OPERATOR =
			new SqlSpecialOperator("CREATE FUNCTION", SqlKind.CREATE_FUNCTION);
	public SqlCreateFunction(SqlParserPos pos,
							 boolean replace,
							 boolean ifNotExists,
							 SqlIdentifier name,
	SqlNode className) {
		super(OPERATOR, pos, replace, ifNotExists);
		this.name = Objects.requireNonNull(name);
		this.className = className;

	}

  //调用 sqlNode.toString方法 可还原sql
	@Override public void unparse(SqlWriter writer, int leftPrec,
								  int rightPrec) {
		writer.keyword(getReplace() ? "CREATE OR REPLACE" : "CREATE");
		writer.keyword("FUNCTION");
		if (ifNotExists) {
			writer.keyword("IF NOT EXISTS");
		}
		name.unparse(writer, 0, 0);
		writer.keyword("AS");
		className.unparse(writer, 0, 0);
	}

	@Override
	public SqlOperator getOperator() {
		return OPERATOR;
	}

	@Override
	public List<SqlNode> getOperandList() {
		return Arrays.asList(name,className);
	}
}

config.fmpp增加相关配置

在 config.fmpp 配置中 增加 相关的信息

 imports: [
    "name.jessica.calcite.sql.ddl.SqlCreateFunction"
    "org.apache.calcite.sql.SqlCreate"
    ]

    # List of new keywords. Example: "DATABASES", "TABLES". If the keyword is not a reserved
    # keyword add it to 'nonReservedKeywords' section.
    keywords: [
    "IF"
    ]
   implementationFiles: [
      "createFunction.ftl"
    ]

生成SqlParserImpl

 final public SqlCreate SqlCreateFunction(Span s, boolean replace) throws ParseException {
final boolean ifNotExists;
final SqlIdentifier functionName;
final SqlNode className;
    jj_consume_token(FUNCTION);
    ifNotExists = IfNotExistsOpt();
    functionName = CompoundIdentifier();
    jj_consume_token(AS);
    className = StringLiteral();
{if (true) return new SqlCreateFunction(s.end(this), replace, ifNotExists,functionName, className);}
    throw new Error("Missing return statement in function");
  }

Java单测进行测试

@Test
	public void testCreateFunction() throws IOException, SqlParseException {
		String sql = "create function `jessica_udf` AS 'name.jessica.udf.SplitUdtf'";
       // 解析配置
		SqlParser.Config javaConfig = SqlParser.configBuilder().setLex(Lex.JAVA).build();
		SqlParser parser = SqlParser.create(sql,javaConfig);
		SqlNode createFunctionNode = parser.parseQuery();
		System.out.println(createFunctionNode.toSqlString(OracleSqlDialect.DEFAULT));
	}

文档信息

Search

    Table of Contents