private Regex getConstantPattern(LikePredicate node) { Regex result = likePatternCache.get(node); if (result == null) { StringLiteral pattern = (StringLiteral) node.getPattern(); StringLiteral escape = (StringLiteral) node.getEscape(); if (escape == null) { result = LikeFunctions.likePattern(pattern.getSlice()); } else { result = LikeFunctions.likePattern(pattern.getSlice(), escape.getSlice()); } likePatternCache.put(node, result); } return result; }
@Override protected RelationType visitShowColumns(ShowColumns showColumns, AnalysisContext context) { QualifiedObjectName tableName = createQualifiedObjectName(session, showColumns, showColumns.getTable()); if (!metadata.getView(session, tableName).isPresent() && !metadata.getTableHandle(session, tableName).isPresent()) { throw new SemanticException(MISSING_TABLE, showColumns, "Table '%s' does not exist", tableName); } Query query = simpleQuery( selectList( aliasedName("column_name", "Column"), aliasedName("data_type", "Type"), aliasedNullToEmpty("comment", "Comment")), from(tableName.getCatalogName(), TABLE_COLUMNS), logicalAnd( equal(nameReference("table_schema"), new StringLiteral(tableName.getSchemaName())), equal(nameReference("table_name"), new StringLiteral(tableName.getObjectName()))), ordering(ascending("ordinal_position"))); return process(query, context); }
private static PlanFragment createValuesPlan() { Symbol symbol = new Symbol("column"); PlanNodeId valuesNodeId = new PlanNodeId("plan"); PlanFragment planFragment = new PlanFragment( new PlanFragmentId("plan"), new ValuesNode(valuesNodeId, ImmutableList.of(symbol), ImmutableList.of(ImmutableList.of(new StringLiteral("foo")))), ImmutableMap.<Symbol, Type>of(symbol, VARCHAR), ImmutableList.of(symbol), PlanDistribution.SINGLE, valuesNodeId, Optional.empty()); return planFragment; }
@Test public void testLimitAll() { Query valuesQuery = query(values( row(new LongLiteral("1"), new StringLiteral("1")), row(new LongLiteral("2"), new StringLiteral("2")))); assertStatement("SELECT * FROM (VALUES (1, '1'), (2, '2')) LIMIT ALL", simpleQuery(selectList(new AllColumns()), subquery(valuesQuery), Optional.empty(), ImmutableList.of(), Optional.empty(), ImmutableList.of(), Optional.of("ALL"))); }
/** * Extracts the literal value from an expression (if expression is supported) * @param expression * @param state * @return a Long, Boolean, Double or String object */ private Object getLiteralValue(Expression expression, QueryState state){ if(expression instanceof LongLiteral) return ((LongLiteral)expression).getValue(); else if(expression instanceof BooleanLiteral) return ((BooleanLiteral)expression).getValue(); else if(expression instanceof DoubleLiteral) return ((DoubleLiteral)expression).getValue(); else if(expression instanceof StringLiteral) return ((StringLiteral)expression).getValue(); else if(expression instanceof ArithmeticUnaryExpression){ ArithmeticUnaryExpression unaryExp = (ArithmeticUnaryExpression)expression; Sign sign = unaryExp.getSign(); Number num = (Number)getLiteralValue(unaryExp.getValue(), state); if(sign == Sign.MINUS){ if(num instanceof Long) return -1*num.longValue(); else if(num instanceof Double) return -1*num.doubleValue(); else { state.addException("Unsupported numeric literal expression encountered : "+num.getClass()); return null; } } return num; } else if(expression instanceof FunctionCall){ FunctionCall fc = (FunctionCall)expression; if(fc.getName().toString().equals("now")) return new Date(); else state.addException("Function '"+fc.getName()+"' is not supported"); }else if(expression instanceof CurrentTime){ CurrentTime ct = (CurrentTime)expression; if(ct.getType() == CurrentTime.Type.DATE) return new LocalDate().toDate(); else if(ct.getType() == CurrentTime.Type.TIME) return new Date(new LocalTime(DateTimeZone.UTC).getMillisOfDay()); else if(ct.getType() == CurrentTime.Type.TIMESTAMP) return new Date(); else if(ct.getType() == CurrentTime.Type.LOCALTIME) return new Date(new LocalTime(DateTimeZone.UTC).getMillisOfDay()); else if(ct.getType() == CurrentTime.Type.LOCALTIMESTAMP) return new Date(); else state.addException("CurrentTime function '"+ct.getType()+"' is not supported"); }else state.addException("Literal type "+expression.getClass().getSimpleName()+" is not supported"); return null; }
private Object getObject(Literal literal){ Object value = null; if(literal instanceof LongLiteral) value = ((LongLiteral)literal).getValue(); else if(literal instanceof BooleanLiteral) value = ((BooleanLiteral)literal).getValue(); else if(literal instanceof DoubleLiteral) value = ((DoubleLiteral)literal).getValue(); else if(literal instanceof StringLiteral) value = ((StringLiteral)literal).getValue(); else if(literal instanceof TimeLiteral) value = ((TimeLiteral)literal).getValue(); else if(literal instanceof TimestampLiteral) value = ((TimestampLiteral)literal).getValue(); return value; }
private Object getLiteralValue(Expression expression) throws SQLException{ if(expression instanceof LongLiteral) return ((LongLiteral)expression).getValue(); else if(expression instanceof BooleanLiteral) return ((BooleanLiteral)expression).getValue(); else if(expression instanceof DoubleLiteral) return ((DoubleLiteral)expression).getValue(); else if(expression instanceof StringLiteral) return ((StringLiteral)expression).getValue(); throw new SQLException("Unsupported literal type: "+expression); }
@VisibleForTesting @NotNull public static Expression createFailureFunction(RuntimeException exception, Type type) { requireNonNull(exception, "Exception is null"); String failureInfo = JsonCodec.jsonCodec(FailureInfo.class).toJson(Failures.toFailure(exception).toFailureInfo()); FunctionCall jsonParse = new FunctionCall(QualifiedName.of("json_parse"), ImmutableList.of(new StringLiteral(failureInfo))); FunctionCall failureFunction = new FunctionCall(QualifiedName.of("fail"), ImmutableList.of(jsonParse)); return new Cast(failureFunction, type.getTypeSignature().toString()); }
@Override protected RelationType visitShowCatalogs(ShowCatalogs node, AnalysisContext context) { List<Expression> rows = metadata.getCatalogNames().keySet().stream() .map(name -> row(new StringLiteral(name))) .collect(toList()); Query query = simpleQuery( selectList(new AllColumns()), aliased(new Values(rows), "catalogs", ImmutableList.of("Catalog"))); return process(query, context); }
@Override protected RelationType visitShowFunctions(ShowFunctions node, AnalysisContext context) { ImmutableList.Builder<Expression> rows = ImmutableList.builder(); for (SqlFunction function : metadata.listFunctions()) { if (function.getSignature().getKind() == APPROXIMATE_AGGREGATE) { continue; } rows.add(row( new StringLiteral(function.getSignature().getName()), new StringLiteral(function.getSignature().getReturnType().toString()), new StringLiteral(Joiner.on(", ").join(function.getSignature().getArgumentTypes())), new StringLiteral(getFunctionType(function)), function.isDeterministic() ? TRUE_LITERAL : FALSE_LITERAL, new StringLiteral(nullToEmpty(function.getDescription())))); } Map<String, String> columns = ImmutableMap.<String, String>builder() .put("function_name", "Function") .put("return_type", "Return Type") .put("argument_types", "Argument Types") .put("function_type", "Function Type") .put("deterministic", "Deterministic") .put("description", "Description") .build(); Query query = simpleQuery( selectAll(columns.entrySet().stream() .map(entry -> aliasedName(entry.getKey(), entry.getValue())) .collect(toImmutableList())), aliased(new Values(rows.build()), "functions", ImmutableList.copyOf(columns.keySet())), ordering( ascending("function_name"), ascending("return_type"), ascending("argument_types"), ascending("function_type"))); return process(query, context); }
@Override protected RelationType visitShowSession(ShowSession node, AnalysisContext context) { ImmutableList.Builder<Expression> rows = ImmutableList.builder(); List<SessionPropertyValue> sessionProperties = metadata.getSessionPropertyManager().getAllSessionProperties(session); for (SessionPropertyValue sessionProperty : sessionProperties) { if (sessionProperty.isHidden()) { continue; } String value = sessionProperty.getValue(); String defaultValue = sessionProperty.getDefaultValue(); rows.add(row( new StringLiteral(sessionProperty.getFullyQualifiedName()), new StringLiteral(nullToEmpty(value)), new StringLiteral(nullToEmpty(defaultValue)), new StringLiteral(sessionProperty.getType()), new StringLiteral(sessionProperty.getDescription()), TRUE_LITERAL)); } // add bogus row so we can support empty sessions StringLiteral empty = new StringLiteral(""); rows.add(row(empty, empty, empty, empty, empty, FALSE_LITERAL)); Query query = simpleQuery( selectList( aliasedName("name", "Name"), aliasedName("value", "Value"), aliasedName("default", "Default"), aliasedName("type", "Type"), aliasedName("description", "Description")), aliased( new Values(rows.build()), "session", ImmutableList.of("name", "value", "default", "type", "description", "include")), nameReference("include")); return process(query, context); }
@Test public void testSetSession() throws Exception { testSetSession(new StringLiteral("baz"), "baz"); testSetSession(new FunctionCall(new QualifiedName("concat"), ImmutableList.of( new StringLiteral("ban"), new StringLiteral("ana"))), "banana"); }
private static void assertLike(byte[] value, String pattern, boolean expected) { Expression predicate = new LikePredicate( rawStringLiteral(Slices.wrappedBuffer(value)), new StringLiteral(pattern), null); assertEquals(evaluate(predicate), expected); }
private static StringLiteral rawStringLiteral(final Slice slice) { return new StringLiteral(slice.toStringUtf8()) { @Override public Slice getSlice() { return slice; } }; }
@Override public Node visitNormalize(SqlBaseParser.NormalizeContext context) { Expression str = (Expression) visit(context.valueExpression()); String normalForm = Optional.ofNullable(context.normalForm()).map(ParserRuleContext::getText).orElse("NFC"); return new FunctionCall(getLocation(context), new QualifiedName("normalize"), ImmutableList.of(str, new StringLiteral(getLocation(context), normalForm))); }
@Test public void testPosition() throws Exception { assertExpression("position('a' in 'b')", new FunctionCall(QualifiedName.of("strpos"), ImmutableList.of( new StringLiteral("b"), new StringLiteral("a")))); assertExpression("position('a' in ('b'))", new FunctionCall(QualifiedName.of("strpos"), ImmutableList.of( new StringLiteral("b"), new StringLiteral("a")))); }
@Test public void testArrayConstructor() throws Exception { assertExpression("ARRAY []", new ArrayConstructor(ImmutableList.<Expression>of())); assertExpression("ARRAY [1, 2]", new ArrayConstructor(ImmutableList.<Expression>of(new LongLiteral("1"), new LongLiteral("2")))); assertExpression("ARRAY [1.0, 2.5]", new ArrayConstructor(ImmutableList.<Expression>of(new DoubleLiteral("1.0"), new DoubleLiteral("2.5")))); assertExpression("ARRAY ['hi']", new ArrayConstructor(ImmutableList.<Expression>of(new StringLiteral("hi")))); assertExpression("ARRAY ['hi', 'hello']", new ArrayConstructor(ImmutableList.<Expression>of(new StringLiteral("hi"), new StringLiteral("hello")))); }
@Test public void testValues() { Query valuesQuery = query(values( row(new StringLiteral("a"), new LongLiteral("1"), new DoubleLiteral("2.2")), row(new StringLiteral("b"), new LongLiteral("2"), new DoubleLiteral("3.3")))); assertStatement("VALUES ('a', 1, 2.2), ('b', 2, 3.3)", valuesQuery); assertStatement("SELECT * FROM (VALUES ('a', 1, 2.2), ('b', 2, 3.3))", simpleQuery( selectList(new AllColumns()), subquery(valuesQuery))); }
@Test public void testSetSession() throws Exception { assertStatement("SET SESSION foo = 'bar'", new SetSession(QualifiedName.of("foo"), new StringLiteral("bar"))); assertStatement("SET SESSION foo.bar = 'baz'", new SetSession(QualifiedName.of("foo", "bar"), new StringLiteral("baz"))); assertStatement("SET SESSION foo.bar.boo = 'baz'", new SetSession(QualifiedName.of("foo", "bar", "boo"), new StringLiteral("baz"))); assertStatement("SET SESSION foo.bar = 'ban' || 'ana'", new SetSession( QualifiedName.of("foo", "bar"), new FunctionCall(QualifiedName.of("concat"), ImmutableList.of( new StringLiteral("ban"), new StringLiteral("ana"))))); }
@Test public void testCall() throws Exception { assertStatement("CALL foo()", new Call(QualifiedName.of("foo"), ImmutableList.of())); assertStatement("CALL foo(123, a => 1, b => 'go', 456)", new Call(QualifiedName.of("foo"), ImmutableList.of( new CallArgument(new LongLiteral("123")), new CallArgument("a", new LongLiteral("1")), new CallArgument("b", new StringLiteral("go")), new CallArgument(new LongLiteral("456"))))); }
@Override protected String visitStringLiteral(StringLiteral node, Void context) { return formatStringLiteral(node.getValue()); }
@Override protected String visitStringLiteral(StringLiteral node, StackableAstVisitorContext<Integer> indent) { return formatStringLiteral(node.getValue()); }
@Override protected IComparison visitExpression(Expression node, QueryState state) { if( node instanceof LogicalBinaryExpression){ LogicalBinaryExpression boolExp = (LogicalBinaryExpression)node; IComparison left = boolExp.getLeft().accept(this, state); IComparison right = boolExp.getRight().accept(this, state); return new BooleanComparison(left, right, boolExp.getType() == Type.AND); }else if( node instanceof ComparisonExpression){ ComparisonExpression compareExp = (ComparisonExpression)node; Column column = new SelectParser().visitExpression(compareExp.getLeft(), state); Column leftCol = state.getHeading().getColumnByLabel(column.getLabel()); if(leftCol == null){ state.addException("Having reference "+column+" not found in SELECT clause"); return null; } // right hand side is a concrete literal to compare with if(compareExp.getRight() instanceof Literal){ Object value; if(compareExp.getRight() instanceof LongLiteral) value = ((LongLiteral)compareExp.getRight()).getValue(); else if(compareExp.getRight() instanceof BooleanLiteral) value = ((BooleanLiteral)compareExp.getRight()).getValue(); else if(compareExp.getRight() instanceof DoubleLiteral) value = ((DoubleLiteral)compareExp.getRight()).getValue(); else if(compareExp.getRight() instanceof StringLiteral) value = ((StringLiteral)compareExp.getRight()).getValue(); else { state.addException("Unable to get value from "+compareExp.getRight()); return null; } return new SimpleComparison(leftCol, compareExp.getType(), (Number)value); // right hand side refers to another column } else if(compareExp.getRight() instanceof DereferenceExpression || compareExp.getRight() instanceof QualifiedNameReference){ String col2; if(compareExp.getLeft() instanceof DereferenceExpression){ // parse columns like 'reference.field' col2 = SelectParser.visitDereferenceExpression((DereferenceExpression)compareExp.getRight()); }else{ col2 = ((QualifiedNameReference)compareExp.getRight()).getName().toString(); } col2 = Heading.findOriginal(state.originalSql(), col2, "having.+", "\\W"); Column rightCol = state.getHeading().getColumnByLabel(col2); if(rightCol == null){ state.addException("column "+col2+" not found in SELECT clause"); return null; } return new SimpleComparison(leftCol, compareExp.getType(), rightCol); }else { // unknown right hand side so state.addException("Unable to get value from "+compareExp.getRight()); return null; } }else if( node instanceof NotExpression){ state.addException("NOT is currently not supported, use '<>' instead"); }else{ state.addException("Unable to parse "+node+" ("+node.getClass().getName()+") is not a supported expression"); } return null; }
@Override protected RowExpression visitStringLiteral(StringLiteral node, Void context) { return constant(node.getSlice(), VARCHAR); }
@Override protected Slice visitStringLiteral(StringLiteral node, ConnectorSession session) { return node.getSlice(); }
@Override protected Object visitLikePredicate(LikePredicate node, Object context) { Object value = process(node.getValue(), context); if (value == null) { return null; } if (value instanceof Slice && node.getPattern() instanceof StringLiteral && (node.getEscape() instanceof StringLiteral || node.getEscape() == null)) { // fast path when we know the pattern and escape are constant return LikeFunctions.like((Slice) value, getConstantPattern(node)); } Object pattern = process(node.getPattern(), context); if (pattern == null) { return null; } Object escape = null; if (node.getEscape() != null) { escape = process(node.getEscape(), context); if (escape == null) { return null; } } if (value instanceof Slice && pattern instanceof Slice && (escape == null || escape instanceof Slice)) { Regex regex; if (escape == null) { regex = LikeFunctions.likePattern((Slice) pattern); } else { regex = LikeFunctions.likePattern((Slice) pattern, (Slice) escape); } return LikeFunctions.like((Slice) value, regex); } // if pattern is a constant without % or _ replace with a comparison if (pattern instanceof Slice && escape == null) { String stringPattern = ((Slice) pattern).toStringUtf8(); if (!stringPattern.contains("%") && !stringPattern.contains("_")) { return new ComparisonExpression(ComparisonExpression.Type.EQUAL, toExpression(value, expressionTypes.get(node.getValue())), toExpression(pattern, expressionTypes.get(node.getPattern()))); } } Expression optimizedEscape = null; if (node.getEscape() != null) { optimizedEscape = toExpression(escape, expressionTypes.get(node.getEscape())); } return new LikePredicate( toExpression(value, expressionTypes.get(node.getValue())), toExpression(pattern, expressionTypes.get(node.getPattern())), optimizedEscape); }
@Override protected Type visitStringLiteral(StringLiteral node, StackableAstVisitorContext<AnalysisContext> context) { expressionTypes.put(node, VARCHAR); return VARCHAR; }
@Override protected RelationType visitShowTables(ShowTables showTables, AnalysisContext context) { String catalogName = session.getCatalog().orElse(null); String schemaName = session.getSchema().orElse(null); Optional<QualifiedName> schema = showTables.getSchema(); if (schema.isPresent()) { List<String> parts = schema.get().getParts(); if (parts.size() > 2) { throw new SemanticException(INVALID_SCHEMA_NAME, showTables, "Too many parts in schema name: %s", schema); } if (parts.size() == 2) { catalogName = parts.get(0); } schemaName = schema.get().getSuffix(); } if (catalogName == null) { throw new SemanticException(CATALOG_NOT_SPECIFIED, showTables, "Catalog must be specified when session catalog is not set"); } if (schemaName == null) { throw new SemanticException(SCHEMA_NOT_SPECIFIED, showTables, "Schema must be specified when session schema is not set"); } if (!metadata.listSchemaNames(session, catalogName).contains(schemaName)) { throw new SemanticException(MISSING_SCHEMA, showTables, "Schema '%s' does not exist", schemaName); } Expression predicate = equal(nameReference("table_schema"), new StringLiteral(schemaName)); Optional<String> likePattern = showTables.getLikePattern(); if (likePattern.isPresent()) { Expression likePredicate = new LikePredicate(nameReference("table_name"), new StringLiteral(likePattern.get()), null); predicate = logicalAnd(predicate, likePredicate); } Query query = simpleQuery( selectList(aliasedName("table_name", "Table")), from(catalogName, TABLE_TABLES), predicate, ordering(ascending("table_name"))); return process(query, context); }
private static StringLiteral stringLiteral(String value) { return new StringLiteral(value); }
@Override public Node visitTimeZoneString(SqlBaseParser.TimeZoneStringContext context) { return new StringLiteral(getLocation(context), unquote(context.STRING().getText())); }
@Override public Node visitStringLiteral(SqlBaseParser.StringLiteralContext context) { return new StringLiteral(getLocation(context), unquote(context.STRING().getText())); }
@Override protected String visitStringLiteral(StringLiteral node, Boolean unmangleNames) { return formatStringLiteral(node.getValue()); }
public static SelectItem aliasedNullToEmpty(String column, String alias) { return new SingleColumn(new CoalesceExpression(nameReference(column), new StringLiteral("")), alias); }