View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.shardingsphere.sql.parser.clickhouse.visitor.statement;
19  
20  import lombok.AccessLevel;
21  import lombok.Getter;
22  import lombok.RequiredArgsConstructor;
23  import org.antlr.v4.runtime.ParserRuleContext;
24  import org.antlr.v4.runtime.Token;
25  import org.antlr.v4.runtime.misc.Interval;
26  import org.antlr.v4.runtime.tree.TerminalNode;
27  import org.apache.shardingsphere.infra.database.core.type.DatabaseType;
28  import org.apache.shardingsphere.sql.parser.api.ASTNode;
29  import org.apache.shardingsphere.sql.parser.autogen.ClickHouseStatementBaseVisitor;
30  import org.apache.shardingsphere.sql.parser.autogen.ClickHouseStatementParser;
31  import org.apache.shardingsphere.sql.parser.statement.core.enums.AggregationType;
32  import org.apache.shardingsphere.sql.parser.statement.core.enums.OrderDirection;
33  import org.apache.shardingsphere.sql.parser.statement.core.enums.ParameterMarkerType;
34  import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.column.ColumnSegment;
35  import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.BetweenExpression;
36  import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.BinaryOperationExpression;
37  import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.ExpressionSegment;
38  import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.FunctionSegment;
39  import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.InExpression;
40  import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.ListExpression;
41  import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.NotExpression;
42  import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.complex.CommonExpressionSegment;
43  import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.simple.LiteralExpressionSegment;
44  import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.simple.ParameterMarkerExpressionSegment;
45  import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.subquery.SubqueryExpressionSegment;
46  import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.subquery.SubquerySegment;
47  import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.AggregationDistinctProjectionSegment;
48  import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.AggregationProjectionSegment;
49  import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.item.ExpressionProjectionSegment;
50  import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.order.OrderBySegment;
51  import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.order.item.ColumnOrderByItemSegment;
52  import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.order.item.IndexOrderByItemSegment;
53  import org.apache.shardingsphere.sql.parser.statement.core.segment.dml.order.item.OrderByItemSegment;
54  import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.DataTypeLengthSegment;
55  import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.DataTypeSegment;
56  import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.OwnerSegment;
57  import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.ParameterMarkerSegment;
58  import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.SimpleTableSegment;
59  import org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.TableNameSegment;
60  import org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.SelectStatement;
61  import org.apache.shardingsphere.sql.parser.statement.core.util.SQLUtils;
62  import org.apache.shardingsphere.sql.parser.statement.core.value.collection.CollectionValue;
63  import org.apache.shardingsphere.sql.parser.statement.core.value.identifier.IdentifierValue;
64  import org.apache.shardingsphere.sql.parser.statement.core.value.keyword.KeywordValue;
65  import org.apache.shardingsphere.sql.parser.statement.core.value.literal.impl.BooleanLiteralValue;
66  import org.apache.shardingsphere.sql.parser.statement.core.value.literal.impl.NullLiteralValue;
67  import org.apache.shardingsphere.sql.parser.statement.core.value.literal.impl.NumberLiteralValue;
68  import org.apache.shardingsphere.sql.parser.statement.core.value.literal.impl.OtherLiteralValue;
69  import org.apache.shardingsphere.sql.parser.statement.core.value.literal.impl.StringLiteralValue;
70  import org.apache.shardingsphere.sql.parser.statement.core.value.parametermarker.ParameterMarkerValue;
71  
72  import java.util.Collection;
73  import java.util.Collections;
74  import java.util.LinkedList;
75  import java.util.List;
76  import java.util.stream.Collectors;
77  
78  /**
79   * ClickHouse Statement visitor.
80   */
81  @RequiredArgsConstructor
82  @Getter(AccessLevel.PROTECTED)
83  public abstract class ClickHouseStatementVisitor extends ClickHouseStatementBaseVisitor<ASTNode> {
84      
85      private final DatabaseType databaseType;
86      
87      private final Collection<ParameterMarkerSegment> parameterMarkerSegments = new LinkedList<>();
88      
89      @Override
90      public final ASTNode visitParameterMarker(final ClickHouseStatementParser.ParameterMarkerContext ctx) {
91          return new ParameterMarkerValue(parameterMarkerSegments.size(), ParameterMarkerType.QUESTION);
92      }
93      
94      @Override
95      public final ASTNode visitLiterals(final ClickHouseStatementParser.LiteralsContext ctx) {
96          if (null != ctx.stringLiterals()) {
97              return visit(ctx.stringLiterals());
98          }
99          if (null != ctx.numberLiterals()) {
100             return visit(ctx.numberLiterals());
101         }
102         if (null != ctx.hexadecimalLiterals()) {
103             return visit(ctx.hexadecimalLiterals());
104         }
105         if (null != ctx.bitValueLiterals()) {
106             return visit(ctx.bitValueLiterals());
107         }
108         if (null != ctx.booleanLiterals()) {
109             return visit(ctx.booleanLiterals());
110         }
111         if (null != ctx.nullValueLiterals()) {
112             return visit(ctx.nullValueLiterals());
113         }
114         throw new IllegalStateException("Literals must have string, number, dateTime, hex, bit, boolean or null.");
115     }
116     
117     @Override
118     public final ASTNode visitStringLiterals(final ClickHouseStatementParser.StringLiteralsContext ctx) {
119         return new StringLiteralValue(ctx.getText());
120     }
121     
122     @Override
123     public final ASTNode visitNumberLiterals(final ClickHouseStatementParser.NumberLiteralsContext ctx) {
124         return new NumberLiteralValue(ctx.getText());
125     }
126     
127     @Override
128     public final ASTNode visitHexadecimalLiterals(final ClickHouseStatementParser.HexadecimalLiteralsContext ctx) {
129         // TODO deal with hexadecimalLiterals
130         return new OtherLiteralValue(ctx.getText());
131     }
132     
133     @Override
134     public final ASTNode visitBitValueLiterals(final ClickHouseStatementParser.BitValueLiteralsContext ctx) {
135         // TODO deal with bitValueLiterals
136         return new OtherLiteralValue(ctx.getText());
137     }
138     
139     @Override
140     public final ASTNode visitBooleanLiterals(final ClickHouseStatementParser.BooleanLiteralsContext ctx) {
141         
142         return new BooleanLiteralValue(ctx.getText());
143     }
144     
145     @Override
146     public final ASTNode visitNullValueLiterals(final ClickHouseStatementParser.NullValueLiteralsContext ctx) {
147         return new NullLiteralValue(ctx.getText());
148     }
149     
150     @Override
151     public final ASTNode visitIdentifier(final ClickHouseStatementParser.IdentifierContext ctx) {
152         ClickHouseStatementParser.UnreservedWordContext unreservedWord = ctx.unreservedWord();
153         return null == unreservedWord ? new IdentifierValue(ctx.getText()) : visit(unreservedWord);
154     }
155     
156     @Override
157     public final ASTNode visitUnreservedWord(final ClickHouseStatementParser.UnreservedWordContext ctx) {
158         return new IdentifierValue(ctx.getText());
159     }
160     
161     @Override
162     public final ASTNode visitSchemaName(final ClickHouseStatementParser.SchemaNameContext ctx) {
163         return visit(ctx.identifier());
164     }
165     
166     @Override
167     public final ASTNode visitTableName(final ClickHouseStatementParser.TableNameContext ctx) {
168         SimpleTableSegment result = new SimpleTableSegment(new TableNameSegment(ctx.name().getStart().getStartIndex(), ctx.name().getStop().getStopIndex(), (IdentifierValue) visit(ctx.name())));
169         ClickHouseStatementParser.OwnerContext owner = ctx.owner();
170         if (null != owner) {
171             result.setOwner(new OwnerSegment(owner.getStart().getStartIndex(), owner.getStop().getStopIndex(), (IdentifierValue) visit(owner.identifier())));
172         }
173         return result;
174     }
175     
176     @Override
177     public final ASTNode visitColumnName(final ClickHouseStatementParser.ColumnNameContext ctx) {
178         ColumnSegment result = new ColumnSegment(ctx.getStart().getStartIndex(), ctx.getStop().getStopIndex(), (IdentifierValue) visit(ctx.name()));
179         ClickHouseStatementParser.OwnerContext owner = ctx.owner();
180         if (null != owner) {
181             result.setOwner(new OwnerSegment(owner.getStart().getStartIndex(), owner.getStop().getStopIndex(), (IdentifierValue) visit(owner.identifier())));
182         }
183         return result;
184     }
185     
186     @Override
187     public final ASTNode visitTableNames(final ClickHouseStatementParser.TableNamesContext ctx) {
188         CollectionValue<SimpleTableSegment> result = new CollectionValue<>();
189         for (ClickHouseStatementParser.TableNameContext each : ctx.tableName()) {
190             result.getValue().add((SimpleTableSegment) visit(each));
191         }
192         return result;
193     }
194     
195     @Override
196     public final ASTNode visitColumnNames(final ClickHouseStatementParser.ColumnNamesContext ctx) {
197         CollectionValue<ColumnSegment> result = new CollectionValue<>();
198         for (ClickHouseStatementParser.ColumnNameContext each : ctx.columnName()) {
199             result.getValue().add((ColumnSegment) visit(each));
200         }
201         return result;
202     }
203     
204     @Override
205     public final ASTNode visitExpr(final ClickHouseStatementParser.ExprContext ctx) {
206         if (null != ctx.booleanPrimary()) {
207             return visit(ctx.booleanPrimary());
208         }
209         if (null != ctx.LP_()) {
210             return visit(ctx.expr(0));
211         }
212         if (null != ctx.andOperator()) {
213             return createBinaryOperationExpression(ctx, ctx.andOperator().getText());
214         }
215         if (null != ctx.orOperator()) {
216             return createBinaryOperationExpression(ctx, ctx.orOperator().getText());
217         }
218         return new NotExpression(ctx.start.getStartIndex(), ctx.stop.getStopIndex(), (ExpressionSegment) visit(ctx.expr(0)), false);
219     }
220     
221     private ASTNode createBinaryOperationExpression(final ClickHouseStatementParser.ExprContext ctx, final String operator) {
222         ExpressionSegment left = (ExpressionSegment) visit(ctx.expr(0));
223         ExpressionSegment right = (ExpressionSegment) visit(ctx.expr(1));
224         String text = ctx.start.getInputStream().getText(new Interval(ctx.start.getStartIndex(), ctx.stop.getStopIndex()));
225         return new BinaryOperationExpression(ctx.start.getStartIndex(), ctx.stop.getStopIndex(), left, right, operator, text);
226     }
227     
228     @Override
229     public final ASTNode visitBooleanPrimary(final ClickHouseStatementParser.BooleanPrimaryContext ctx) {
230         if (null != ctx.IS()) {
231             String rightText = "";
232             if (null != ctx.NOT()) {
233                 rightText = rightText.concat(ctx.start.getInputStream().getText(new Interval(ctx.NOT().getSymbol().getStartIndex(),
234                         ctx.NOT().getSymbol().getStopIndex()))).concat(" ");
235             }
236             Token operatorToken = null;
237             if (null != ctx.NULL()) {
238                 operatorToken = ctx.NULL().getSymbol();
239             }
240             if (null != ctx.TRUE()) {
241                 operatorToken = ctx.TRUE().getSymbol();
242             }
243             if (null != ctx.FALSE()) {
244                 operatorToken = ctx.FALSE().getSymbol();
245             }
246             int startIndex = null == operatorToken ? ctx.IS().getSymbol().getStopIndex() + 2 : operatorToken.getStartIndex();
247             rightText = rightText.concat(ctx.start.getInputStream().getText(new Interval(startIndex, ctx.stop.getStopIndex())));
248             ExpressionSegment right = new LiteralExpressionSegment(ctx.IS().getSymbol().getStopIndex() + 2, ctx.stop.getStopIndex(), rightText);
249             String text = ctx.start.getInputStream().getText(new Interval(ctx.start.getStartIndex(), ctx.stop.getStopIndex()));
250             ExpressionSegment left = (ExpressionSegment) visit(ctx.booleanPrimary());
251             String operator = "IS";
252             return new BinaryOperationExpression(ctx.start.getStartIndex(), ctx.stop.getStopIndex(), left, right, operator, text);
253         }
254         if (null != ctx.comparisonOperator() || null != ctx.SAFE_EQ_()) {
255             return createCompareSegment(ctx);
256         }
257         return visit(ctx.predicate());
258     }
259     
260     private ASTNode createCompareSegment(final ClickHouseStatementParser.BooleanPrimaryContext ctx) {
261         ExpressionSegment left = (ExpressionSegment) visit(ctx.booleanPrimary());
262         ExpressionSegment right;
263         if (null != ctx.predicate()) {
264             right = (ExpressionSegment) visit(ctx.predicate());
265         } else {
266             right = (ExpressionSegment) visit(ctx.subquery());
267         }
268         String operator = null == ctx.SAFE_EQ_() ? ctx.comparisonOperator().getText() : ctx.SAFE_EQ_().getText();
269         String text = ctx.start.getInputStream().getText(new Interval(ctx.start.getStartIndex(), ctx.stop.getStopIndex()));
270         return new BinaryOperationExpression(ctx.start.getStartIndex(), ctx.stop.getStopIndex(), left, right, operator, text);
271     }
272     
273     @Override
274     public final ASTNode visitPredicate(final ClickHouseStatementParser.PredicateContext ctx) {
275         if (null != ctx.IN()) {
276             return createInSegment(ctx);
277         }
278         if (null != ctx.BETWEEN()) {
279             return createBetweenSegment(ctx);
280         }
281         if (null != ctx.LIKE()) {
282             return createBinaryOperationExpressionFromLike(ctx);
283         }
284         return visit(ctx.bitExpr(0));
285     }
286     
287     private BinaryOperationExpression createBinaryOperationExpressionFromLike(final ClickHouseStatementParser.PredicateContext ctx) {
288         ExpressionSegment left = (ExpressionSegment) visit(ctx.bitExpr(0));
289         ListExpression right = new ListExpression(ctx.simpleExpr(0).start.getStartIndex(), ctx.simpleExpr().get(ctx.simpleExpr().size() - 1).stop.getStopIndex());
290         for (ClickHouseStatementParser.SimpleExprContext each : ctx.simpleExpr()) {
291             right.getItems().add((ExpressionSegment) visit(each));
292         }
293         String operator = null == ctx.NOT() ? "LIKE" : "NOT LIKE";
294         String text = ctx.start.getInputStream().getText(new Interval(ctx.start.getStartIndex(), ctx.stop.getStopIndex()));
295         return new BinaryOperationExpression(ctx.start.getStartIndex(), ctx.stop.getStopIndex(), left, right, operator, text);
296     }
297     
298     private InExpression createInSegment(final ClickHouseStatementParser.PredicateContext ctx) {
299         ExpressionSegment left = (ExpressionSegment) visit(ctx.bitExpr(0));
300         ExpressionSegment right;
301         if (null != ctx.subquery()) {
302             right = new SubqueryExpressionSegment(new SubquerySegment(ctx.subquery().start.getStartIndex(), ctx.subquery().stop.getStopIndex(), (SelectStatement) visit(ctx.subquery()),
303                     getOriginalText(ctx.subquery())));
304         } else {
305             ListExpression listExpression = new ListExpression(ctx.LP_().getSymbol().getStartIndex(), ctx.RP_().getSymbol().getStopIndex());
306             for (ClickHouseStatementParser.ExprContext each : ctx.expr()) {
307                 listExpression.getItems().add((ExpressionSegment) visit(each));
308             }
309             right = listExpression;
310         }
311         boolean not = null != ctx.NOT();
312         return new InExpression(ctx.start.getStartIndex(), ctx.stop.getStopIndex(), left, right, not);
313     }
314     
315     private BetweenExpression createBetweenSegment(final ClickHouseStatementParser.PredicateContext ctx) {
316         ExpressionSegment left = (ExpressionSegment) visit(ctx.bitExpr(0));
317         ExpressionSegment between = (ExpressionSegment) visit(ctx.bitExpr(1));
318         ExpressionSegment and = (ExpressionSegment) visit(ctx.predicate());
319         boolean not = null != ctx.NOT();
320         return new BetweenExpression(ctx.start.getStartIndex(), ctx.stop.getStopIndex(), left, between, and, not);
321     }
322     
323     @Override
324     public final ASTNode visitBitExpr(final ClickHouseStatementParser.BitExprContext ctx) {
325         if (null != ctx.simpleExpr()) {
326             return createExpressionSegment(visit(ctx.simpleExpr()), ctx);
327         }
328         ExpressionSegment left = (ExpressionSegment) visit(ctx.getChild(0));
329         ExpressionSegment right = (ExpressionSegment) visit(ctx.getChild(2));
330         String operator = ctx.getChild(1).getText();
331         String text = ctx.start.getInputStream().getText(new Interval(ctx.start.getStartIndex(), ctx.stop.getStopIndex()));
332         return new BinaryOperationExpression(ctx.start.getStartIndex(), ctx.stop.getStopIndex(), left, right, operator, text);
333     }
334     
335     private ASTNode createExpressionSegment(final ASTNode astNode, final ParserRuleContext context) {
336         if (astNode instanceof StringLiteralValue) {
337             return new LiteralExpressionSegment(context.start.getStartIndex(), context.stop.getStopIndex(), ((StringLiteralValue) astNode).getValue());
338         }
339         if (astNode instanceof NumberLiteralValue) {
340             return new LiteralExpressionSegment(context.start.getStartIndex(), context.stop.getStopIndex(), ((NumberLiteralValue) astNode).getValue());
341         }
342         if (astNode instanceof BooleanLiteralValue) {
343             return new LiteralExpressionSegment(context.start.getStartIndex(), context.stop.getStopIndex(), ((BooleanLiteralValue) astNode).getValue());
344         }
345         if (astNode instanceof ParameterMarkerValue) {
346             ParameterMarkerValue parameterMarker = (ParameterMarkerValue) astNode;
347             ParameterMarkerExpressionSegment segment = new ParameterMarkerExpressionSegment(context.start.getStartIndex(), context.stop.getStopIndex(),
348                     parameterMarker.getValue(), parameterMarker.getType());
349             parameterMarkerSegments.add(segment);
350             return segment;
351         }
352         if (astNode instanceof SubquerySegment) {
353             return new SubqueryExpressionSegment((SubquerySegment) astNode);
354         }
355         if (astNode instanceof OtherLiteralValue) {
356             return new CommonExpressionSegment(context.getStart().getStartIndex(), context.getStop().getStopIndex(), ((OtherLiteralValue) astNode).getValue());
357         }
358         return astNode;
359     }
360     
361     @Override
362     public final ASTNode visitSimpleExpr(final ClickHouseStatementParser.SimpleExprContext ctx) {
363         int startIndex = ctx.getStart().getStartIndex();
364         int stopIndex = ctx.getStop().getStopIndex();
365         if (null != ctx.subquery()) {
366             return new SubquerySegment(startIndex, stopIndex, (SelectStatement) visit(ctx.subquery()), getOriginalText(ctx.subquery()));
367         }
368         if (null != ctx.parameterMarker()) {
369             ParameterMarkerValue parameterMarker = (ParameterMarkerValue) visit(ctx.parameterMarker());
370             ParameterMarkerExpressionSegment segment = new ParameterMarkerExpressionSegment(startIndex, stopIndex, parameterMarker.getValue(), parameterMarker.getType());
371             parameterMarkerSegments.add(segment);
372             return segment;
373         }
374         if (null != ctx.literals()) {
375             return SQLUtils.createLiteralExpression(visit(ctx.literals()), startIndex, stopIndex, ctx.literals().start.getInputStream().getText(new Interval(startIndex, stopIndex)));
376         }
377         if (null != ctx.functionCall()) {
378             return visit(ctx.functionCall());
379         }
380         if (null != ctx.columnName()) {
381             return visit(ctx.columnName());
382         }
383         return new CommonExpressionSegment(startIndex, stopIndex, ctx.getText());
384     }
385     
386     @Override
387     public final ASTNode visitIntervalExpression(final ClickHouseStatementParser.IntervalExpressionContext ctx) {
388         calculateParameterCount(Collections.singleton(ctx.expr()));
389         return new ExpressionProjectionSegment(ctx.getStart().getStartIndex(), ctx.getStop().getStopIndex(), getOriginalText(ctx));
390     }
391     
392     @Override
393     public final ASTNode visitFunctionCall(final ClickHouseStatementParser.FunctionCallContext ctx) {
394         if (null != ctx.aggregationFunction()) {
395             return visit(ctx.aggregationFunction());
396         }
397         if (null != ctx.specialFunction()) {
398             return visit(ctx.specialFunction());
399         }
400         if (null != ctx.regularFunction()) {
401             return visit(ctx.regularFunction());
402         }
403         throw new IllegalStateException("FunctionCallContext must have aggregationFunction, regularFunction or specialFunction.");
404     }
405     
406     @Override
407     public final ASTNode visitAggregationFunction(final ClickHouseStatementParser.AggregationFunctionContext ctx) {
408         String aggregationType = ctx.aggregationFunctionName().getText();
409         return AggregationType.isAggregationType(aggregationType)
410                 ? createAggregationSegment(ctx, aggregationType)
411                 : new ExpressionProjectionSegment(ctx.getStart().getStartIndex(), ctx.getStop().getStopIndex(), getOriginalText(ctx));
412     }
413     
414     private ASTNode createAggregationSegment(final ClickHouseStatementParser.AggregationFunctionContext ctx, final String aggregationType) {
415         AggregationType type = AggregationType.valueOf(aggregationType.toUpperCase());
416         if (null != ctx.distinct()) {
417             AggregationDistinctProjectionSegment result =
418                     new AggregationDistinctProjectionSegment(ctx.getStart().getStartIndex(), ctx.getStop().getStopIndex(), type, getOriginalText(ctx), getDistinctExpression(ctx));
419             result.getParameters().addAll(getExpressions(ctx));
420             return result;
421         }
422         AggregationProjectionSegment result = new AggregationProjectionSegment(ctx.getStart().getStartIndex(), ctx.getStop().getStopIndex(), type, getOriginalText(ctx));
423         result.getParameters().addAll(getExpressions(ctx));
424         return result;
425     }
426     
427     private Collection<ExpressionSegment> getExpressions(final ClickHouseStatementParser.AggregationFunctionContext ctx) {
428         if (null == ctx.expr()) {
429             return Collections.emptyList();
430         }
431         Collection<ExpressionSegment> result = new LinkedList<>();
432         for (ClickHouseStatementParser.ExprContext each : ctx.expr()) {
433             result.add((ExpressionSegment) visit(each));
434         }
435         return result;
436     }
437     
438     private String getDistinctExpression(final ClickHouseStatementParser.AggregationFunctionContext ctx) {
439         StringBuilder result = new StringBuilder();
440         for (int i = 3; i < ctx.getChildCount() - 1; i++) {
441             result.append(ctx.getChild(i).getText());
442         }
443         return result.toString();
444     }
445     
446     @Override
447     public final ASTNode visitSpecialFunction(final ClickHouseStatementParser.SpecialFunctionContext ctx) {
448         if (null != ctx.castFunction()) {
449             return visit(ctx.castFunction());
450         }
451         return new FunctionSegment(ctx.getStart().getStartIndex(), ctx.getStop().getStopIndex(), ctx.getChild(0).getChild(0).getText(), getOriginalText(ctx));
452     }
453     
454     @Override
455     public final ASTNode visitCastFunction(final ClickHouseStatementParser.CastFunctionContext ctx) {
456         calculateParameterCount(Collections.singleton(ctx.expr()));
457         FunctionSegment result = new FunctionSegment(ctx.getStart().getStartIndex(), ctx.getStop().getStopIndex(), ctx.CAST().getText(), getOriginalText(ctx));
458         ASTNode exprSegment = visit(ctx.expr());
459         if (exprSegment instanceof ColumnSegment) {
460             result.getParameters().add((ColumnSegment) exprSegment);
461         } else if (exprSegment instanceof LiteralExpressionSegment) {
462             result.getParameters().add((LiteralExpressionSegment) exprSegment);
463         }
464         result.getParameters().add((DataTypeSegment) visit(ctx.dataType()));
465         return result;
466     }
467     
468     @Override
469     public final ASTNode visitRegularFunction(final ClickHouseStatementParser.RegularFunctionContext ctx) {
470         FunctionSegment result = new FunctionSegment(ctx.getStart().getStartIndex(), ctx.getStop().getStopIndex(), ctx.regularFunctionName().getText(), getOriginalText(ctx));
471         Collection<ExpressionSegment> expressionSegments = ctx.expr().stream().map(each -> (ExpressionSegment) visit(each)).collect(Collectors.toList());
472         result.getParameters().addAll(expressionSegments);
473         return result;
474     }
475     
476     @Override
477     public final ASTNode visitDataTypeName(final ClickHouseStatementParser.DataTypeNameContext ctx) {
478         Collection<String> dataTypeNames = new LinkedList<>();
479         for (int i = 0; i < ctx.getChildCount(); i++) {
480             dataTypeNames.add(ctx.getChild(i).getText());
481         }
482         return new KeywordValue(String.join(" ", dataTypeNames));
483     }
484     
485     // TODO :FIXME, sql case id: insert_with_str_to_date
486     private void calculateParameterCount(final Collection<ClickHouseStatementParser.ExprContext> exprContexts) {
487         for (ClickHouseStatementParser.ExprContext each : exprContexts) {
488             visit(each);
489         }
490     }
491     
492     @Override
493     public final ASTNode visitOrderByClause(final ClickHouseStatementParser.OrderByClauseContext ctx) {
494         Collection<OrderByItemSegment> items = new LinkedList<>();
495         for (ClickHouseStatementParser.OrderByItemContext each : ctx.orderByItem()) {
496             items.add((OrderByItemSegment) visit(each));
497         }
498         return new OrderBySegment(ctx.getStart().getStartIndex(), ctx.getStop().getStopIndex(), items);
499     }
500     
501     @Override
502     public final ASTNode visitOrderByItem(final ClickHouseStatementParser.OrderByItemContext ctx) {
503         OrderDirection orderDirection = null == ctx.DESC() ? OrderDirection.ASC : OrderDirection.DESC;
504         if (null != ctx.columnName()) {
505             ColumnSegment column = (ColumnSegment) visit(ctx.columnName());
506             return new ColumnOrderByItemSegment(column, orderDirection, null);
507         }
508         return new IndexOrderByItemSegment(ctx.numberLiterals().getStart().getStartIndex(), ctx.numberLiterals().getStop().getStopIndex(),
509                 SQLUtils.getExactlyNumber(ctx.numberLiterals().getText(), 10).intValue(), orderDirection, null);
510     }
511     
512     @Override
513     public final ASTNode visitDataType(final ClickHouseStatementParser.DataTypeContext ctx) {
514         DataTypeSegment result = new DataTypeSegment();
515         result.setDataTypeName(((KeywordValue) visit(ctx.dataTypeName())).getValue());
516         result.setStartIndex(ctx.start.getStartIndex());
517         result.setStopIndex(ctx.stop.getStopIndex());
518         if (null != ctx.dataTypeLength()) {
519             DataTypeLengthSegment dataTypeLengthSegment = (DataTypeLengthSegment) visit(ctx.dataTypeLength());
520             result.setDataLength(dataTypeLengthSegment);
521         }
522         return result;
523     }
524     
525     @Override
526     public final ASTNode visitDataTypeLength(final ClickHouseStatementParser.DataTypeLengthContext ctx) {
527         DataTypeLengthSegment result = new DataTypeLengthSegment();
528         result.setStartIndex(ctx.start.getStartIndex());
529         result.setStopIndex(ctx.stop.getStartIndex());
530         List<TerminalNode> numbers = ctx.NUMBER_();
531         if (numbers.size() == 1) {
532             result.setPrecision(Integer.parseInt(numbers.get(0).getText()));
533         }
534         if (numbers.size() == 2) {
535             result.setPrecision(Integer.parseInt(numbers.get(0).getText()));
536             result.setScale(Integer.parseInt(numbers.get(1).getText()));
537         }
538         return result;
539     }
540     
541     /**
542      * Get original text.
543      *
544      * @param ctx context
545      * @return original text
546      */
547     protected String getOriginalText(final ParserRuleContext ctx) {
548         return ctx.start.getInputStream().getText(new Interval(ctx.start.getStartIndex(), ctx.stop.getStopIndex()));
549     }
550     
551 }