diff --git a/be/src/exec/ddl-executor.cc b/be/src/exec/ddl-executor.cc index 5019b4ed9..5aeab7390 100644 --- a/be/src/exec/ddl-executor.cc +++ b/be/src/exec/ddl-executor.cc @@ -17,7 +17,7 @@ DdlExecutor::DdlExecutor(ImpalaServer* impala_server, const string& delimiter) } Status DdlExecutor::Exec(TDdlExecRequest* exec_request) { - if (exec_request->ddl_type == TDdlType::SHOW) { + if (exec_request->ddl_type == TDdlType::SHOW_TABLES) { // A NULL pattern means match all tables. However, Thrift string types can't // be NULL in C++, so we have to test if it's set rather than just blindly // using the value. @@ -30,6 +30,13 @@ Status DdlExecutor::Exec(TDdlExecRequest* exec_request) { return Status::OK; } + if (exec_request->ddl_type == TDdlType::SHOW_DBS) { + string* db_pattern = + exec_request->__isset.show_pattern ? (&exec_request->show_pattern) : NULL; + RETURN_IF_ERROR(impala_server_->GetDbNames(db_pattern, &ascii_rows_)); + return Status::OK; + } + if (exec_request->ddl_type == TDdlType::DESCRIBE) { vector columns; RETURN_IF_ERROR(impala_server_->DescribeTable(exec_request->database, diff --git a/be/src/service/impala-server.cc b/be/src/service/impala-server.cc index 316ed0a34..e54240c1e 100644 --- a/be/src/service/impala-server.cc +++ b/be/src/service/impala-server.cc @@ -638,6 +638,8 @@ ImpalaServer::ImpalaServer(ExecEnv* exec_env) EXIT_IF_EXC(jni_env); describe_table_id_ = jni_env->GetMethodID(fe_class, "describeTable", "([B)[B"); EXIT_IF_EXC(jni_env); + get_db_names_id_ = jni_env->GetMethodID(fe_class, "getDbNames", "([B)[B"); + EXIT_IF_EXC(jni_env); jboolean lazy = (FLAGS_load_catalog_at_startup ? false : true); jobject fe = jni_env->NewObject(fe_class, fe_ctor, lazy); @@ -984,6 +986,31 @@ Status ImpalaServer::GetTableNames(string* db, string* pattern, } } +Status ImpalaServer::GetDbNames(string* pattern, vector* db_names) { + if (!FLAGS_use_planservice) { + JNIEnv* jni_env = getJNIEnv(); + jbyteArray request_bytes; + TGetDbsParams params; + if (pattern != NULL) { + params.__set_pattern(*pattern); + } + + RETURN_IF_ERROR(SerializeThriftMsg(jni_env, ¶ms, &request_bytes)); + jbyteArray result_bytes = static_cast( + jni_env->CallObjectMethod(fe_, get_db_names_id_, request_bytes)); + + RETURN_ERROR_IF_EXC(jni_env, JniUtil::throwable_to_string_id()); + + TGetDbsResult result; + RETURN_IF_ERROR(DeserializeThriftMsg(jni_env, result_bytes, &result)); + + db_names->insert(db_names->begin(), result.dbs.begin(), result.dbs.end()); + return Status::OK; + } else { + return Status("GetDbNames not supported with external planservice"); + } +} + Status ImpalaServer::GetExecRequest( const TClientRequest& request, TExecRequest* result) { if (!FLAGS_use_planservice) { diff --git a/be/src/service/impala-server.h b/be/src/service/impala-server.h index a159d69ad..0f698254a 100644 --- a/be/src/service/impala-server.h +++ b/be/src/service/impala-server.h @@ -226,7 +226,7 @@ class ImpalaServer : public ImpalaServiceIf, public ImpalaInternalServiceIf, void InitializeConfigVariables(); // Returns all matching table names, per Hive's "SHOW TABLES ". Each - // table name is fully-qualified, i.e. of the form db.table_name + // table name returned is unqualified. // If db is NULL, match table names from all databases, otherwise restrict the // search to the named database. // If pattern is NULL, match all tables otherwise match only those tables that @@ -235,6 +235,12 @@ class ImpalaServer : public ImpalaServiceIf, public ImpalaInternalServiceIf, Status GetTableNames(std::string* db, std::string* pattern, std::vector* table_names); + // Return all databases matching the optional argument 'pattern'. + // If pattern is NULL, match all databases otherwise match only those databases that + // match the pattern string. Patterns are "p1|p2|p3" where | denotes choice, + // and each pN may contain wildcards denoted by '*' which match all strings. + Status GetDbNames(std::string* pattern, std::vector* table_names); + // Returns (in the output parameter) a list of columns for the specified table // in the specified database. Status DescribeTable(const std::string& db, const std::string& table, @@ -252,6 +258,7 @@ class ImpalaServer : public ImpalaServiceIf, public ImpalaInternalServiceIf, jmethodID update_metastore_id_; // JniFrontend.updateMetastore() jmethodID get_table_names_id_; // JniFrontend.getTableNames jmethodID describe_table_id_,; // JniFrontend.describeTable + jmethodID get_db_names_id_; // JniFrontend.getDbNames ExecEnv* exec_env_; // not owned // plan service-related - impalad optionally uses a standalone diff --git a/common/thrift/Frontend.thrift b/common/thrift/Frontend.thrift index f2b900d99..ff1313f7d 100644 --- a/common/thrift/Frontend.thrift +++ b/common/thrift/Frontend.thrift @@ -22,11 +22,23 @@ struct TGetTablesParams { 2: optional string pattern } -// getTableNames returns a list of fully-qualified table names +// getTableNames returns a list of unqualified table names struct TGetTablesResult { 1: list tables } +// Arguments to getDbNames, which returns a list of dbs that match an optional +// pattern +struct TGetDbsParams { + // If not set, match every database + 1: optional string pattern +} + +// getDbNames returns a list of database names +struct TGetDbsResult { + 1: list dbs +} + struct TColumnDesc { 1: required string columnName 2: required Types.TPrimitiveType columnType @@ -128,7 +140,8 @@ struct TQueryExecRequest2 { } enum TDdlType { - SHOW, + SHOW_TABLES, + SHOW_DBS, USE, DESCRIBE } diff --git a/fe/src/main/cup/sql-parser.y b/fe/src/main/cup/sql-parser.y index 5d393d623..ee2d3fd79 100644 --- a/fe/src/main/cup/sql-parser.y +++ b/fe/src/main/cup/sql-parser.y @@ -133,14 +133,15 @@ parser code {: :}; terminal KW_AND, KW_ALL, KW_AS, KW_ASC, KW_AVG, KW_BETWEEN, KW_BIGINT, KW_BOOLEAN, KW_BY, - KW_CASE, KW_CAST, KW_COUNT, KW_DATE, KW_DATETIME, KW_DESC, KW_DESCRIBE, KW_DISTINCT, - KW_DISTINCTPC, KW_DISTINCTPCSA, + KW_CASE, KW_CAST, KW_COUNT, KW_DATABASES, KW_DATE, KW_DATETIME, KW_DESC, KW_DESCRIBE, + KW_DISTINCT, KW_DISTINCTPC, KW_DISTINCTPCSA, KW_DIV, KW_DOUBLE, KW_ELSE, KW_END, KW_FALSE, KW_FLOAT, KW_FROM, KW_FULL, KW_GROUP, KW_HAVING, KW_IS, KW_IN, KW_INNER, KW_JOIN, KW_INT, KW_LEFT, KW_LIKE, KW_LIMIT, KW_MIN, KW_MAX, KW_NOT, KW_NULL, KW_ON, KW_OR, KW_ORDER, KW_OUTER, KW_REGEXP, - KW_RLIKE, KW_RIGHT, KW_SELECT, KW_SHOW, KW_SEMI, KW_SMALLINT, KW_STRING, KW_SUM, - KW_TABLES, KW_TINYINT, KW_TRUE, KW_UNION, KW_USE, KW_USING, KW_WHEN, KW_WHERE, KW_THEN, - KW_TIMESTAMP, KW_INSERT, KW_INTO, KW_OVERWRITE, KW_TABLE, KW_PARTITION, KW_INTERVAL; + KW_RLIKE, KW_RIGHT, KW_SCHEMAS, KW_SELECT, KW_SHOW, KW_SEMI, KW_SMALLINT, KW_STRING, + KW_SUM, KW_TABLES, KW_TINYINT, KW_TRUE, KW_UNION, KW_USE, KW_USING, KW_WHEN, KW_WHERE, + KW_THEN, KW_TIMESTAMP, KW_INSERT, KW_INTO, KW_OVERWRITE, KW_TABLE, KW_PARTITION, + KW_INTERVAL; terminal COMMA, DOT, STAR, LPAREN, RPAREN, DIVIDE, MOD, ADD, SUBTRACT; terminal BITAND, BITOR, BITXOR, BITNOT; terminal EQUAL, NOT, LESSTHAN, GREATERTHAN; @@ -161,7 +162,8 @@ nonterminal QueryStmt query_stmt; nonterminal List union_operands; // USE stmt nonterminal UseStmt use_stmt; -nonterminal ShowStmt show_stmt; +nonterminal ShowTablesStmt show_tables_stmt; +nonterminal ShowDbsStmt show_dbs_stmt; nonterminal String show_pattern; nonterminal DescribeStmt describe_stmt; // List of select blocks connected by UNION operators, with order by or limit. @@ -230,8 +232,10 @@ stmt ::= {: RESULT = insert; :} | use_stmt:use {: RESULT = use; :} - | show_stmt:show - {: RESULT = show; :} + | show_tables_stmt:show_tables + {: RESULT = show_tables; :} + | show_dbs_stmt:show_dbs + {: RESULT = show_dbs; :} | describe_stmt:describe {: RESULT = describe; :} ; @@ -380,16 +384,35 @@ use_stmt ::= {: RESULT = new UseStmt(db); :} ; -show_stmt ::= +show_tables_stmt ::= KW_SHOW KW_TABLES - {: RESULT = new ShowStmt(); :} + {: RESULT = new ShowTablesStmt(); :} | KW_SHOW KW_TABLES show_pattern:showPattern - {: RESULT = new ShowStmt(showPattern); :} + {: RESULT = new ShowTablesStmt(showPattern); :} + | KW_SHOW KW_TABLES KW_IN IDENT:db + {: RESULT = new ShowTablesStmt(db, null); :} + | KW_SHOW KW_TABLES KW_IN IDENT:db show_pattern:showPattern + {: RESULT = new ShowTablesStmt(db, showPattern); :} + ; + +// TODO: No obvious way in CUP to create a rule that returns nothing (needed to +// make choice between DATABASES and SCHEMAS less ugly below). +show_dbs_stmt ::= + KW_SHOW KW_DATABASES + {: RESULT = new ShowDbsStmt(); :} + | KW_SHOW KW_DATABASES show_pattern:showPattern + {: RESULT = new ShowDbsStmt(showPattern); :} + | KW_SHOW KW_SCHEMAS + {: RESULT = new ShowDbsStmt(); :} + | KW_SHOW KW_SCHEMAS show_pattern:showPattern + {: RESULT = new ShowDbsStmt(showPattern); :} ; show_pattern ::= STRING_LITERAL:showPattern {: RESULT = showPattern; :} + | KW_LIKE STRING_LITERAL:showPattern + {: RESULT = showPattern; :} ; describe_stmt ::= diff --git a/fe/src/main/java/com/cloudera/impala/analysis/AnalysisContext.java b/fe/src/main/java/com/cloudera/impala/analysis/AnalysisContext.java index 6bfa3e7b8..d264ebe1c 100644 --- a/fe/src/main/java/com/cloudera/impala/analysis/AnalysisContext.java +++ b/fe/src/main/java/com/cloudera/impala/analysis/AnalysisContext.java @@ -43,8 +43,12 @@ public class AnalysisContext { return stmt instanceof UseStmt; } - public boolean isShowStmt() { - return stmt instanceof ShowStmt; + public boolean isShowTablesStmt() { + return stmt instanceof ShowTablesStmt; + } + + public boolean isShowDbsStmt() { + return stmt instanceof ShowDbsStmt; } public boolean isDescribeStmt() { @@ -52,7 +56,7 @@ public class AnalysisContext { } public boolean isDdlStmt() { - return isUseStmt() || isShowStmt() || isDescribeStmt(); + return isUseStmt() || isShowTablesStmt() || isShowDbsStmt() || isDescribeStmt(); } public boolean isDmlStmt() { @@ -71,8 +75,12 @@ public class AnalysisContext { return (UseStmt) stmt; } - public ShowStmt getShowStmt() { - return (ShowStmt) stmt; + public ShowTablesStmt getShowTablesStmt() { + return (ShowTablesStmt) stmt; + } + + public ShowDbsStmt getShowDbsStmt() { + return (ShowDbsStmt) stmt; } public DescribeStmt getDescribeStmt() { diff --git a/fe/src/main/java/com/cloudera/impala/analysis/ShowStmt.java b/fe/src/main/java/com/cloudera/impala/analysis/ShowDbsStmt.java similarity index 50% rename from fe/src/main/java/com/cloudera/impala/analysis/ShowStmt.java rename to fe/src/main/java/com/cloudera/impala/analysis/ShowDbsStmt.java index 311f7d667..25d417764 100644 --- a/fe/src/main/java/com/cloudera/impala/analysis/ShowStmt.java +++ b/fe/src/main/java/com/cloudera/impala/analysis/ShowDbsStmt.java @@ -6,20 +6,32 @@ import com.cloudera.impala.common.AnalysisException; import com.cloudera.impala.common.InternalException; /** - * Representation of a SHOW TABLES [pattern] statement. + * Representation of a SHOW DATABASES [pattern] statement. + * Acceptable syntax: + * + * SHOW DATABASES + * SHOW SCHEMAS + * SHOW DATABASES LIKE 'pattern' + * SHOW SCHEMAS LIKE 'pattern' + * */ -public class ShowStmt extends ParseNodeBase { +public class ShowDbsStmt extends ParseNodeBase { // Pattern to match tables against. | denotes choice, * matches all strings private final String pattern; - // Set during analysis - private String db; - - public ShowStmt() { - pattern = null; + /** + * Default constructor, which creates a show statement which returns all + * databases. + */ + public ShowDbsStmt() { + this(null); } - public ShowStmt(String pattern) { + /** + * Constructs a show statement which matches all databases against the + * supplied pattern. + */ + public ShowDbsStmt(String pattern) { this.pattern = pattern; } @@ -27,15 +39,11 @@ public class ShowStmt extends ParseNodeBase { return pattern; } - public String getDb() { - return db; - } - public String toSql() { if (pattern == null) { - return "SHOW TABLES"; + return "SHOW DATABASES"; } else { - return "SHOW TABLES \"" + pattern + "\""; + return "SHOW DATABASES LIKE '" + pattern + "'"; } } @@ -44,6 +52,6 @@ public class ShowStmt extends ParseNodeBase { } public void analyze(Analyzer analyzer) throws AnalysisException, InternalException { - db = analyzer.getDefaultDb(); + // Nothing to do here } } diff --git a/fe/src/main/java/com/cloudera/impala/analysis/ShowTablesStmt.java b/fe/src/main/java/com/cloudera/impala/analysis/ShowTablesStmt.java new file mode 100644 index 000000000..f8664d375 --- /dev/null +++ b/fe/src/main/java/com/cloudera/impala/analysis/ShowTablesStmt.java @@ -0,0 +1,99 @@ +// Copyright (c) 2012 Cloudera, Inc. All rights reserved. + +package com.cloudera.impala.analysis; + +import com.cloudera.impala.common.AnalysisException; +import com.cloudera.impala.common.InternalException; + +import com.google.common.base.Preconditions; + +/** + * Representation of a SHOW TABLES [pattern] statement. + * Acceptable syntax: + * + * SHOW TABLES + * SHOW TABLES "pattern" + * SHOW TABLES LIKE "pattern" + * SHOW TABLES IN database + * SHOW TABLES IN database "pattern" + * SHOW TABLES IN database LIKE "pattern" + * + * In Hive, the 'LIKE' is optional. Also SHOW TABLES unquotedpattern is accepted + * by the parser but returns no results. We don't support that syntax. + */ +public class ShowTablesStmt extends ParseNodeBase { + // Pattern to match tables against. | denotes choice, * matches all strings + private final String pattern; + + // DB (if any) as seen by the parser + private final String parsedDb; + + // Set during analysis + private String postAnalysisDb; + + /** + * Default constructor, which creates a show statement with the default + * database and no pattern (which returns all tables in the default database). + */ + public ShowTablesStmt() { + this(null, null); + } + + /** + * Constructs a show statement against the default database using the supplied + * pattern. + */ + public ShowTablesStmt(String pattern) { + this(null, pattern); + } + + /** + * General purpose constructor which builds a show statement that matches + * table names against a given pattern in the supplied database. + * + * If pattern is null, all tables in the supplied database match. + * If database is null, the default database is searched. + */ + public ShowTablesStmt(String database, String pattern) { + this.parsedDb = database; + this.pattern = pattern; + this.postAnalysisDb = null; + } + + public String getPattern() { + return pattern; + } + + /** + * Can only be called after analysis, returns the name of the database that + * this show will search against. + */ + public String getDb() { + Preconditions.checkNotNull(postAnalysisDb); + return postAnalysisDb; + } + + public String toSql() { + if (pattern == null) { + if (parsedDb == null) { + return "SHOW TABLES"; + } else { + return "SHOW TABLES IN " + parsedDb; + } + } else { + if (parsedDb == null) { + return "SHOW TABLES LIKE '" + pattern + "'"; + } else { + return "SHOW TABLES IN " + parsedDb + " LIKE '" + pattern + "'"; + } + } + } + + public String debugString() { + return toSql(); + } + + public void analyze(Analyzer analyzer) throws AnalysisException, InternalException { + postAnalysisDb = (parsedDb == null ? analyzer.getDefaultDb() : parsedDb); + } +} diff --git a/fe/src/main/java/com/cloudera/impala/catalog/Catalog.java b/fe/src/main/java/com/cloudera/impala/catalog/Catalog.java index b3536edf3..1521d80d6 100644 --- a/fe/src/main/java/com/cloudera/impala/catalog/Catalog.java +++ b/fe/src/main/java/com/cloudera/impala/catalog/Catalog.java @@ -19,6 +19,8 @@ import com.google.common.collect.Maps; import com.google.common.collect.Lists; import com.google.common.base.Preconditions; +import com.cloudera.impala.common.ImpalaException; + /** * Interface to metadata stored in MetaStore instance. * Caches all db-, table- and column-related md during construction. @@ -93,53 +95,85 @@ public class Catalog { return msClient; } + public static class DatabaseNotFoundException extends ImpalaException { + // Dummy serial ID to satisfy Eclipse + private static final long serialVersionUID = -2203080667446640542L; + + public DatabaseNotFoundException(String s) { super(s); } + } + /** - * Implement Hive's pattern-matching getTables call. The only metacharacters - * are '*' which matches any string of characters, and '|' which denotes - * choice. Doing the work here saves loading the tables from the metastore - * (which Hive would do if we passed the call through to the metastore client). + * Returns a list of tables in the supplied database that match + * tablePattern. See filterStringsByPattern for details of the pattern match + * semantics. * - * If tablePattern is null, all tables are considered to match. If it is the - * empty string, no tables match. + * dbName must not be null. tablePattern may be null (and thus matches + * everything). * * Table names are returned unqualified. */ - public List getTableNames(String dbName, String tablePattern) { + public List getTableNames(String dbName, String tablePattern) + throws DatabaseNotFoundException { Preconditions.checkNotNull(dbName); List matchingTables = Lists.newArrayList(); Db db = getDb(dbName); if (db == null) { - return matchingTables; + throw new DatabaseNotFoundException("Database '" + dbName + "' not found"); } - List patterns = Lists.newArrayList(); - // Hive ignores pretty much all metacharacters, so we have to escape them. - final String metaCharacters = "+?.^()]\\/{}"; - final Pattern regex = Pattern.compile("([" + Pattern.quote(metaCharacters) + "])"); - if (tablePattern != null) { - for (String pattern: Arrays.asList(tablePattern.split("\\|"))) { + return filterStringsByPattern(db.getAllTableNames(), tablePattern); + } + + /** + * Returns a list of databases that match dbPattern. See + * filterStringsByPattern for details of the pattern match semantics. + * + * dbPattern may be null (and thus matches + * everything). + */ + public List getDbNames(String dbPattern) { + return filterStringsByPattern(dbs.keySet(), dbPattern); + } + + /** + * Implement Hive's pattern-matching semantics for SHOW statements. The only + * metacharacters are '*' which matches any string of characters, and '|' + * which denotes choice. Doing the work here saves loading tables or + * databases from the metastore (which Hive would do if we passed the call + * through to the metastore client). + * + * If matchPattern is null, all strings are considered to match. If it is the + * empty string, no strings match. + */ + private List filterStringsByPattern(Iterable candidates, + String matchPattern) { + List filtered = Lists.newArrayList(); + if (matchPattern == null) { + filtered = Lists.newArrayList(candidates); + } else { + List patterns = Lists.newArrayList(); + // Hive ignores pretty much all metacharacters, so we have to escape them. + final String metaCharacters = "+?.^()]\\/{}"; + final Pattern regex = Pattern.compile("([" + Pattern.quote(metaCharacters) + "])"); + + for (String pattern: Arrays.asList(matchPattern.split("\\|"))) { Matcher matcher = regex.matcher(pattern); pattern = matcher.replaceAll("\\\\$1").replace("*", ".*"); patterns.add(pattern); } - } - - for (String table: db.getAllTableNames()) { - if (tablePattern == null) { - matchingTables.add(table); - } else { + + for (String candidate: candidates) { for (String pattern: patterns) { // Empty string matches nothing in Hive's implementation - if (!pattern.isEmpty() && table.matches(pattern)) { - matchingTables.add(table); + if (!pattern.isEmpty() && candidate.matches(pattern)) { + filtered.add(candidate); } } } } - - Collections.sort(matchingTables); - return matchingTables; + Collections.sort(filtered, String.CASE_INSENSITIVE_ORDER); + return filtered; } /** @@ -150,7 +184,7 @@ public class Catalog { invalidateTable(DEFAULT_DB, tableName); } - public static class TableNotFoundException extends Exception { + public static class TableNotFoundException extends ImpalaException { private static final long serialVersionUID = -2203080667446640542L; public TableNotFoundException(String s) { super(s); } diff --git a/fe/src/main/java/com/cloudera/impala/service/Frontend.java b/fe/src/main/java/com/cloudera/impala/service/Frontend.java index 3adc64515..1bf18c076 100644 --- a/fe/src/main/java/com/cloudera/impala/service/Frontend.java +++ b/fe/src/main/java/com/cloudera/impala/service/Frontend.java @@ -20,6 +20,7 @@ import com.cloudera.impala.analysis.AnalysisContext; import com.cloudera.impala.analysis.InsertStmt; import com.cloudera.impala.analysis.QueryStmt; import com.cloudera.impala.catalog.Catalog; +import com.cloudera.impala.catalog.Catalog.DatabaseNotFoundException; import com.cloudera.impala.catalog.Column; import com.cloudera.impala.catalog.Db; import com.cloudera.impala.catalog.HdfsTable; @@ -203,10 +204,15 @@ public class Frontend { ddl.ddl_type = TDdlType.USE; ddl.setDatabase(analysis.getUseStmt().getDatabase()); metadata.setColumnDescs(Collections.emptyList()); - } else if (analysis.isShowStmt()) { - ddl.ddl_type = TDdlType.SHOW; - ddl.setShow_pattern(analysis.getShowStmt().getPattern()); - ddl.setDatabase(analysis.getShowStmt().getDb()); + } else if (analysis.isShowTablesStmt()) { + ddl.ddl_type = TDdlType.SHOW_TABLES; + ddl.setShow_pattern(analysis.getShowTablesStmt().getPattern()); + ddl.setDatabase(analysis.getShowTablesStmt().getDb()); + metadata.setColumnDescs(Arrays.asList( + new TColumnDesc("name", TPrimitiveType.STRING))); + } else if (analysis.isShowDbsStmt()) { + ddl.ddl_type = TDdlType.SHOW_DBS; + ddl.setShow_pattern(analysis.getShowDbsStmt().getPattern()); metadata.setColumnDescs(Arrays.asList( new TColumnDesc("name", TPrimitiveType.STRING))); } else if (analysis.isDescribeStmt()) { @@ -242,6 +248,16 @@ public class Frontend { return catalog.getTableNames(dbName, tablePattern); } + /** + * Returns all tables that match the specified database and pattern. If + * pattern is null, matches all tables. If db is null, all databases are + * searched for matches. + */ + public List getDbNames(String dbPattern) + throws ImpalaException { + return catalog.getDbNames(dbPattern); + } + /** * Returns a list of column descriptors describing a the columns in the * specified table. Throws an AnalysisException if the table or db is not diff --git a/fe/src/main/java/com/cloudera/impala/service/JniFrontend.java b/fe/src/main/java/com/cloudera/impala/service/JniFrontend.java index c68115b57..6de454873 100644 --- a/fe/src/main/java/com/cloudera/impala/service/JniFrontend.java +++ b/fe/src/main/java/com/cloudera/impala/service/JniFrontend.java @@ -23,6 +23,9 @@ import com.cloudera.impala.thrift.TDescribeTableResult; import com.cloudera.impala.thrift.TExecRequest; import com.cloudera.impala.thrift.TGetTablesParams; import com.cloudera.impala.thrift.TGetTablesResult; +import com.cloudera.impala.thrift.TGetDbsParams; +import com.cloudera.impala.thrift.TGetDbsResult; +import com.cloudera.impala.thrift.TQueryExecRequest2; /** * JNI-callable interface onto a wrapped Frontend instance. The main point is to serialise @@ -154,6 +157,28 @@ public class JniFrontend { } } + /** + * Returns a list of table names matching an optional pattern. + * The argument is a serialized TGetTablesParams object. + * The return type is a serialised TGetTablesResult object. + * @see Frontend#getTableNames + */ + public byte[] getDbNames(byte[] thriftGetTablesParams) throws ImpalaException { + TGetDbsParams params = new TGetDbsParams(); + deserializeThrift(params, thriftGetTablesParams); + List dbs = frontend.getDbNames(params.pattern); + + TGetDbsResult result = new TGetDbsResult(); + result.setDbs(dbs); + + TSerializer serializer = new TSerializer(protocolFactory); + try { + return serializer.serialize(result); + } catch (TException e) { + throw new InternalException(e.getMessage()); + } + } + /** * Returns a list of the columns making up a table. * The argument is a serialized TDescribeTableParams object. diff --git a/fe/src/main/jflex/sql-scanner.flex b/fe/src/main/jflex/sql-scanner.flex index 06d7cbab4..50bb1cddd 100644 --- a/fe/src/main/jflex/sql-scanner.flex +++ b/fe/src/main/jflex/sql-scanner.flex @@ -50,6 +50,7 @@ import com.cloudera.impala.analysis.SqlParserSymbols; keywordMap.put("case", new Integer(SqlParserSymbols.KW_CASE)); keywordMap.put("cast", new Integer(SqlParserSymbols.KW_CAST)); keywordMap.put("count", new Integer(SqlParserSymbols.KW_COUNT)); + keywordMap.put("databases", new Integer(SqlParserSymbols.KW_DATABASES)); keywordMap.put("date", new Integer(SqlParserSymbols.KW_DATE)); keywordMap.put("datetime", new Integer(SqlParserSymbols.KW_DATETIME)); keywordMap.put("desc", new Integer(SqlParserSymbols.KW_DESC)); @@ -89,6 +90,7 @@ import com.cloudera.impala.analysis.SqlParserSymbols; keywordMap.put("regexp", new Integer(SqlParserSymbols.KW_REGEXP)); keywordMap.put("rlike", new Integer(SqlParserSymbols.KW_RLIKE)); keywordMap.put("right", new Integer(SqlParserSymbols.KW_RIGHT)); + keywordMap.put("schemas", new Integer(SqlParserSymbols.KW_SCHEMAS)); keywordMap.put("select", new Integer(SqlParserSymbols.KW_SELECT)); keywordMap.put("semi", new Integer(SqlParserSymbols.KW_SEMI)); keywordMap.put("show", new Integer(SqlParserSymbols.KW_SHOW)); @@ -166,7 +168,7 @@ Whitespace = {LineTerminator} | [ \t\f] IdentifierOrKw = [:jletter:][:jletterdigit:]* | "&&" | "||" IntegerLiteral = [:digit:][:digit:]* -SingleQuoteStringLiteral = \'([^\n\r\']|\\\\|\\\')*\' +SingleQuoteStringLiteral = (\'([^\n\r\']|\\\\|\\\')*\')|(\`([^\n\r\`]|\\\\|\\\`)*\`) DoubleQuoteStringLiteral = \"([^\n\r\"]|\\\\|\\\")*\" FLit1 = [0-9]+ \. [0-9]* @@ -201,6 +203,7 @@ EndOfLineComment = "--" {NonTerminator}* {LineTerminator}? ">" { return newToken(SqlParserSymbols.GREATERTHAN, null); } "\"" { return newToken(SqlParserSymbols.UNMATCHED_STRING_LITERAL, null); } "'" { return newToken(SqlParserSymbols.UNMATCHED_STRING_LITERAL, null); } +"`" { return newToken(SqlParserSymbols.UNMATCHED_STRING_LITERAL, null); } {IdentifierOrKw} { Integer kw_id = keywordMap.get(yytext().toLowerCase()); diff --git a/fe/src/test/java/com/cloudera/impala/analysis/ParserTest.java b/fe/src/test/java/com/cloudera/impala/analysis/ParserTest.java index 38060b75d..be45d1a07 100644 --- a/fe/src/test/java/com/cloudera/impala/analysis/ParserTest.java +++ b/fe/src/test/java/com/cloudera/impala/analysis/ParserTest.java @@ -318,10 +318,12 @@ public class ParserTest { ParsesOk("select \"\\\"five\\\"\" from t\n"); ParsesOk("select \"\'five\'\" from t\n"); ParsesOk("select \"\'five\" from t\n"); + // missing quotes ParserError("select \'5 from t"); ParserError("select \"5 from t"); ParserError("select '5 from t"); + ParserError("select `5 from t"); ParserError("select \"\"five\"\" from t\n"); ParserError("select 5.0.5 from t"); // NULL literal in arithmetic expr @@ -626,6 +628,11 @@ public class ParserTest { ParsesOk("SHOW TABLES 'tablename|othername'"); // Empty pattern ok ParsesOk("SHOW TABLES ''"); + // Databases + ParsesOk("SHOW DATABASES"); + ParsesOk("SHOW SCHEMAS"); + ParsesOk("SHOW DATABASES LIKE 'pattern'"); + ParsesOk("SHOW SCHEMAS LIKE 'p*ttern'"); } @Test diff --git a/testdata/workloads/functional-query/queries/QueryTest/describe.test b/testdata/workloads/functional-query/queries/QueryTest/describe.test index 5a46f7965..c84af529a 100644 --- a/testdata/workloads/functional-query/queries/QueryTest/describe.test +++ b/testdata/workloads/functional-query/queries/QueryTest/describe.test @@ -17,6 +17,8 @@ string, string 'year','int' 'month','int' ==== +USE default +==== # Default database describe alltypes ---- TYPES diff --git a/testdata/workloads/functional-query/queries/QueryTest/show.test b/testdata/workloads/functional-query/queries/QueryTest/show.test index 19f2b4704..2a5f648c6 100644 --- a/testdata/workloads/functional-query/queries/QueryTest/show.test +++ b/testdata/workloads/functional-query/queries/QueryTest/show.test @@ -87,4 +87,112 @@ string 'alltypes' 'stringpartitionkey' 'testtbl' +==== +# Show tables in +show tables in testdb1 +---- TYPES +string +---- RESULTS +'alltypes' +'alltypes_rc' +'alltypes_rc_bzip' +'alltypes_rc_def' +'alltypes_rc_gzip' +'alltypes_rc_snap' +'alltypes_seq' +'alltypes_seq_bzip' +'alltypes_seq_def' +'alltypes_seq_gzip' +'alltypes_seq_record_bzip' +'alltypes_seq_record_def' +'alltypes_seq_record_gzip' +'alltypes_seq_record_snap' +'alltypes_seq_snap' +'alltypes_trevni' +'alltypes_trevni_def' +'alltypes_trevni_snap' +'testtbl' +'testtbl_rc' +'testtbl_rc_bzip' +'testtbl_rc_def' +'testtbl_rc_gzip' +'testtbl_rc_snap' +'testtbl_seq' +'testtbl_seq_bzip' +'testtbl_seq_def' +'testtbl_seq_gzip' +'testtbl_seq_record_bzip' +'testtbl_seq_record_def' +'testtbl_seq_record_gzip' +'testtbl_seq_record_snap' +'testtbl_seq_snap' +'testtbl_trevni' +'testtbl_trevni_def' +'testtbl_trevni_snap' +==== +# Show tables in with a pattern +show tables in testdb1 like 'alltype*' +---- TYPES +string +---- RESULTS +'alltypes' +'alltypes_rc' +'alltypes_rc_bzip' +'alltypes_rc_def' +'alltypes_rc_gzip' +'alltypes_rc_snap' +'alltypes_seq' +'alltypes_seq_bzip' +'alltypes_seq_def' +'alltypes_seq_gzip' +'alltypes_seq_record_bzip' +'alltypes_seq_record_def' +'alltypes_seq_record_gzip' +'alltypes_seq_record_snap' +'alltypes_seq_snap' +'alltypes_trevni' +'alltypes_trevni_def' +'alltypes_trevni_snap' +==== +USE testdb1 +==== +# Coverage of syntax variations. +show tables in testdb1 'alltypes' +---- TYPES +string +---- RESULTS +'alltypes' +==== +show tables in testdb1 like 'alltypes' +---- TYPES +string +---- RESULTS +'alltypes' +==== +show tables 'alltypes' +---- TYPES +string +---- RESULTS +'alltypes' +==== +show tables like 'alltypes' +---- TYPES +string +---- RESULTS +'alltypes' +==== +# Show databases +show databases +---- TYPES +string +---- RESULTS +'default' +'testdb1' +'tpch' +==== +show databases like 'def*' +---- TYPES +string +---- RESULTS +'default' ==== \ No newline at end of file