blob: 8aaa38ac6ece923d915c87ebe6b1c879a63d02b5 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.flink.sql.parser;
import org.apache.flink.sql.parser.impl.FlinkSqlParserImpl;
import org.apache.flink.sql.parser.util.SqlValidatorTestCase;
import org.apache.flink.sql.parser.util.TestUtil;
import org.apache.flink.shaded.guava18.com.google.common.io.Files;
import org.apache.calcite.avatica.util.Casing;
import org.apache.calcite.avatica.util.Quoting;
import org.apache.calcite.config.Lex;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.parser.SqlParseException;
import org.apache.calcite.sql.parser.SqlParser;
import org.apache.calcite.sql.parser.SqlParserImplFactory;
import org.apache.calcite.sql.parser.SqlParserUtil;
import org.apache.calcite.sql.validate.SqlConformanceEnum;
import org.apache.calcite.util.Util;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.Charset;
import static org.junit.Assert.assertThat;
/**
*
*/
public abstract class ParserTestBase {
protected String readFile(String path) throws IOException {
URL url = getClass().getClassLoader().getResource(path);
assert url != null;
File file = new File(url.getFile());
return Files.toString(file, Charset.forName("UTF-8"));
}
private static final ThreadLocal<boolean[]> LINUXIFY =
new ThreadLocal<boolean[]>() {
@Override
protected boolean[] initialValue() {
return new boolean[]{true};
}
};
// Helper functions -------------------------------------------------------
protected Tester getTester() {
return new TesterImpl();
}
protected void check(
String sql,
String expected) {
sql(sql).ok(expected);
}
protected Sql sql(String sql) {
return new Sql(sql);
}
/**
* Implementors of custom parsing logic who want to reuse this test should
* override this method with the factory for their extension parser.
*/
protected SqlParserImplFactory parserImplFactory() {
return FlinkSqlParserImpl.FACTORY;
}
protected SqlParser.Config getParserConfig() {
return SqlParser.configBuilder()
.setParserFactory(parserImplFactory())
.setQuoting(Quoting.BACK_TICK)
.setUnquotedCasing(Casing.UNCHANGED)
.setQuotedCasing(Casing.UNCHANGED)
.setConformance(SqlConformanceEnum.DEFAULT)
.setIdentifierMaxLength(256)
.setLex(Lex.JAVA)
.build();
}
protected SqlParser getSqlParser(String sql) {
return SqlParser.create(sql, getParserConfig());
}
protected void checkExp(
String sql,
String expected) {
getTester().checkExp(sql, expected);
}
protected void checkExpSame(String sql) {
checkExp(sql, sql);
}
/**
* Returns a {@link Matcher} that succeeds if the given {@link SqlNode} is a
* DDL statement.
*/
public static Matcher<SqlNode> isDdl() {
return new BaseMatcher<SqlNode>() {
public boolean matches(Object item) {
return item instanceof SqlNode
&& SqlKind.DDL.contains(((SqlNode) item).getKind());
}
public void describeTo(Description description) {
description.appendText("isDdl");
}
};
}
//~ Inner Interfaces -------------------------------------------------------
/**
* Callback to control how test actions are performed.
*/
protected interface Tester {
void check(String sql, String expected);
void checkExp(String sql, String expected);
void checkFails(String sql, String expectedMsgPattern);
void checkNode(String sql, Matcher<SqlNode> matcher);
}
//~ Inner Classes ----------------------------------------------------------
/**
* Default implementation of {@link Tester}.
*/
protected class TesterImpl implements Tester {
public void check(
String sql,
String expected) {
final SqlNode sqlNode = parseStmtAndHandleEx(sql);
// no dialect, always parenthesize
String actual = sqlNode.toSqlString(null, true).getSql();
if (LINUXIFY.get()[0]) {
actual = Util.toLinux(actual);
}
TestUtil.assertEqualsVerbose(expected, actual);
}
public void checkFails(
String sql,
String expectedMsgPattern) {
SqlParserUtil.StringAndPos sap = SqlParserUtil.findPos(sql);
Throwable thrown = null;
try {
final SqlNode sqlNode = getSqlParser(sap.sql).parseStmt();
Util.discard(sqlNode);
} catch (Throwable ex) {
thrown = ex;
}
SqlValidatorTestCase.checkEx(thrown, expectedMsgPattern, sap);
}
protected SqlNode parseStmtAndHandleEx(String sql) {
final SqlNode sqlNode;
try {
sqlNode = getSqlParser(sql).parseStmt();
} catch (SqlParseException e) {
throw new RuntimeException("Error while parsing SQL: " + sql, e);
}
return sqlNode;
}
public void checkExp(
String sql,
String expected) {
final SqlNode sqlNode = parseExpressionAndHandleEx(sql);
String actual = sqlNode.toSqlString(null, true).getSql();
if (LINUXIFY.get()[0]) {
actual = Util.toLinux(actual);
}
TestUtil.assertEqualsVerbose(expected, actual);
}
protected SqlNode parseExpressionAndHandleEx(String sql) {
final SqlNode sqlNode;
try {
sqlNode = getSqlParser(sql).parseExpression();
} catch (SqlParseException e) {
throw new RuntimeException("Error while parsing expression: " + sql, e);
}
return sqlNode;
}
public void checkNode(String sql, Matcher<SqlNode> matcher) {
SqlParserUtil.StringAndPos sap = SqlParserUtil.findPos(sql);
try {
final SqlNode sqlNode = getSqlParser(sap.sql).parseStmt();
assertThat(sqlNode, matcher);
} catch (SqlParseException e) {
throw new RuntimeException(e);
}
}
}
/**
*
*/
protected class Sql {
private final String sql;
Sql(String sql) {
this.sql = sql;
}
public Sql ok(String expected) {
getTester().check(sql, expected);
return this;
}
public Sql fails(String expectedMsgPattern) {
getTester().checkFails(sql, expectedMsgPattern);
return this;
}
public Sql node(Matcher<SqlNode> matcher) {
getTester().checkNode(sql, matcher);
return this;
}
}
}