/* * Licensed to Crate under one or more contributor license agreements. * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. Crate 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. * * However, if you have executed another commercial license agreement * with Crate these terms will supersede the license and you may use the * software solely pursuant to the terms of the relevant commercial * agreement. */ package io.crate.analyze; import io.crate.data.Row; import io.crate.data.RowN; import io.crate.metadata.Schemas; import io.crate.sql.ExpressionFormatter; import io.crate.sql.parser.SqlParser; import io.crate.sql.tree.*; import org.elasticsearch.common.collect.Tuple; import java.util.*; class ShowStatementAnalyzer { private Analyzer analyzer; private String[] explicitSchemas = new String[]{"information_schema", "sys", "pg_catalog"}; ShowStatementAnalyzer(Analyzer analyzer) { this.analyzer = analyzer; } Query rewriteShowTransaction() { /* * Rewrite * SHOW TRANSACTION ISOLATION LEVEL * To * select 'read uncommitted' from sys.cluster * * In order to return a single row with 'read uncommitted`. * * 'read uncommitted' is used because it matches the behaviour in Crate best. * Although there would be read-isolation on a lucene-reader basis there are no transactions so anything written * by any other client *may* become visible at any time. * * See https://www.postgresql.org/docs/9.5/static/transaction-iso.html */ return (Query) SqlParser.createStatement("select 'read uncommitted' as transaction_isolation from sys.cluster"); } AnalyzedStatement analyzeShowTransaction(Analysis analysis) { Query query = rewriteShowTransaction(); Analysis newAnalysis = analyzer.boundAnalyze(query, analysis.sessionContext(), ParameterContext.EMPTY); analysis.rootRelation(newAnalysis.rootRelation()); return newAnalysis.analyzedStatement(); } Tuple<Query, ParameterContext> rewriteShow(ShowSchemas node) { /* * <code> * SHOW SCHEMAS [ LIKE 'pattern' ]; * </code> * needs to be rewritten to select query: * <code> * SELECT schema_name * FROM information_schema.schemata * [ WHERE schema_name LIKE 'pattern' ] * ORDER BY schema_name; * </code> */ List<String> params = new ArrayList<>(); StringBuilder sb = new StringBuilder("SELECT schema_name "); sb.append("FROM information_schema.schemata "); if (node.likePattern().isPresent()) { params.add(node.likePattern().get()); sb.append("WHERE schema_name LIKE ? "); } else if (node.whereExpression().isPresent()) { sb.append(String.format(Locale.ENGLISH, "WHERE %s ", node.whereExpression().get().toString())); } sb.append("ORDER BY schema_name"); return new Tuple<>( (Query) SqlParser.createStatement(sb.toString()), new ParameterContext(new RowN(params.toArray(new Object[0])), Collections.<Row>emptyList()) ); } public AnalyzedStatement analyze(ShowSchemas node, Analysis analysis) { Tuple<Query, ParameterContext> tuple = rewriteShow(node); Analysis newAnalysis = analyzer.boundAnalyze(tuple.v1(), analysis.sessionContext(), tuple.v2()); analysis.rootRelation(newAnalysis.rootRelation()); return newAnalysis.analyzedStatement(); } Tuple<Query, ParameterContext> rewriteShow(ShowColumns node) { /* * <code> * SHOW COLUMNS {FROM | IN} table [{FROM | IN} schema] [LIKE 'pattern' | WHERE expr]; * </code> * needs to be rewritten to select query: * <code> * SELECT column_name, data_type * FROM information_schema.columns * WHERE table_schema = {'doc' | ['%s'] } * [ AND WHERE table_name LIKE '%s' | column_name LIKE '%s'] * ORDER BY column_name; * </code> */ List<String> params = new ArrayList<>(); StringBuilder sb = new StringBuilder( "SELECT column_name, data_type " + "FROM information_schema.columns "); params.add(node.table().toString()); sb.append("WHERE table_name = ? "); sb.append("AND table_schema = ? "); if (node.schema().isPresent()) { params.add(node.schema().get().toString()); } else { params.add(Schemas.DEFAULT_SCHEMA_NAME); } if (node.likePattern().isPresent()) { params.add(node.likePattern().get()); sb.append("AND column_name LIKE ? "); } else if (node.where().isPresent()) { sb.append(String.format(Locale.ENGLISH, "AND %s", ExpressionFormatter.formatExpression(node.where().get()))); } sb.append("ORDER BY column_name"); return new Tuple<>( (Query) SqlParser.createStatement(sb.toString()), new ParameterContext(new RowN(params.toArray(new Object[0])), Collections.<Row>emptyList()) ); } public AnalyzedStatement analyze(ShowColumns node, Analysis analysis) { Tuple<Query, ParameterContext> tuple = rewriteShow(node); Analysis newAnalysis = analyzer.boundAnalyze(tuple.v1(), analysis.sessionContext(), tuple.v2()); analysis.rootRelation(newAnalysis.rootRelation()); return newAnalysis.analyzedStatement(); } public AnalyzedStatement analyze(ShowTables node, Analysis analysis) { Tuple<Query, ParameterContext> tuple = rewriteShow(node); Analysis newAnalysis = analyzer.boundAnalyze(tuple.v1(), analysis.sessionContext(), tuple.v2()); analysis.rootRelation(newAnalysis.rootRelation()); return newAnalysis.analyzedStatement(); } public Tuple<Query, ParameterContext> rewriteShow(ShowTables node) { /* * <code> * SHOW TABLES [{ FROM | IN } table_schema ] [ { LIKE 'pattern' | WHERE expr } ]; * </code> * needs to be rewritten to select query: * <code> * SELECT distinct(table_name) as table_name * FROM information_schema.tables * [ WHERE ( table_schema = 'table_schema' | table_schema NOT IN ( 'sys', 'information_schema' ) ) * [ AND ( table_name LIKE 'pattern' | <where-clause > ) ] * ] * ORDER BY 1; * </code> */ List<String> params = new ArrayList<>(); StringBuilder sb = new StringBuilder("SELECT distinct(table_name) as table_name FROM information_schema.tables "); Optional<QualifiedName> schema = node.schema(); if (schema.isPresent()) { params.add(schema.get().toString()); sb.append("WHERE table_schema = ?"); } else { sb.append("WHERE table_schema NOT IN ("); for (int i = 0; i < explicitSchemas.length; i++) { params.add(explicitSchemas[i]); sb.append("?"); if (i < explicitSchemas.length - 1) { sb.append(", "); } } sb.append(")"); } Optional<Expression> whereExpression = node.whereExpression(); if (whereExpression.isPresent()) { sb.append(" AND ("); sb.append(whereExpression.get().toString()); sb.append(")"); } else { Optional<String> likePattern = node.likePattern(); if (likePattern.isPresent()) { params.add(likePattern.get()); sb.append(" AND table_name like ?"); } } sb.append(" ORDER BY 1"); return new Tuple<>( (Query) SqlParser.createStatement(sb.toString()), new ParameterContext(new RowN(params.toArray(new Object[0])), Collections.<Row>emptyList()) ); } }