Repository: calcite Updated Branches: refs/heads/master 7d8e0528b -> d61930407
http://git-wip-us.apache.org/repos/asf/calcite/blob/d6193040/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java b/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java index 22eac7b..2de7643 100644 --- a/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java +++ b/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java @@ -172,12 +172,9 @@ public abstract class SqlImplementor { SqlCall unionAll = SqlStdOperatorTable.UNION_ALL .createCall(POS, unionOperand, unionOperand); - final SqlNodeList subQuery = new SqlNodeList(POS); - subQuery.add(unionAll); - final SqlNodeList selectList2 = new SqlNodeList(POS); selectList2.add(nullLiteral); - elseExpr = SqlStdOperatorTable.SCALAR_QUERY.createCall(POS, subQuery); + elseExpr = SqlStdOperatorTable.SCALAR_QUERY.createCall(POS, unionAll); break; default: http://git-wip-us.apache.org/repos/asf/calcite/blob/d6193040/core/src/main/java/org/apache/calcite/sql/fun/SqlItemOperator.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlItemOperator.java b/core/src/main/java/org/apache/calcite/sql/fun/SqlItemOperator.java index 7cd0e1e..603057e 100644 --- a/core/src/main/java/org/apache/calcite/sql/fun/SqlItemOperator.java +++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlItemOperator.java @@ -26,12 +26,15 @@ import org.apache.calcite.sql.SqlOperandCountRange; import org.apache.calcite.sql.SqlOperatorBinding; import org.apache.calcite.sql.SqlSpecialOperator; import org.apache.calcite.sql.SqlWriter; +import org.apache.calcite.sql.parser.SqlParserPos; import org.apache.calcite.sql.type.OperandTypes; import org.apache.calcite.sql.type.SqlOperandCountRanges; import org.apache.calcite.sql.type.SqlSingleOperandTypeChecker; import org.apache.calcite.sql.type.SqlTypeFamily; import org.apache.calcite.sql.type.SqlTypeName; +import java.util.Arrays; + /** * The item operator {@code [ ... ]}, used to access a given element of an * array or map. For example, {@code myArray[3]} or {@code "myMap['foo']"}. @@ -55,9 +58,10 @@ class SqlItemOperator extends SqlSpecialOperator { return new ReduceResult(ordinal - 1, ordinal + 2, createCall( - left.getParserPosition() - .plus(right.getParserPosition()) - .plus(list.pos(ordinal)), + SqlParserPos.sum( + Arrays.asList(left.getParserPosition(), + right.getParserPosition(), + list.pos(ordinal))), left, right)); } http://git-wip-us.apache.org/repos/asf/calcite/blob/d6193040/core/src/main/java/org/apache/calcite/sql/parser/Span.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/parser/Span.java b/core/src/main/java/org/apache/calcite/sql/parser/Span.java new file mode 100644 index 0000000..785cd8a --- /dev/null +++ b/core/src/main/java/org/apache/calcite/sql/parser/Span.java @@ -0,0 +1,154 @@ +/* + * 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.calcite.sql.parser; + +import org.apache.calcite.sql.SqlNode; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Builder for {@link SqlParserPos}. + * + * <p>Because it is mutable, it is convenient for keeping track of the + * positions of the tokens that go into a non-terminal. It can be passed + * into methods, which can add the positions of tokens consumed to it. + * + * <p>Some patterns: + * + * <ul> + * <li>{@code final Span s;} declaration of a Span at the top of a production + * <li>{@code s = span();} initializes s to a Span that includes the token we + * just saw; very often occurs immediately after the first token in the + * production + * <li>{@code s.end(this);} adds the most recent token to span s and evaluates + * to a SqlParserPosition that spans from beginning to end; commonly used + * when making a call to a function + * <li>{@code s.pos()} returns a position spanning all tokens in the list + * <li>{@code s.add(node);} adds a SqlNode's parser position to a span + * <li>{@code s.addAll(nodeList);} adds several SqlNodes' parser positions to + * a span + * <li>{@code s = Span.of();} initializes s to an empty Span, not even + * including the most recent token; rarely used + * </ul> + */ +public final class Span { + private final List<SqlParserPos> posList = new ArrayList<>(); + + /** Use one of the {@link #of} methods. */ + private Span() {} + + /** Creates an empty Span. */ + public static Span of() { + return new Span(); + } + + /** Creates a Span with one position. */ + public static Span of(SqlParserPos p) { + return new Span().add(p); + } + + /** Creates a Span of one node. */ + public static Span of(SqlNode n) { + return new Span().add(n); + } + + /** Creates a Span between two nodes. */ + public static Span of(SqlNode n0, SqlNode n1) { + return new Span().add(n0).add(n1); + } + + /** Creates a Span of a list of nodes. */ + public static Span of(Collection<? extends SqlNode> nodes) { + return new Span().addAll(nodes); + } + + /** Adds a node's position to the list, + * and returns this Span. */ + public Span add(SqlNode n) { + return add(n.getParserPosition()); + } + + /** Adds a node's position to the list if the node is not null, + * and returns this Span. */ + public Span addIf(SqlNode n) { + return n == null ? this : add(n); + } + + /** Adds a position to the list, + * and returns this Span. */ + public Span add(SqlParserPos pos) { + posList.add(pos); + return this; + } + + /** Adds the positions of a collection of nodes to the list, + * and returns this Span. */ + public Span addAll(Iterable<? extends SqlNode> nodes) { + for (SqlNode node : nodes) { + add(node); + } + return this; + } + + /** Adds the position of the last token emitted by a parser to the list, + * and returns this Span. */ + public Span add(SqlAbstractParserImpl parser) { + try { + final SqlParserPos pos = parser.getPos(); + return add(pos); + } catch (Exception e) { + // getPos does not really throw an exception + throw new AssertionError(e); + } + } + + /** Returns a position spanning the earliest position to the latest. + * Does not assume that the positions are sorted. + * Throws if the list is empty. */ + public SqlParserPos pos() { + switch (posList.size()) { + case 0: + throw new AssertionError(); + case 1: + return posList.get(0); + default: + return SqlParserPos.sum(posList); + } + } + + /** Adds the position of the last token emitted by a parser to the list, + * and returns a position that covers the whole range. */ + public SqlParserPos end(SqlAbstractParserImpl parser) { + return add(parser).pos(); + } + + /** Adds a node's position to the list, + * and returns a position that covers the whole range. */ + public SqlParserPos end(SqlNode n) { + return add(n).pos(); + } + + /** Clears the contents of this Span, and returns this Span. */ + public Span clear() { + posList.clear(); + return this; + } +} + +// End Span.java http://git-wip-us.apache.org/repos/asf/calcite/blob/d6193040/core/src/main/java/org/apache/calcite/sql/parser/SqlAbstractParserImpl.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/parser/SqlAbstractParserImpl.java b/core/src/main/java/org/apache/calcite/sql/parser/SqlAbstractParserImpl.java index f4631f6..34f10f5 100644 --- a/core/src/main/java/org/apache/calcite/sql/parser/SqlAbstractParserImpl.java +++ b/core/src/main/java/org/apache/calcite/sql/parser/SqlAbstractParserImpl.java @@ -31,6 +31,7 @@ import org.apache.calcite.util.Glossary; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import java.io.Reader; @@ -362,6 +363,26 @@ public abstract class SqlAbstractParserImpl { SqlParserPos pos, SqlFunctionCategory funcType, SqlLiteral functionQualifier, + Iterable<? extends SqlNode> operands) { + return createCall(funName, pos, funcType, functionQualifier, + Iterables.toArray(operands, SqlNode.class)); + } + + /** + * Creates a call. + * + * @param funName Name of function + * @param pos Position in source code + * @param funcType Type of function + * @param functionQualifier Qualifier + * @param operands Operands to call + * @return Call + */ + protected SqlCall createCall( + SqlIdentifier funName, + SqlParserPos pos, + SqlFunctionCategory funcType, + SqlLiteral functionQualifier, SqlNode[] operands) { SqlOperator fun = null; @@ -401,6 +422,8 @@ public abstract class SqlAbstractParserImpl { */ public abstract SqlParseException normalizeException(Throwable ex); + protected abstract SqlParserPos getPos() throws Exception; + /** * Reinitializes parser with new input. * http://git-wip-us.apache.org/repos/asf/calcite/blob/d6193040/core/src/main/java/org/apache/calcite/sql/parser/SqlParserPos.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/parser/SqlParserPos.java b/core/src/main/java/org/apache/calcite/sql/parser/SqlParserPos.java index 0e321d1..80d9cc9 100644 --- a/core/src/main/java/org/apache/calcite/sql/parser/SqlParserPos.java +++ b/core/src/main/java/org/apache/calcite/sql/parser/SqlParserPos.java @@ -20,8 +20,10 @@ import org.apache.calcite.sql.SqlNode; import com.google.common.base.Function; import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; import java.io.Serializable; +import java.util.AbstractList; import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -44,6 +46,13 @@ public class SqlParserPos implements Serializable { private static final long serialVersionUID = 1L; + private static final Function<SqlNode, SqlParserPos> NODE_TO_POS = + new Function<SqlNode, SqlParserPos>() { + public SqlParserPos apply(SqlNode input) { + return input.getParserPosition(); + } + }; + //~ Instance fields -------------------------------------------------------- private final int lineNumber; @@ -59,10 +68,7 @@ public class SqlParserPos implements Serializable { public SqlParserPos( int lineNumber, int columnNumber) { - this.lineNumber = lineNumber; - this.columnNumber = columnNumber; - this.endLineNumber = lineNumber; - this.endColumnNumber = columnNumber; + this(lineNumber, columnNumber, lineNumber, columnNumber); } /** @@ -77,6 +83,9 @@ public class SqlParserPos implements Serializable { this.columnNumber = startColumnNumber; this.endLineNumber = endLineNumber; this.endColumnNumber = endColumnNumber; + assert startLineNumber < endLineNumber + || startLineNumber == endLineNumber + && startColumnNumber <= endColumnNumber; } //~ Methods ---------------------------------------------------------------- @@ -165,34 +174,68 @@ public class SqlParserPos implements Serializable { * Combines the parser positions of an array of nodes to create a position * which spans from the beginning of the first to the end of the last. */ - public static SqlParserPos sum(SqlNode[] nodes) { - final Iterable<SqlParserPos> poses = toPos(Arrays.asList(nodes)); - return sum(poses, Integer.MAX_VALUE, Integer.MAX_VALUE, -1, -1); + public static SqlParserPos sum(final SqlNode[] nodes) { + return sum(toPos(nodes)); + } + + private static List<SqlParserPos> toPos(final SqlNode[] nodes) { + return new AbstractList<SqlParserPos>() { + public SqlParserPos get(int index) { + return nodes[index].getParserPosition(); + } + public int size() { + return nodes.length; + } + }; } private static Iterable<SqlParserPos> toPos(Iterable<SqlNode> nodes) { - return Iterables.transform(nodes, - new Function<SqlNode, SqlParserPos>() { - public SqlParserPos apply(SqlNode input) { - return input.getParserPosition(); - } - }); + return Iterables.transform(nodes, NODE_TO_POS); } /** * Combines the parser positions of a list of nodes to create a position * which spans from the beginning of the first to the end of the last. */ - public static SqlParserPos sum(List<? extends SqlNode> nodes) { - return sum(nodes.toArray(new SqlNode[nodes.size()])); + public static SqlParserPos sum(final List<? extends SqlNode> nodes) { + return sum(Lists.transform(nodes, NODE_TO_POS)); } /** - * Combines an array of parser positions to create a position which spans + * Combines an iterable of parser positions to create a position which spans * from the beginning of the first to the end of the last. */ public static SqlParserPos sum(Iterable<SqlParserPos> poses) { - return sum(poses, Integer.MAX_VALUE, Integer.MAX_VALUE, -1, -1); + final List<SqlParserPos> list = + poses instanceof List + ? (List<SqlParserPos>) poses + : Lists.newArrayList(poses); + return sum_(list); + } + + /** + * Combines a list of parser positions to create a position which spans + * from the beginning of the first to the end of the last. + */ + private static SqlParserPos sum_(final List<SqlParserPos> positions) { + switch (positions.size()) { + case 0: + throw new AssertionError(); + case 1: + return positions.get(0); + default: + final List<SqlParserPos> poses = new AbstractList<SqlParserPos>() { + public SqlParserPos get(int index) { + return positions.get(index + 1); + } + public int size() { + return positions.size() - 1; + } + }; + final SqlParserPos p = positions.get(0); + return sum(poses, p.lineNumber, p.columnNumber, p.endLineNumber, + p.endColumnNumber); + } } /** http://git-wip-us.apache.org/repos/asf/calcite/blob/d6193040/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java index 65d53a2..a035d34 100644 --- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java +++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java @@ -3889,7 +3889,8 @@ public class SqlValidatorImpl implements SqlValidatorWithHints { final RelDataType logicalSourceRowType = getLogicalSourceRowType(sourceRowType, insert); - checkFieldCount(insert, table, logicalSourceRowType, logicalTargetRowType); + checkFieldCount(insert.getTargetTable(), table, logicalSourceRowType, + logicalTargetRowType); checkTypeAssignment(logicalSourceRowType, logicalTargetRowType, insert); http://git-wip-us.apache.org/repos/asf/calcite/blob/d6193040/core/src/test/codegen/includes/parserImpls.ftl ---------------------------------------------------------------------- diff --git a/core/src/test/codegen/includes/parserImpls.ftl b/core/src/test/codegen/includes/parserImpls.ftl index 56a4099..cea63a4 100644 --- a/core/src/test/codegen/includes/parserImpls.ftl +++ b/core/src/test/codegen/includes/parserImpls.ftl @@ -16,7 +16,7 @@ --> -SqlAlter SqlUploadJarNode(SqlParserPos pos, String scope) : +SqlAlter SqlUploadJarNode(Span s, String scope) : { SqlNode jarPath; final List<SqlNode> jarPathsList; @@ -32,18 +32,18 @@ SqlAlter SqlUploadJarNode(SqlParserPos pos, String scope) : } )* { - return new SqlUploadJarNode(pos.plus(getPos()), scope, jarPathsList); + return new SqlUploadJarNode(s.end(this), scope, jarPathsList); } } -SqlCreate SqlCreateTable(SqlParserPos pos, boolean replace) : +SqlCreate SqlCreateTable(Span s, boolean replace) : { final SqlIdentifier id; final SqlNodeList columnList; } { <TABLE> id = CompoundIdentifier() columnList = ExtendList() { - return new SqlCreateTable(pos, id, columnList); + return new SqlCreateTable(s.end(columnList), id, columnList); } } http://git-wip-us.apache.org/repos/asf/calcite/blob/d6193040/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java b/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java index d6e8d75..b3dafec 100644 --- a/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java +++ b/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java @@ -3575,8 +3575,8 @@ public class SqlParserTest { "nullif(v1,v2)", "(NULLIF(`V1`, `V2`))"); checkExpFails( - "1 ^+^ nullif + 3", - "(?s)Encountered \"\\+ nullif \\+\" at line 1, column 3.*"); + "1 + ^nullif^ + 3", + "(?s)Encountered \"nullif \\+\" at line 1, column 5.*"); } @Test public void testCoalesce() { http://git-wip-us.apache.org/repos/asf/calcite/blob/d6193040/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java index c902669..30481d6 100644 --- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java +++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java @@ -5332,7 +5332,7 @@ public class SqlValidatorTest extends SqlValidatorTestCase { @Test public void testCrossJoinUsingFails() { checkFails( - "select * from emp cross join dept ^using (deptno)^", + "select * from emp cross join dept ^using^ (deptno)", "Cannot specify condition \\(NATURAL keyword, or ON or USING clause\\) following CROSS JOIN"); } @@ -5380,7 +5380,7 @@ public class SqlValidatorTest extends SqlValidatorTestCase { @Test public void testCrossJoinOnFails() { checkFails( "select * from emp cross join dept\n" - + " ^on emp.deptno = dept.deptno^", + + " ^on^ emp.deptno = dept.deptno", "Cannot specify condition \\(NATURAL keyword, or ON or USING clause\\) following CROSS JOIN"); } @@ -6684,7 +6684,7 @@ public class SqlValidatorTest extends SqlValidatorTestCase { .fails("Expression 'EMPNO' is not being grouped"); // in OVER clause with more than one level of nesting - checkFails("select ^avg(sum(min(sal))) OVER (partition by deptno)^\n" + checkFails("select ^avg(sum(min(sal)))^ OVER (partition by deptno)\n" + "from emp group by deptno", ERR_NESTED_AGG); @@ -9582,12 +9582,14 @@ public class SqlValidatorTest extends SqlValidatorTestCase { } @Test public void testInsertExtendedColumnModifiableViewFailColumnCount() { - tester.checkQueryFails("insert into EMP_MODIFIABLEVIEW2(\"rank\" INT, extra2 BOOLEAN^)^" - + " values ('nom', 1, 'job', 0, true, 0, false, timestamp '1970-01-01 00:00:00', 1, 1," - + " 1)", + final String sql0 = "insert into ^EMP_MODIFIABLEVIEW2(\"rank\" INT, extra2 BOOLEAN)^" + + " values ('nom', 1, 'job', 0, true, 0, false," + + " timestamp '1970-01-01 00:00:00', 1, 1, 1)"; + tester.checkQueryFails(sql0, "Number of INSERT target columns \\(12\\) does not equal number of source items \\(11\\)"); - tester.checkQueryFails("insert into EMP_MODIFIABLEVIEW2(\"rank\" INT, extra2 BOOLEAN^)^" - + " (deptno, empno, ename, extra2, \"rank\") values (?, 10, '2', true)", + final String sql1 = "insert into ^EMP_MODIFIABLEVIEW2(\"rank\" INT, extra2 BOOLEAN)^" + + " (deptno, empno, ename, extra2, \"rank\") values (?, 10, '2', true)"; + tester.checkQueryFails(sql1, "Number of INSERT target columns \\(5\\) does not equal number of source items \\(4\\)"); } http://git-wip-us.apache.org/repos/asf/calcite/blob/d6193040/piglet/src/main/javacc/PigletParser.jj ---------------------------------------------------------------------- diff --git a/piglet/src/main/javacc/PigletParser.jj b/piglet/src/main/javacc/PigletParser.jj index 069400d..d11b819 100644 --- a/piglet/src/main/javacc/PigletParser.jj +++ b/piglet/src/main/javacc/PigletParser.jj @@ -89,11 +89,11 @@ JAVACODE SqlParserPos pos() { } JAVACODE SqlParserPos pos2(SqlParserPos p) { - return pos().plus(p); + return p.plus(pos()); } JAVACODE SqlParserPos pos3(Node n) { - return pos().plus(n.pos); + return n.pos.plus(pos()); } /** http://git-wip-us.apache.org/repos/asf/calcite/blob/d6193040/site/_docs/history.md ---------------------------------------------------------------------- diff --git a/site/_docs/history.md b/site/_docs/history.md index 8c8d17b..e8c327d 100644 --- a/site/_docs/history.md +++ b/site/_docs/history.md @@ -38,6 +38,12 @@ Guava versions 14.0 to 21.0; Druid version 0.10.0; other software versions as specified in `pom.xml`. +Breaking changes: + +* [<a href="https://issues.apache.org/jira/browse/CALCITE-1788">CALCITE-1788</a>] + (Simplify handling of position in the parser) + Requires changes in the parameter lists of parser extension methods + ## <a href="https://github.com/apache/calcite/releases/tag/calcite-1.12.0">1.12.0</a> / 2017-03-24 {: #v1-12-0} http://git-wip-us.apache.org/repos/asf/calcite/blob/d6193040/site/_docs/reference.md ---------------------------------------------------------------------- diff --git a/site/_docs/reference.md b/site/_docs/reference.md index 88a3bb0..ab6ec62 100644 --- a/site/_docs/reference.md +++ b/site/_docs/reference.md @@ -1698,4 +1698,4 @@ and *minRepeat* and *maxRepeat* are non-negative integers. The following clauses are not implemented: * `PARTITION BY` -* `ORDER BY` \ No newline at end of file +* `ORDER BY`
