CalcitePlanner extends AbstractSQLPlanner { @Override public TranslatedQuery translate(String defaultTableSpace, String query, List<Object> parameters, boolean scan, boolean allowCache, boolean returnValues, int maxRows) throws StatementExecutionException { ensureDefaultTableSpaceBootedLocally(defaultTableSpace); int idx = SQLUtils.findQueryStart(query); if (idx != -1) { query = query.substring(idx); } if (parameters == null) { parameters = Collections.emptyList(); } String cacheKey = "scan:" + scan + ",defaultTableSpace:" + defaultTableSpace + ",query:" + query + ",returnValues:" + returnValues + ",maxRows:" + maxRows; boolean forceAcquireWriteLock; if (query.endsWith(" FOR UPDATE") && query.substring(0, 6).toLowerCase().equals("select")) { forceAcquireWriteLock = true; query = query.substring(0, query.length() - " FOR UPDATE".length()); } else { forceAcquireWriteLock = false; } if (allowCache) { ExecutionPlan cached = cache.get(cacheKey); if (cached != null) { return new TranslatedQuery(cached, new SQLStatementEvaluationContext(query, parameters, forceAcquireWriteLock, false)); } } if (isDDL(query)) { query = JSQLParserPlanner.rewriteExecuteSyntax(query); return fallback.translate(defaultTableSpace, query, parameters, scan, allowCache, returnValues, maxRows); } if (query.startsWith(TABLE_CONSISTENCY_COMMAND)) { query = JSQLParserPlanner.rewriteExecuteSyntax(query); return fallback.translate(defaultTableSpace, query, parameters, scan, allowCache, returnValues, maxRows); } if (query.startsWith(TABLESPACE_CONSISTENCY_COMMAND)) { query = JSQLParserPlanner.rewriteExecuteSyntax(query); return fallback.translate(defaultTableSpace, query, parameters, scan, allowCache, returnValues, maxRows); } if (!isCachable(query)) { allowCache = false; } try { if (query.startsWith("EXPLAIN ")) { query = query.substring("EXPLAIN ".length()); PlannerResult plan = runPlanner(defaultTableSpace, query); boolean upsert = detectUpsert(plan); PlannerOp finalPlan = convertRelNode(plan.topNode, plan.originalRowType, returnValues, upsert) .optimize(); ValuesOp values = new ValuesOp(manager.getNodeId(), new String[]{"name", "value"}, new Column[]{ column("name", ColumnTypes.STRING), column("value", ColumnTypes.STRING)}, java.util.Arrays.asList( java.util.Arrays.asList( new ConstantExpression("query", ColumnTypes.NOTNULL_STRING), new ConstantExpression(query, ColumnTypes.NOTNULL_STRING) ), java.util.Arrays.asList( new ConstantExpression("logicalplan", ColumnTypes.NOTNULL_STRING), new ConstantExpression(RelOptUtil.dumpPlan("", plan.logicalPlan, SqlExplainFormat.TEXT, SqlExplainLevel.ALL_ATTRIBUTES) , ColumnTypes.NOTNULL_STRING)), java.util.Arrays.asList( new ConstantExpression("plan", ColumnTypes.NOTNULL_STRING), new ConstantExpression(RelOptUtil.dumpPlan("", plan.topNode, SqlExplainFormat.TEXT, SqlExplainLevel.ALL_ATTRIBUTES), ColumnTypes.NOTNULL_STRING) ), java.util.Arrays.asList( new ConstantExpression("finalplan", ColumnTypes.NOTNULL_STRING), new ConstantExpression(finalPlan + "", ColumnTypes.NOTNULL_STRING) ) ) ); ExecutionPlan executionPlan = ExecutionPlan.simple( new SQLPlannedOperationStatement(values), values ); return new TranslatedQuery(executionPlan, new SQLStatementEvaluationContext(query, parameters, false, false)); } if (query.startsWith("SHOW")) { return calculateShowCreateTable(query, defaultTableSpace, parameters, manager); } PlannerResult plan = runPlanner(defaultTableSpace, query); boolean upsert = detectUpsert(plan); SQLPlannedOperationStatement sqlPlannedOperationStatement = new SQLPlannedOperationStatement( convertRelNode(plan.topNode, plan.originalRowType, returnValues, upsert) .optimize()); if (LOG.isLoggable(DUMP_QUERY_LEVEL)) { LOG.log(DUMP_QUERY_LEVEL, "Query: {0} --HerdDB Plan\n{1}", new Object[]{query, sqlPlannedOperationStatement.getRootOp()}); } if (!scan) { ScanStatement scanStatement = sqlPlannedOperationStatement.unwrap(ScanStatement.class); if (scanStatement != null) { Table tableDef = scanStatement.getTableDef(); CompiledSQLExpression where = scanStatement.getPredicate().unwrap(CompiledSQLExpression.class); SQLRecordKeyFunction keyFunction = IndexUtils.findIndexAccess(where, tableDef.getPrimaryKey(), tableDef, "=", tableDef); if (keyFunction == null || !keyFunction.isFullPrimaryKey()) { throw new StatementExecutionException("unsupported GET not on PK, bad where clause: " + query); } GetStatement get = new GetStatement(scanStatement.getTableSpace(), scanStatement.getTable(), keyFunction, scanStatement.getPredicate(), true); ExecutionPlan executionPlan = ExecutionPlan.simple( get ); if (allowCache) { cache.put(cacheKey, executionPlan); } return new TranslatedQuery(executionPlan, new SQLStatementEvaluationContext(query, parameters, forceAcquireWriteLock, false)); } } if (maxRows > 0) { PlannerOp op = new LimitOp(sqlPlannedOperationStatement.getRootOp(), new ConstantExpression(maxRows, ColumnTypes.NOTNULL_LONG), new ConstantExpression(0, ColumnTypes.NOTNULL_LONG)) .optimize(); sqlPlannedOperationStatement = new SQLPlannedOperationStatement(op); } PlannerOp rootOp = sqlPlannedOperationStatement.getRootOp(); ExecutionPlan executionPlan; if (rootOp.isSimpleStatementWrapper()) { executionPlan = ExecutionPlan.simple(rootOp.unwrap(herddb.model.Statement.class), rootOp); } else { executionPlan = ExecutionPlan.simple(sqlPlannedOperationStatement, rootOp); } if (allowCache) { cache.put(cacheKey, executionPlan); } return new TranslatedQuery(executionPlan, new SQLStatementEvaluationContext(query, parameters, forceAcquireWriteLock, false)); } catch (CalciteContextException ex) { LOG.log(Level.INFO, "Error while parsing '" + ex.getOriginalStatement() + "'", ex); throw new StatementExecutionException(ex.getMessage()); } catch (RelConversionException | ValidationException | SqlParseException ex) { LOG.log(Level.INFO, "Error while parsing '" + query + "'", ex); throw new StatementExecutionException(ex.getMessage().replace("org.apache.calcite.runtime.CalciteContextException: ", ""), ex); } catch (MetadataStorageManagerException ex) { LOG.log(Level.INFO, "Error while parsing '" + query + "'", ex); throw new StatementExecutionException(ex); } } CalcitePlanner(DBManager manager, PlansCache plansCache); @Override long getCacheSize(); @Override long getCacheHits(); @Override long getCacheMisses(); @Override void clearCache(); @Override TranslatedQuery translate(String defaultTableSpace, String query, List<Object> parameters, boolean scan, boolean allowCache, boolean returnValues, int maxRows); static int convertToHerdType(RelDataType type); static final String TABLE_CONSISTENCY_COMMAND; static final String TABLESPACE_CONSISTENCY_COMMAND; }
@Test public void showCreateTable_with_recreating_table_from_command_output() throws Exception { String nodeId = "localhost"; try (DBManager manager = new DBManager("localhost", new MemoryMetadataStorageManager(), new MemoryDataStorageManager(), new MemoryCommitLogManager(), null, null)) { manager.start(); CreateTableSpaceStatement st1 = new CreateTableSpaceStatement("tblspace1", Collections.singleton(nodeId), nodeId, 1, 0, 0); manager.executeStatement(st1, StatementEvaluationContext.DEFAULT_EVALUATION_CONTEXT(), TransactionContext.NO_TRANSACTION); manager.waitForTablespace("tblspace1", 10000); execute(manager, "CREATE TABLE tblspace1.test23 (k1 int auto_increment ," + "s1 string not null, n1 int, primary key(k1))", Collections.emptyList()); TranslatedQuery translatedQuery = manager.getPlanner().translate("tblspace1", "SHOW CREATE TABLE tblspace1.test23", Collections.emptyList(), true, false, true, -1); if (translatedQuery.plan.mainStatement instanceof SQLPlannedOperationStatement || translatedQuery.plan.mainStatement instanceof ScanStatement) { ScanResult scanResult = (ScanResult) manager.executePlan(translatedQuery.plan, translatedQuery.context, TransactionContext.NO_TRANSACTION); DataScanner dataScanner = scanResult.dataScanner; String[] columns = dataScanner.getFieldNames(); List<DataAccessor> records = dataScanner.consume(2); TuplesList tuplesList = new TuplesList(columns, records); assertTrue(tuplesList.columnNames[0].equalsIgnoreCase("tabledef")); Tuple values = (Tuple) records.get(0); assertTrue("CREATE TABLE tblspace1.test23(k1 integer auto_increment,s1 string not null,n1 integer,PRIMARY KEY(k1))".equalsIgnoreCase(values.get("tabledef").toString())); execute(manager, "DROP TABLE tblspace1.test23", Collections.emptyList()); try (DataScanner scan = scan(manager, "SELECT * FROM tblspace1.systables where table_name='test23'", Collections.emptyList())) { assertTrue(scan.consume().isEmpty()); } String showCreateTableResult = values.get("tabledef").toString(); execute(manager, showCreateTableResult, Collections.emptyList()); try (DataScanner scan = scan(manager, "SELECT * FROM tblspace1.systables where table_name='test23'", Collections.emptyList())) { assertFalse(scan.consume().isEmpty()); } } } }
@Test(expected = TableDoesNotExistException.class) public void showCreateTable_Non_Existent_Table() throws Exception { String nodeId = "localhost"; try (DBManager manager = new DBManager("localhost", new MemoryMetadataStorageManager(), new MemoryDataStorageManager(), new MemoryCommitLogManager(), null, null)) { manager.start(); CreateTableSpaceStatement st1 = new CreateTableSpaceStatement("tblspace1", Collections.singleton(nodeId), nodeId, 1, 0, 0); manager.executeStatement(st1, StatementEvaluationContext.DEFAULT_EVALUATION_CONTEXT(), TransactionContext.NO_TRANSACTION); manager.waitForTablespace("tblspace1", 10000); TranslatedQuery translatedQuery = manager.getPlanner().translate("herd", "SHOW CREATE TABLE tblspace1.test223", Collections.emptyList(), true, false, true, -1); } }
@Test(expected = TableSpaceDoesNotExistException.class) public void showCreateTable_Non_Existent_TableSpace() throws Exception { String nodeId = "localhost"; try (DBManager manager = new DBManager("localhost", new MemoryMetadataStorageManager(), new MemoryDataStorageManager(), new MemoryCommitLogManager(), null, null)) { manager.start(); TranslatedQuery translatedQuery = manager.getPlanner().translate("herd", "SHOW CREATE TABLE tblspace1.test223", Collections.emptyList(), true, false, true, -1); } }
@Test(expected = TableDoesNotExistException.class) public void showCreateTable_Within_TransactionContext() throws Exception { String nodeId = "localhost"; try (DBManager manager = new DBManager("localhost", new MemoryMetadataStorageManager(), new MemoryDataStorageManager(), new MemoryCommitLogManager(), null, null)) { manager.start(); CreateTableSpaceStatement st1 = new CreateTableSpaceStatement("tblspace1", Collections.singleton(nodeId), nodeId, 1, 0, 0); manager.executeStatement(st1, StatementEvaluationContext.DEFAULT_EVALUATION_CONTEXT(), TransactionContext.NO_TRANSACTION); manager.waitForTablespace("tblspace1", 10000); long tx = beginTransaction(manager, "tblspace1"); execute(manager, "CREATE TABLE tblspace1.test (k1 string primary key," + "s1 string not null)", Collections.emptyList(), new TransactionContext(tx)); TranslatedQuery translatedQuery = manager.getPlanner().translate("herd", "SHOW CREATE TABLE tblspace1.test", Collections.emptyList(), true, false, true, -1); } }
@Test public void showCreateTableTest() throws Exception { String nodeId = "localhost"; try (DBManager manager = new DBManager("localhost", new MemoryMetadataStorageManager(), new MemoryDataStorageManager(), new MemoryCommitLogManager(), null, null)) { manager.start(); CreateTableSpaceStatement st1 = new CreateTableSpaceStatement("tblspace1", Collections.singleton(nodeId), nodeId, 1, 0, 0); manager.executeStatement(st1, StatementEvaluationContext.DEFAULT_EVALUATION_CONTEXT(), TransactionContext.NO_TRANSACTION); manager.waitForTablespace("tblspace1", 10000); execute(manager, "CREATE TABLE tblspace1.test (k1 string primary key," + "s1 string not null default 'mydefault')", Collections.emptyList()); Table table = manager.getTableSpaceManager("tblspace1").getTableManager("test").getTable(); Column c = table.getColumn("s1"); assertEquals(ColumnTypes.NOTNULL_STRING, c.type); assertEquals("mydefault", c.defaultValue.to_string()); execute(manager, "CREATE TABLE tblspace1.test22 (k1 string not null ," + "s1 string not null, n1 int, primary key(k1,n1))", Collections.emptyList()); TranslatedQuery translatedQuery = manager.getPlanner().translate("tblspace1", "SHOW CREATE TABLE tblspace1.test", Collections.emptyList(), true, false, true, -1); if (translatedQuery.plan.mainStatement instanceof SQLPlannedOperationStatement || translatedQuery.plan.mainStatement instanceof ScanStatement) { ScanResult scanResult = (ScanResult) manager.executePlan(translatedQuery.plan, translatedQuery.context, TransactionContext.NO_TRANSACTION); DataScanner dataScanner = scanResult.dataScanner; String[] columns = dataScanner.getFieldNames(); List<DataAccessor> records = dataScanner.consume(2); TuplesList tuplesList = new TuplesList(columns, records); assertTrue(tuplesList.columnNames[0].equalsIgnoreCase("tabledef")); Tuple values = (Tuple) records.get(0); assertTrue("CREATE TABLE tblspace1.test(k1 string,s1 string not null DEFAULT 'mydefault',PRIMARY KEY(k1))".equalsIgnoreCase(values.get("tabledef").toString())); } translatedQuery = manager.getPlanner().translate("tblspace1", "SHOW CREATE TABLE tblspace1.test22", Collections.emptyList(), true, false, true, -1); if (translatedQuery.plan.mainStatement instanceof SQLPlannedOperationStatement || translatedQuery.plan.mainStatement instanceof ScanStatement) { ScanResult scanResult = (ScanResult) manager.executePlan(translatedQuery.plan, translatedQuery.context, TransactionContext.NO_TRANSACTION); DataScanner dataScanner = scanResult.dataScanner; String[] columns = dataScanner.getFieldNames(); List<DataAccessor> records = dataScanner.consume(2); TuplesList tuplesList = new TuplesList(columns, records); assertTrue(tuplesList.columnNames[0].equalsIgnoreCase("tabledef")); Tuple values = (Tuple) records.get(0); assertTrue("CREATE TABLE tblspace1.test22(k1 string not null,s1 string not null,n1 integer,Primary key(k1,n1))".equalsIgnoreCase(values.get("tabledef").toString())); } } }
@Test public void showCreateTableTest_with_Indexes() throws Exception { String nodeId = "localhost"; try (DBManager manager = new DBManager("localhost", new MemoryMetadataStorageManager(), new MemoryDataStorageManager(), new MemoryCommitLogManager(), null, null)) { manager.start(); CreateTableSpaceStatement st1 = new CreateTableSpaceStatement("tblspace1", Collections.singleton(nodeId), nodeId, 1, 0, 0); manager.executeStatement(st1, StatementEvaluationContext.DEFAULT_EVALUATION_CONTEXT(), TransactionContext.NO_TRANSACTION); manager.waitForTablespace("tblspace1", 10000); execute(manager, "CREATE TABLE tblspace1.test23 (k1 string not null ," + "s1 string not null, n1 int, primary key(k1,n1))", Collections.emptyList()); execute(manager, "CREATE INDEX ixn1s1 on tblspace1.test23(n1,s1)", Collections.emptyList()); execute(manager, "CREATE UNIQUE INDEX ixn1s2 on tblspace1.test23(n1,s1,k1)", Collections.emptyList()); TranslatedQuery translatedQuery = manager.getPlanner().translate("tblspace1", "SHOW CREATE TABLE tblspace1.test23 WITH INDEXES", Collections.emptyList(), true, false, true, -1); assertTrue (translatedQuery.plan.mainStatement instanceof SQLPlannedOperationStatement || translatedQuery.plan.mainStatement instanceof ScanStatement); ScanResult scanResult = (ScanResult) manager.executePlan(translatedQuery.plan, translatedQuery.context, TransactionContext.NO_TRANSACTION); DataScanner dataScanner = scanResult.dataScanner; String[] columns = dataScanner.getFieldNames(); List<DataAccessor> records = dataScanner.consumeAndClose(); TuplesList tuplesList = new TuplesList(columns, records); assertTrue(tuplesList.columnNames[0].equalsIgnoreCase("tabledef")); Tuple values = (Tuple) records.get(0); assertEquals("CREATE TABLE tblspace1.test23(k1 string not null,s1 string not null,n1 integer,PRIMARY KEY(k1,n1),INDEX ixn1s1(n1,s1),UNIQUE KEY ixn1s2 (n1,s1,k1))", values.get("tabledef").toString()); String showCreateCommandOutput = values.get("tabledef").toString(); execute(manager, "DROP TABLE tblspace1.test23", Collections.emptyList()); try (DataScanner scan = scan(manager, "SELECT * FROM tblspace1.systables where table_name='test23'", Collections.emptyList())) { assertTrue(scan.consume().isEmpty()); } try (DataScanner scan = scan(manager, "SELECT * FROM tblspace1.sysindexes where index_name='ixn1s1'", Collections.emptyList())) { assertTrue(scan.consume().isEmpty()); } try (DataScanner scan = scan(manager, "SELECT * FROM tblspace1.sysindexes where index_name='ixn1s2'", Collections.emptyList())) { assertTrue(scan.consume().isEmpty()); } execute(manager, showCreateCommandOutput, Collections.emptyList()); try (DataScanner scan = scan(manager, "SELECT * FROM tblspace1.systables where table_name='test23'", Collections.emptyList())) { assertFalse(scan.consume().isEmpty()); } try (DataScanner scan = scan(manager, "SELECT * FROM tblspace1.sysindexes where index_name='ixn1s1'", Collections.emptyList())) { assertFalse(scan.consume().isEmpty()); } } }