/** * Copyright (C) 2009-2013 FoundationDB, LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.foundationdb.server.rowdata; import com.foundationdb.ais.model.AISMerge; import com.foundationdb.ais.model.AkibanInformationSchema; import com.foundationdb.ais.model.DefaultNameGenerator; import com.foundationdb.ais.model.Group; import com.foundationdb.ais.model.Index; import com.foundationdb.ais.model.Routine; import com.foundationdb.ais.model.Sequence; import com.foundationdb.ais.model.Table; import com.foundationdb.ais.model.View; import com.foundationdb.qp.virtualadapter.VirtualAdapter; import com.foundationdb.qp.operator.QueryContext; import com.foundationdb.server.SimpleTableStatusCache; import com.foundationdb.server.TableStatus; import com.foundationdb.server.TableStatusCache; import com.foundationdb.server.api.DDLFunctions; import com.foundationdb.server.api.ddl.DDLFunctionsMockBase; import com.foundationdb.server.service.routines.MockRoutineLoader; import com.foundationdb.server.service.session.Session; import com.foundationdb.sql.StandardException; import com.foundationdb.sql.aisddl.AlterTableDDL; import com.foundationdb.sql.aisddl.IndexDDL; import com.foundationdb.sql.aisddl.RoutineDDL; import com.foundationdb.sql.aisddl.SequenceDDL; import com.foundationdb.sql.aisddl.TableDDL; import com.foundationdb.sql.aisddl.ViewDDL; import com.foundationdb.sql.optimizer.AISBinderContext; import com.foundationdb.sql.parser.AlterTableNode; import com.foundationdb.sql.parser.CreateAliasNode; import com.foundationdb.sql.parser.CreateIndexNode; import com.foundationdb.sql.parser.CreateTableNode; import com.foundationdb.sql.parser.CreateViewNode; import com.foundationdb.sql.parser.CreateSequenceNode; import com.foundationdb.sql.parser.SQLParser; import com.foundationdb.sql.parser.StatementNode; import com.foundationdb.sql.server.ServerSession; import com.foundationdb.sql.types.DataTypeDescriptor; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; public class SchemaFactory { private final static String DEFAULT_DEFAULT_SCHEMA = "test"; private final String defaultSchema; public SchemaFactory() { this(DEFAULT_DEFAULT_SCHEMA); } public SchemaFactory(String defaultSchema) { this.defaultSchema = defaultSchema; } public AkibanInformationSchema aisWithRowDefs(String... ddl) { AkibanInformationSchema ais = ais(ddl); buildRowDefs(ais); return ais; } public AkibanInformationSchema ais(String... ddl) { DDLFunctions ddlFunctions = new CreateOnlyDDLMock(); Session session = null; ddl(ddlFunctions, session, ddl); return ddlFunctions.getAIS(session); } public void ddl(DDLFunctions ddlFunctions, Session session, String... ddl){ ddl(ddlFunctions, session, null, null, null, ddl); } public void ddl(DDLFunctions ddlFunctions, Session session, List<DataTypeDescriptor> descriptors, List<String> columnNames, ServerSession server, String... ddl){ StringBuilder buffer = new StringBuilder(); for (String line : ddl) { buffer.append(line); } String fullDDL = buffer.toString(); SQLParser parser = new SQLParser(); List<StatementNode> nodes; try { nodes = parser.parseStatements(fullDDL); } catch(StandardException e) { throw new RuntimeException(e); } for(StatementNode stmt : nodes) { if (stmt instanceof CreateTableNode) { if( descriptors != null && columnNames != null){ TableDDL.createTable(ddlFunctions, session, defaultSchema, (CreateTableNode) stmt, null, descriptors, columnNames, server); } else { TableDDL.createTable(ddlFunctions, session, defaultSchema, (CreateTableNode) stmt, null); } } else if (stmt instanceof CreateIndexNode) { IndexDDL.createIndex(ddlFunctions, session, defaultSchema, (CreateIndexNode) stmt, null); } else if (stmt instanceof CreateViewNode) { ViewDDL.createView(ddlFunctions, session, defaultSchema, (CreateViewNode) stmt, new AISBinderContext(ddlFunctions.getAIS(session), defaultSchema), null, server); } else if (stmt instanceof CreateSequenceNode) { SequenceDDL.createSequence(ddlFunctions, session, defaultSchema, (CreateSequenceNode)stmt); } else if (stmt instanceof CreateAliasNode) { RoutineDDL.createRoutine(ddlFunctions, new MockRoutineLoader(), session, defaultSchema, (CreateAliasNode)stmt); } else if (stmt instanceof AlterTableNode) { AlterTableNode atNode = (AlterTableNode) stmt; assert !atNode.isTruncateTable() : "TRUNCATE not supported"; AlterTableDDL.alterTable(ddlFunctions, null /* DMLFunctions */, session, defaultSchema, (AlterTableNode)stmt, null /*QueryContext*/); } else { throw new IllegalStateException("Unsupported StatementNode type: " + stmt); } } } public void buildRowDefs(AkibanInformationSchema ais) { SimpleTableStatusCache tableStatusCache = new SimpleTableStatusCache(); // TODO: this attaches the TableStatus to each table. // This used to be done in RowDefBuilder#build() but no longer. for (final Table table : ais.getTables().values()) { final TableStatus status; if (table.isVirtual()) { status = tableStatusCache.getOrCreateVirtualTableStatus(table.getTableId(), VirtualAdapter.getFactory(table)); } else { status = tableStatusCache.createTableStatus(table); } table.tableStatus(status); } RowDefBuilder rowDefBuilder = new MockRowDefBuilder(ais, tableStatusCache); rowDefBuilder.build(); } private static class MockRowDefBuilder extends RowDefBuilder { public MockRowDefBuilder(AkibanInformationSchema ais, TableStatusCache tableStatusCache) { // Note: Somewhat fragile -- Session isn't needed for creating RowDefs super(null, ais, tableStatusCache); } @Override protected Map<Table,Integer> createOrdinalMap() { Map<Group,List<RowDef>> groupToRowDefs = getRowDefsByGroup(); Map<Table,Integer> ordinalMap = new HashMap<>(); for(List<RowDef> allRowDefs : groupToRowDefs.values()) { int tableOrdinal = 1; for(RowDef userRowDef : allRowDefs) { int ordinal = tableOrdinal++; userRowDef.table().setOrdinal(ordinal); ordinalMap.put(userRowDef.table(), ordinal); } } return ordinalMap; } } private static class CreateOnlyDDLMock extends DDLFunctionsMockBase { AkibanInformationSchema ais = new AkibanInformationSchema(); @Override public void createTable(Session session, Table newTable) { AISMerge merge = AISMerge.newForAddTable(getAISCloner(), new DefaultNameGenerator(ais), ais, newTable); merge.merge(); ais = merge.getAIS(); } @Override public void createTable(Session session, Table newTable, String queryExpression, QueryContext context, ServerSession server) { AISMerge merge = AISMerge.newForAddTable(getAISCloner(), new DefaultNameGenerator(ais), ais, newTable); merge.merge(); ais = merge.getAIS(); } @Override public void createView(Session session, View view) { ais = AISMerge.mergeView(getAISCloner(), ais, view); } @Override public AkibanInformationSchema getAIS(Session session) { return ais; } @Override public void createIndexes(Session session, Collection<? extends Index> indexesToAdd) { AISMerge merge = AISMerge.newForAddIndex(getAISCloner(), new DefaultNameGenerator(ais), ais); for(Index newIndex : indexesToAdd) { merge.mergeIndex(newIndex); } merge.merge(); ais = merge.getAIS(); } @Override public void createSequence(Session session, Sequence sequence) { AISMerge merge = AISMerge.newForOther(getAISCloner(), new DefaultNameGenerator(ais), ais); ais = merge.mergeSequence(sequence); } @Override public void createRoutine(Session session, Routine routine, boolean replaceExisting) { ais = AISMerge.mergeRoutine(getAISCloner(), ais, routine); } } }