SQL: Remove slightly used meta commands (#37506)

Remove SYS CATALOGS and SYS TABLE TYPES as they are a subset of SYS
TABLES (and thus somewhat redundant) and used only by JDBC.

Close #37409
This commit is contained in:
Costin Leau 2019-01-16 12:36:35 +02:00 committed by GitHub
parent 5a5e44d1de
commit 023bb2f1e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 806 additions and 1159 deletions

View File

@ -773,12 +773,16 @@ class JdbcDatabaseMetaData implements DatabaseMetaData, JdbcWrapper {
@Override
public ResultSet getCatalogs() throws SQLException {
return con.createStatement().executeQuery("SYS CATALOGS");
// TABLE_CAT is the first column
Object[][] data = queryColumn(con, "SYS TABLES CATALOG LIKE '%'", 1);
return memorySet(con.cfg, columnInfo("", "TABLE_CAT"), data);
}
@Override
public ResultSet getTableTypes() throws SQLException {
return con.createStatement().executeQuery("SYS TABLE TYPES");
// TABLE_TYPE (4)
Object[][] data = queryColumn(con, "SYS TABLES TYPE '%'", 4);
return memorySet(con.cfg, columnInfo("", "TABLE_TYPE"), data);
}
@Override
@ -1114,6 +1118,26 @@ class JdbcDatabaseMetaData implements DatabaseMetaData, JdbcWrapper {
return false;
}
//
// Utility methods
//
private static Object[][] queryColumn(JdbcConnection con, String query, int... cols) throws SQLException {
List<Object[]> data = new ArrayList<>();
try (ResultSet rs = con.createStatement().executeQuery(query)) {
while (rs.next()) {
Object[] row = new Object[cols.length];
for (int i = 0; i < cols.length; i++) {
row[i] = rs.getObject(cols[i]);
}
data.add(row);
}
}
return data.toArray(new Object[][] {});
}
private static List<JdbcColumnInfo> columnInfo(String tableName, Object... cols) throws JdbcSQLException {
List<JdbcColumnInfo> columns = new ArrayList<>();
@ -1156,7 +1180,7 @@ class JdbcDatabaseMetaData implements DatabaseMetaData, JdbcWrapper {
return new JdbcResultSet(cfg, null, new InMemoryCursor(columns, data));
}
static class InMemoryCursor implements Cursor {
private static class InMemoryCursor implements Cursor {
private final List<JdbcColumnInfo> columns;
private final Object[][] data;

View File

@ -71,12 +71,12 @@ public class DatabaseMetaDataTestCase extends JdbcIntegrationTestCase {
}
}
public void testGetTableTypes() throws Exception {
public void testGetTypeOfTables() throws Exception {
index("test1", body -> body.field("name", "bob"));
index("test2", body -> body.field("name", "bob"));
try (Connection h2 = LocalH2.anonymousDb(); Connection es = esJdbc()) {
h2.createStatement().executeUpdate("RUNSCRIPT FROM 'classpath:/setup_mock_metadata_get_table_types.sql'");
h2.createStatement().executeUpdate("RUNSCRIPT FROM 'classpath:/setup_mock_metadata_get_types_of_table.sql'");
CheckedSupplier<ResultSet, SQLException> all = () -> h2.createStatement()
.executeQuery("SELECT '" + clusterName() + "' AS TABLE_CAT, * FROM mock");
@ -88,6 +88,23 @@ public class DatabaseMetaDataTestCase extends JdbcIntegrationTestCase {
}
}
public void testGetTableTypes() throws Exception {
index("test1", body -> body.field("name", "bob"));
index("test2", body -> body.field("name", "bob"));
try (Connection h2 = LocalH2.anonymousDb(); Connection es = esJdbc()) {
h2.createStatement().executeUpdate("RUNSCRIPT FROM 'classpath:/setup_mock_metadata_get_table_types.sql'");
assertResultSets(h2.createStatement().executeQuery("SELECT * FROM mock"), es.getMetaData().getTableTypes());
}
}
public void testGetCatalogs() throws Exception {
try (Connection h2 = LocalH2.anonymousDb(); Connection es = esJdbc()) {
assertResultSets(h2.createStatement().executeQuery("SELECT '" + clusterName() + "' AS TABLE_CAT"),
es.getMetaData().getCatalogs());
}
}
public void testColumns() throws Exception {
index("test1", body -> body.field("name", "bob"));
index("test2", body -> {

View File

@ -160,8 +160,8 @@ public abstract class RestSqlUsageTestCase extends ESRestTestCase {
allTotalQueries += randomCommandExecutions;
for (int i = 0; i < randomCommandExecutions; i++) {
runSql(randomFrom("SHOW FUNCTIONS", "SHOW COLUMNS FROM library", "SHOW SCHEMAS",
"SHOW TABLES", "SYS CATALOGS", "SYS COLUMNS LIKE '%name'",
"SYS TABLES", "SYS TYPES"));
"SHOW TABLES", "SYS TABLES", "SYS COLUMNS LIKE '%name'",
"SYS TABLES TYPE '%'", "SYS TYPES"));
}
responseAsMap = getStats();
assertFeatureMetric(baseMetrics.get("command") + randomCommandExecutions, responseAsMap, "command");

View File

@ -1,15 +1,7 @@
CREATE TABLE mock (
TABLE_SCHEM VARCHAR,
TABLE_NAME VARCHAR,
TABLE_TYPE VARCHAR,
REMARKS VARCHAR,
TYPE_CAT VARCHAR,
TYPE_SCHEM VARCHAR,
TYPE_NAME VARCHAR,
SELF_REFERENCING_COL_NAME VARCHAR,
REF_GENERATION VARCHAR
) AS
SELECT '', 'test1', 'BASE TABLE', '', null, null, null, null, null FROM DUAL
SELECT 'ALIAS' FROM DUAL
UNION ALL
SELECT '', 'test2', 'BASE TABLE', '', null, null, null, null, null FROM DUAL
SELECT 'BASE TABLE' FROM DUAL
;

View File

@ -0,0 +1,15 @@
CREATE TABLE mock (
TABLE_SCHEM VARCHAR,
TABLE_NAME VARCHAR,
TABLE_TYPE VARCHAR,
REMARKS VARCHAR,
TYPE_CAT VARCHAR,
TYPE_SCHEM VARCHAR,
TYPE_NAME VARCHAR,
SELF_REFERENCING_COL_NAME VARCHAR,
REF_GENERATION VARCHAR
) AS
SELECT '', 'test1', 'BASE TABLE', '', null, null, null, null, null FROM DUAL
UNION ALL
SELECT '', 'test2', 'BASE TABLE', '', null, null, null, null, null FROM DUAL
;

View File

@ -55,7 +55,6 @@ statement
| (DESCRIBE | DESC) (tableLike=likePattern | tableIdent=tableIdentifier) #showColumns
| SHOW FUNCTIONS (likePattern)? #showFunctions
| SHOW SCHEMAS #showSchemas
| SYS CATALOGS #sysCatalogs
| SYS TABLES (CATALOG clusterLike=likePattern)?
(tableLike=likePattern | tableIdent=tableIdentifier)?
(TYPE string (',' string)* )? #sysTables
@ -63,7 +62,6 @@ statement
(TABLE tableLike=likePattern | tableIdent=tableIdentifier)?
(columnPattern=likePattern)? #sysColumns
| SYS TYPES ((PLUS | MINUS)? type=number)? #sysTypes
| SYS TABLE TYPES #sysTableTypes
;
query

View File

@ -16,9 +16,7 @@ import org.elasticsearch.xpack.sql.parser.SqlBaseParser.ShowFunctionsContext;
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.ShowSchemasContext;
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.ShowTablesContext;
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.StringContext;
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.SysCatalogsContext;
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.SysColumnsContext;
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.SysTableTypesContext;
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.SysTablesContext;
import org.elasticsearch.xpack.sql.parser.SqlBaseParser.SysTypesContext;
import org.elasticsearch.xpack.sql.plan.TableIdentifier;
@ -29,9 +27,7 @@ import org.elasticsearch.xpack.sql.plan.logical.command.ShowColumns;
import org.elasticsearch.xpack.sql.plan.logical.command.ShowFunctions;
import org.elasticsearch.xpack.sql.plan.logical.command.ShowSchemas;
import org.elasticsearch.xpack.sql.plan.logical.command.ShowTables;
import org.elasticsearch.xpack.sql.plan.logical.command.sys.SysCatalogs;
import org.elasticsearch.xpack.sql.plan.logical.command.sys.SysColumns;
import org.elasticsearch.xpack.sql.plan.logical.command.sys.SysTableTypes;
import org.elasticsearch.xpack.sql.plan.logical.command.sys.SysTables;
import org.elasticsearch.xpack.sql.plan.logical.command.sys.SysTypes;
import org.elasticsearch.xpack.sql.proto.SqlTypedParamValue;
@ -144,11 +140,6 @@ abstract class CommandBuilder extends LogicalPlanBuilder {
return new ShowColumns(source(ctx), index, visitLikePattern(ctx.likePattern()));
}
@Override
public Object visitSysCatalogs(SysCatalogsContext ctx) {
return new SysCatalogs(source(ctx));
}
@Override
public SysTables visitSysTables(SysTablesContext ctx) {
List<IndexType> types = new ArrayList<>();
@ -199,9 +190,4 @@ abstract class CommandBuilder extends LogicalPlanBuilder {
return new SysTypes(source(ctx), Integer.valueOf(type));
}
@Override
public Object visitSysTableTypes(SysTableTypesContext ctx) {
return new SysTableTypes(source(ctx));
}
}

View File

@ -119,18 +119,6 @@ class SqlBaseBaseListener implements SqlBaseListener {
* <p>The default implementation does nothing.</p>
*/
@Override public void exitShowSchemas(SqlBaseParser.ShowSchemasContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterSysCatalogs(SqlBaseParser.SysCatalogsContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitSysCatalogs(SqlBaseParser.SysCatalogsContext ctx) { }
/**
* {@inheritDoc}
*
@ -167,18 +155,6 @@ class SqlBaseBaseListener implements SqlBaseListener {
* <p>The default implementation does nothing.</p>
*/
@Override public void exitSysTypes(SqlBaseParser.SysTypesContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void enterSysTableTypes(SqlBaseParser.SysTableTypesContext ctx) { }
/**
* {@inheritDoc}
*
* <p>The default implementation does nothing.</p>
*/
@Override public void exitSysTableTypes(SqlBaseParser.SysTableTypesContext ctx) { }
/**
* {@inheritDoc}
*

View File

@ -74,13 +74,6 @@ class SqlBaseBaseVisitor<T> extends AbstractParseTreeVisitor<T> implements SqlBa
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitShowSchemas(SqlBaseParser.ShowSchemasContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitSysCatalogs(SqlBaseParser.SysCatalogsContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
@ -102,13 +95,6 @@ class SqlBaseBaseVisitor<T> extends AbstractParseTreeVisitor<T> implements SqlBa
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitSysTypes(SqlBaseParser.SysTypesContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*
* <p>The default implementation returns the result of calling
* {@link #visitChildren} on {@code ctx}.</p>
*/
@Override public T visitSysTableTypes(SqlBaseParser.SysTableTypesContext ctx) { return visitChildren(ctx); }
/**
* {@inheritDoc}
*

View File

@ -111,18 +111,6 @@ interface SqlBaseListener extends ParseTreeListener {
* @param ctx the parse tree
*/
void exitShowSchemas(SqlBaseParser.ShowSchemasContext ctx);
/**
* Enter a parse tree produced by the {@code sysCatalogs}
* labeled alternative in {@link SqlBaseParser#statement}.
* @param ctx the parse tree
*/
void enterSysCatalogs(SqlBaseParser.SysCatalogsContext ctx);
/**
* Exit a parse tree produced by the {@code sysCatalogs}
* labeled alternative in {@link SqlBaseParser#statement}.
* @param ctx the parse tree
*/
void exitSysCatalogs(SqlBaseParser.SysCatalogsContext ctx);
/**
* Enter a parse tree produced by the {@code sysTables}
* labeled alternative in {@link SqlBaseParser#statement}.
@ -159,18 +147,6 @@ interface SqlBaseListener extends ParseTreeListener {
* @param ctx the parse tree
*/
void exitSysTypes(SqlBaseParser.SysTypesContext ctx);
/**
* Enter a parse tree produced by the {@code sysTableTypes}
* labeled alternative in {@link SqlBaseParser#statement}.
* @param ctx the parse tree
*/
void enterSysTableTypes(SqlBaseParser.SysTableTypesContext ctx);
/**
* Exit a parse tree produced by the {@code sysTableTypes}
* labeled alternative in {@link SqlBaseParser#statement}.
* @param ctx the parse tree
*/
void exitSysTableTypes(SqlBaseParser.SysTableTypesContext ctx);
/**
* Enter a parse tree produced by {@link SqlBaseParser#query}.
* @param ctx the parse tree

View File

@ -71,13 +71,6 @@ interface SqlBaseVisitor<T> extends ParseTreeVisitor<T> {
* @return the visitor result
*/
T visitShowSchemas(SqlBaseParser.ShowSchemasContext ctx);
/**
* Visit a parse tree produced by the {@code sysCatalogs}
* labeled alternative in {@link SqlBaseParser#statement}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitSysCatalogs(SqlBaseParser.SysCatalogsContext ctx);
/**
* Visit a parse tree produced by the {@code sysTables}
* labeled alternative in {@link SqlBaseParser#statement}.
@ -99,13 +92,6 @@ interface SqlBaseVisitor<T> extends ParseTreeVisitor<T> {
* @return the visitor result
*/
T visitSysTypes(SqlBaseParser.SysTypesContext ctx);
/**
* Visit a parse tree produced by the {@code sysTableTypes}
* labeled alternative in {@link SqlBaseParser#statement}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitSysTableTypes(SqlBaseParser.SysTableTypesContext ctx);
/**
* Visit a parse tree produced by {@link SqlBaseParser#query}.
* @param ctx the parse tree

View File

@ -1,64 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.sql.plan.logical.command.sys;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.xpack.sql.expression.Attribute;
import org.elasticsearch.xpack.sql.plan.logical.command.Command;
import org.elasticsearch.xpack.sql.session.Rows;
import org.elasticsearch.xpack.sql.session.SchemaRowSet;
import org.elasticsearch.xpack.sql.session.SqlSession;
import org.elasticsearch.xpack.sql.tree.Source;
import org.elasticsearch.xpack.sql.tree.NodeInfo;
import java.util.List;
import static java.util.Collections.singletonList;
/**
* System command returning the catalogs (clusters) available.
* Currently returns only the current cluster name.
*/
public class SysCatalogs extends Command {
public SysCatalogs(Source source) {
super(source);
}
@Override
protected NodeInfo<SysCatalogs> info() {
return NodeInfo.create(this);
}
@Override
public List<Attribute> output() {
return singletonList(keyword("TABLE_CAT"));
}
@Override
public final void execute(SqlSession session, ActionListener<SchemaRowSet> listener) {
String cluster = session.indexResolver().clusterName();
listener.onResponse(Rows.of(output(), singletonList(singletonList(cluster))));
}
@Override
public int hashCode() {
return getClass().hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
return true;
}
}

View File

@ -1,70 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.sql.plan.logical.command.sys;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.xpack.sql.analysis.index.IndexResolver.IndexType;
import org.elasticsearch.xpack.sql.expression.Attribute;
import org.elasticsearch.xpack.sql.plan.logical.command.Command;
import org.elasticsearch.xpack.sql.session.Rows;
import org.elasticsearch.xpack.sql.session.SchemaRowSet;
import org.elasticsearch.xpack.sql.session.SqlSession;
import org.elasticsearch.xpack.sql.tree.Source;
import org.elasticsearch.xpack.sql.tree.NodeInfo;
import java.util.Comparator;
import java.util.List;
import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.toList;
/**
* System command returning the types of tables supported,
* index and alias.
*/
public class SysTableTypes extends Command {
public SysTableTypes(Source source) {
super(source);
}
@Override
protected NodeInfo<SysTableTypes> info() {
return NodeInfo.create(this);
}
@Override
public List<Attribute> output() {
return singletonList(keyword("TABLE_TYPE"));
}
@Override
public final void execute(SqlSession session, ActionListener<SchemaRowSet> listener) {
listener.onResponse(Rows.of(output(), IndexType.VALID.stream()
// *DBC requires ascending order
.sorted(Comparator.comparing(t -> t.toSql()))
.map(t -> singletonList(t.toSql()))
.collect(toList())));
}
@Override
public int hashCode() {
return getClass().hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
return true;
}
}

View File

@ -1,61 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.sql.plan.logical.command.sys;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.sql.TestUtils;
import org.elasticsearch.xpack.sql.analysis.analyzer.Analyzer;
import org.elasticsearch.xpack.sql.analysis.analyzer.Verifier;
import org.elasticsearch.xpack.sql.analysis.index.EsIndex;
import org.elasticsearch.xpack.sql.analysis.index.IndexResolution;
import org.elasticsearch.xpack.sql.analysis.index.IndexResolver;
import org.elasticsearch.xpack.sql.expression.function.FunctionRegistry;
import org.elasticsearch.xpack.sql.parser.SqlParser;
import org.elasticsearch.xpack.sql.plan.logical.command.Command;
import org.elasticsearch.xpack.sql.session.SqlSession;
import org.elasticsearch.xpack.sql.stats.Metrics;
import org.elasticsearch.xpack.sql.type.TypesTests;
import static java.util.Collections.singletonList;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class SysCatalogsTests extends ESTestCase {
private final SqlParser parser = new SqlParser();
@SuppressWarnings({ "rawtypes", "unchecked" })
private Tuple<Command, SqlSession> sql(String sql) {
EsIndex test = new EsIndex("test", TypesTests.loadMapping("mapping-multi-field-with-nested.json", true));
Analyzer analyzer = new Analyzer(TestUtils.TEST_CFG, new FunctionRegistry(), IndexResolution.valid(test),
new Verifier(new Metrics()));
Command cmd = (Command) analyzer.analyze(parser.createStatement(sql), true);
IndexResolver resolver = mock(IndexResolver.class);
when(resolver.clusterName()).thenReturn("cluster");
doAnswer(invocation -> {
((ActionListener) invocation.getArguments()[2]).onResponse(singletonList(test));
return Void.TYPE;
}).when(resolver).resolveAsSeparateMappings(any(), any(), any());
SqlSession session = new SqlSession(null, null, null, resolver, null, null, null, null);
return new Tuple<>(cmd, session);
}
public void testSysCatalogs() throws Exception {
Tuple<Command, SqlSession> sql = sql("SYS CATALOGS");
sql.v1().execute(sql.v2(), ActionListener.wrap(r -> {
assertEquals(1, r.size());
assertEquals("cluster", r.column(0));
}, ex -> fail(ex.getMessage())));
}
}

View File

@ -1,52 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.sql.plan.logical.command.sys;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.sql.TestUtils;
import org.elasticsearch.xpack.sql.analysis.analyzer.Analyzer;
import org.elasticsearch.xpack.sql.analysis.analyzer.Verifier;
import org.elasticsearch.xpack.sql.analysis.index.EsIndex;
import org.elasticsearch.xpack.sql.analysis.index.IndexResolution;
import org.elasticsearch.xpack.sql.analysis.index.IndexResolver;
import org.elasticsearch.xpack.sql.expression.function.FunctionRegistry;
import org.elasticsearch.xpack.sql.parser.SqlParser;
import org.elasticsearch.xpack.sql.plan.logical.command.Command;
import org.elasticsearch.xpack.sql.session.SqlSession;
import org.elasticsearch.xpack.sql.stats.Metrics;
import org.elasticsearch.xpack.sql.type.TypesTests;
import static org.mockito.Mockito.mock;
public class SysTableTypesTests extends ESTestCase {
private final SqlParser parser = new SqlParser();
private Tuple<Command, SqlSession> sql(String sql) {
EsIndex test = new EsIndex("test", TypesTests.loadMapping("mapping-multi-field-with-nested.json", true));
Analyzer analyzer = new Analyzer(TestUtils.TEST_CFG, new FunctionRegistry(), IndexResolution.valid(test),
new Verifier(new Metrics()));
Command cmd = (Command) analyzer.analyze(parser.createStatement(sql), true);
IndexResolver resolver = mock(IndexResolver.class);
SqlSession session = new SqlSession(null, null, null, resolver, null, null, null, null);
return new Tuple<>(cmd, session);
}
public void testSysTableTypes() throws Exception {
Tuple<Command, SqlSession> sql = sql("SYS TABLE TYPES");
sql.v1().execute(sql.v2(), ActionListener.wrap(r -> {
assertEquals(2, r.size());
assertEquals("ALIAS", r.column(0));
assertTrue(r.advanceRow());
assertEquals("BASE TABLE", r.column(0));
}, ex -> fail(ex.getMessage())));
}
}

View File

@ -93,8 +93,7 @@ public class VerifierMetricsTests extends ESTestCase {
public void testCommand() {
Counters c = sql(randomFrom("SHOW FUNCTIONS", "SHOW COLUMNS FROM library", "SHOW SCHEMAS",
"SHOW TABLES", "SYS CATALOGS", "SYS COLUMNS LIKE '%name'",
"SYS TABLES", "SYS TYPES"));
"SHOW TABLES", "SYS COLUMNS LIKE '%name'", "SYS TABLES", "SYS TYPES"));
assertEquals(0, where(c));
assertEquals(0, limit(c));
assertEquals(0, groupby(c));