/**
* 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.ais.model;
import com.foundationdb.server.store.format.StorageFormatRegistry;
import com.foundationdb.server.types.TInstance;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// AISBuilder can be used to create an AIS. The API is designed to sify the creation of an AIS during a scan
// of a dump. The user need not search the AIS and hold on to AIS objects (Table, Column, etc.). Instead,
// only names from the dump need be supplied.
public class AISBuilder {
private static final Logger LOG = LoggerFactory.getLogger(AISBuilder.class);
// API for creating capturing basic schema information
// Used when no generator is passed to constructor (i.e. stub or test).
// Generate temporary, but unique, IDs until the server assigns them.
private static class SimpleGenerator extends DefaultNameGenerator {
private int indexID = 1;
public SimpleGenerator(AkibanInformationSchema ais) {
super(ais);
indexID = super.getMaxIndexID();
}
@Override
public int generateIndexID(int rootTableID) {
return indexID++;
}
}
public AISBuilder() {
this(new AkibanInformationSchema());
}
public AISBuilder(AkibanInformationSchema ais) {
this(ais, new SimpleGenerator(ais), null);
}
public AISBuilder(AkibanInformationSchema ais, NameGenerator nameGenerator,
StorageFormatRegistry storageFormatRegistry) {
LOG.trace("creating builder");
this.ais = ais;
this.nameGenerator = nameGenerator;
this.storageFormatRegistry = storageFormatRegistry;
}
public NameGenerator getNameGenerator() {
return nameGenerator;
}
public Sequence sequence(String schemaName, String sequenceName,
long start, long increment, long minValue, long maxValue, boolean cycle) {
LOG.trace("sequence: {}.{} ", schemaName,sequenceName);
Sequence identityGenerator = Sequence.create(ais, schemaName, sequenceName, start, increment, minValue, maxValue, cycle);
finishStorageDescription(identityGenerator);
return identityGenerator;
}
public Table table(String schemaName, String tableName) {
LOG.trace("table: " + schemaName + "." + tableName);
return Table.create(ais, schemaName, tableName, nameGenerator.generateTableID(new TableName(schemaName, tableName)));
}
public void view(String schemaName, String tableName,
String definition, Properties definitionProperties,
Map<TableName,Collection<String>> tableColumnReferences) {
LOG.trace("view: " + schemaName + "." + tableName);
View.create(ais, schemaName, tableName,
definition, definitionProperties, tableColumnReferences);
}
public Column column(String schemaName, String tableName, String columnName,
Integer position, TInstance type, Boolean autoIncrement,
String defaultValue, String defaultFunction) {
Columnar table = ais.getColumnar(schemaName, tableName);
checkFound(table, "creating column", "user table",
concat(schemaName, tableName));
return column(table, columnName, position, type,
autoIncrement, defaultValue, defaultFunction);
}
private Column column(Columnar table, String columnName,
Integer position, TInstance type, Boolean autoIncrement,
String defaultValue, String defaultFunction) {
LOG.trace("column: " + table + "." + columnName);
Column column = Column.create(table, columnName, position, type);
column.setDefaultValue(defaultValue);
column.setDefaultFunction(defaultFunction);
column.finishCreating();
return column;
}
public void columnAsIdentity(String schemaName, String tableName, String columnName,
long start, long increment, boolean defaultIdentity) {
// The merge process will generate the *real* sequence name
// Note: This will generate the same name twice and is intended for now, see bug1168552 discussion.
String sequenceName = tableName + "-temp-sequence-1";
long min = (increment > 0) ? start : Long.MIN_VALUE;
long max = (increment > 0) ? Long.MAX_VALUE : start;
sequence(schemaName, sequenceName, start, increment, min, max, false);
columnAsIdentity(schemaName, tableName, columnName, sequenceName, defaultIdentity);
}
public void columnAsIdentity (String schemaName, String tableName, String columnName,
String sequenceName, Boolean defaultIdentity) {
LOG.trace("column as identity: " + schemaName + "." + tableName + "." + columnName + ": " + sequenceName);
Column column = ais.getTable(schemaName, tableName).getColumn(columnName);
column.setDefaultIdentity(defaultIdentity);
Sequence identityGenerator = ais.getSequence(new TableName (schemaName, sequenceName));
column.setIdentityGenerator(identityGenerator);
}
public void pk(String schemaName, String tableName) {
TableName constraintName = nameGenerator.generatePKConstraintName(schemaName, tableName);
index(schemaName, tableName, Index.PRIMARY, true, true, constraintName);
}
public void pkConstraint(String schemaName, String tableName, TableName constraintName) {
index(schemaName, tableName, Index.PRIMARY, true, true, constraintName);
}
public void unique(String schemaName, String tableName, String indexName) {
TableName constraintName = nameGenerator.generateUniqueConstraintName(schemaName, tableName);
index(schemaName, tableName, indexName, true, false, constraintName);
}
public void uniqueConstraint(String schemaName, String tableName, String indexName, TableName constraintName) {
index(schemaName, tableName, indexName, true, false, constraintName);
}
public void index(String schemaName, String tableName, String indexName) {
index(schemaName, tableName, indexName, false, false, null);
}
public void index(String schemaName, String tableName, String indexName, Boolean unique, Boolean isPrimaryKey, TableName constraintName) {
index(schemaName, tableName, indexName, unique, isPrimaryKey, constraintName, null);
}
public void index(String schemaName, String tableName, String indexName, Boolean unique, Boolean isPrimaryKey, TableName constraintName, StorageDescription copyStorage) {
Table table = ais.getTable(schemaName, tableName);
int indexID = nameGenerator.generateIndexID(getRooTableID(table));
LOG.trace("index: " + schemaName + "." + tableName + "." + indexName);
checkFound(table, "creating index", "table", concat(schemaName, tableName));
Index index = TableIndex.create(ais, table, indexName, indexID, unique, isPrimaryKey, constraintName);
finishStorageDescription(index, copyStorage);
}
// deprecate?
public void groupIndex(String groupName, String indexName, Boolean unique, Index.JoinType joinType)
{
groupIndex(findFullGroupName(groupName), indexName, unique, joinType);
}
public void groupIndex(TableName groupName, String indexName, Boolean unique, Index.JoinType joinType)
{
LOG.trace("groupIndex: " + groupName + "." + indexName);
Group group = ais.getGroup(groupName);
checkFound(group, "creating group index", "group", groupName);
setRootIfNeeded(group);
int indexID = nameGenerator.generateIndexID(getRooTableID(group.getRoot()));
Index index = GroupIndex.create(ais, group, indexName, indexID, unique, false, joinType);
finishStorageDescription(index);
}
public void indexColumn(String schemaName, String tableName,
String indexName, String columnName, Integer position,
Boolean ascending, Integer indexedLength) {
LOG.trace("indexColumn: " + schemaName + "." + tableName + "."
+ indexName + ":" + columnName);
Table table = ais.getTable(schemaName, tableName);
checkFound(table, "creating index column", "table",
concat(schemaName, tableName));
Column column = table.getColumn(columnName);
checkFound(column, "creating index column", "column",
concat(schemaName, tableName, columnName));
Index index = table.getIndex(indexName);
checkFound(table, "creating index column", "index",
concat(schemaName, tableName, indexName));
IndexColumn.create(index, column, position, ascending, indexedLength);
}
// deprecate?
public void groupIndexColumn(String groupName, String indexName, String schemaName, String tableName,
String columnName, Integer position) {
groupIndexColumn(findFullGroupName(groupName), indexName, schemaName, tableName, columnName, position);
}
public void groupIndexColumn(TableName groupName, String indexName, String schemaName, String tableName,
String columnName, Integer position)
{
LOG.trace("groupIndexColumn: " + groupName + "." + indexName + ":" + columnName);
Group group = ais.getGroup(groupName);
checkFound(group, "creating group index column", "group", groupName);
Index index = group.getIndex(indexName);
checkFound(index, "creating group index column", "index", concat(groupName.toString(), indexName));
Table table = ais.getTable(schemaName, tableName);
if (!table.getGroup().getName().equals(groupName)) {
throw new IllegalArgumentException("group name mismatch: " + groupName + " != " + table.getGroup());
}
checkFound(table, "creating group index column", "table", concat(schemaName, tableName));
Column column = table.getColumn(columnName);
checkFound(column, "creating group index column", "column", concat(schemaName, tableName, columnName));
IndexColumn.create(index, column, position, true, null);
}
public void fullTextIndex(TableName tableName, String indexName)
{
LOG.trace("fullTextIndex: " + tableName + "." + indexName);
Table table = ais.getTable(tableName);
checkFound(table, "creating full text index", "table", tableName);
int indexID = nameGenerator.generateIndexID(getRooTableID(table));
FullTextIndex.create(ais, table, indexName, indexID);
}
public void fullTextIndexColumn(TableName indexedTableName, String indexName,
String schemaName, String tableName, String columnName, Integer position)
{
LOG.trace("fullTextIndexColumn: " + indexedTableName + "." + indexName + ":" + columnName);
Table indexedTable = ais.getTable(indexedTableName);
checkFound(indexedTable, "creating full text index column", "table", indexedTableName);
Index index = indexedTable.getFullTextIndex(indexName);
checkFound(index, "creating full text index column", "index", concat(tableName.toString(), indexName));
Table table = ais.getTable(schemaName, tableName);
checkFound(table, "creating full text index column", "table", concat(schemaName, tableName));
Column column = table.getColumn(columnName);
checkFound(column, "creating full text index column", "column", concat(schemaName, tableName, columnName));
IndexColumn.create(index, column, position, true, null);
}
public void joinTables(String joinName, String parentSchemaName,
String parentTableName, String childSchemaName,
String childTableName) {
LOG.trace("joinTables: " + joinName + ": " + childSchemaName + "."
+ childTableName + " -> " + parentSchemaName + "."
+ parentTableName);
Table child = ais.getTable(childSchemaName, childTableName);
checkFound(child, "creating join", "child table",
concat(childSchemaName, childTableName));
Table parent = ais.getTable(parentSchemaName, parentTableName);
if (parent == null) {
TableName parentName = new TableName(parentSchemaName,
parentTableName);
ForwardTableReference forwardTableReference = new ForwardTableReference(
joinName, parentName, child);
forwardReferences.put(joinName, forwardTableReference);
} else {
Join.create(ais, joinName, parent, child);
}
}
public void joinColumns(String joinName, String parentSchemaName,
String parentTableName, String parentColumnName,
String childSchemaName, String childTableName,
String childColumnName)
{
LOG.trace("joinColumns: " + joinName + ": " + childSchemaName + "."
+ childTableName + "." + childColumnName + " -> "
+ parentSchemaName + "." + parentTableName + "."
+ parentColumnName);
// Get child info
Table childTable = ais
.getTable(childSchemaName, childTableName);
checkFound(childTable, "creating join column", "child table",
concat(childSchemaName, childTableName));
Column childColumn = childTable.getColumn(childColumnName);
checkFound(childColumn, "creating join column", "child column",
concat(childSchemaName, childTableName, childColumnName));
// Handle parent - could be a forward reference
Table parentTable = ais.getTable(parentSchemaName,
parentTableName);
if (parentTable == null) {
// forward reference
ForwardTableReference forwardTableReference = forwardReferences
.get(joinName);
forwardTableReference.addColumnReference(parentColumnName,
childColumn);
} else {
// we've seen the child table
Column parentColumn = parentTable.getColumn(parentColumnName);
checkFound(parentColumn, "creating join column", "parent column",
concat(parentSchemaName, parentTableName, parentColumnName));
Join join = ais.getJoin(joinName);
checkFound(
join,
"creating join column",
"join",
concat(parentSchemaName, parentTableName, parentColumnName)
+ "/"
+ concat(childSchemaName, childTableName,
childColumnName));
join.addJoinColumn(parentColumn, childColumn);
}
}
public void routine(String schemaName, String routineName,
String language, Routine.CallingConvention callingConvention) {
LOG.trace("routine: {}.{} ", schemaName, routineName);
Routine routine = Routine.create(ais, schemaName, routineName,
language, callingConvention);
}
public void parameter(String schemaName, String routineName,
String parameterName, Parameter.Direction direction,
TInstance type) {
LOG.trace("parameter: {} {}", concat(schemaName, routineName), parameterName);
Routine routine = ais.getRoutine(schemaName, routineName);
checkFound(routine, "creating parameter", "routine",
concat(schemaName, routineName));
Parameter parameter = Parameter.create(routine, parameterName, direction,
type);
}
public void routineExternalName(String schemaName, String routineName,
String jarSchema, String jarName,
String className, String methodName) {
LOG.trace("external name: {} {}", concat(schemaName, routineName), concat(jarName, className, methodName));
Routine routine = ais.getRoutine(schemaName, routineName);
checkFound(routine, "external name", "routine",
concat(schemaName, routineName));
SQLJJar sqljJar = null;
if (jarName != null) {
sqljJar = ais.getSQLJJar(jarSchema, jarName);
checkFound(sqljJar, "external name", "SQJ/J jar",
concat(jarSchema, jarName));
}
routine.setExternalName(sqljJar, className, methodName);
}
public void routineDefinition(String schemaName, String routineName,
String definition) {
LOG.trace("external name: {} {}", concat(schemaName, routineName), definition);
Routine routine = ais.getRoutine(schemaName, routineName);
checkFound(routine, "external name", "routine",
concat(schemaName, routineName));
routine.setDefinition(definition);
}
public void routineSQLAllowed(String schemaName, String routineName,
Routine.SQLAllowed sqlAllowed) {
LOG.trace("SQL allowed: {} {}", concat(schemaName, routineName), sqlAllowed);
Routine routine = ais.getRoutine(schemaName, routineName);
checkFound(routine, "SQL allowed", "routine",
concat(schemaName, routineName));
routine.setSQLAllowed(sqlAllowed);
}
public void routineDynamicResultSets(String schemaName, String routineName,
int dynamicResultSets) {
LOG.trace("dynamic result sets: {} {}", concat(schemaName, routineName), dynamicResultSets);
Routine routine = ais.getRoutine(schemaName, routineName);
checkFound(routine, "dynamic result sets", "routine",
concat(schemaName, routineName));
routine.setDynamicResultSets(dynamicResultSets);
}
public void routineDeterministic(String schemaName, String routineName,
boolean deterministic) {
LOG.trace("deterministic: {} {}", concat(schemaName, routineName), deterministic);
Routine routine = ais.getRoutine(schemaName, routineName);
checkFound(routine, "deterministic", "routine",
concat(schemaName, routineName));
routine.setDeterministic(deterministic);
}
public void routineCalledOnNullInput(String schemaName, String routineName,
boolean calledOnNullInput) {
LOG.trace("called on NULL input: {} {}", concat(schemaName, routineName), calledOnNullInput);
Routine routine = ais.getRoutine(schemaName, routineName);
checkFound(routine, "calledOnNullInput", "routine",
concat(schemaName, routineName));
routine.setCalledOnNullInput(calledOnNullInput);
}
public void sqljJar(String schemaName, String jarName, URL url) {
LOG.trace("SQL/J jar: {}.{} ", schemaName, jarName);
SQLJJar sqljJar = SQLJJar.create(ais, schemaName, jarName, url);
}
public void basicSchemaIsComplete() {
LOG.trace("basicSchemaIsComplete");
for (ForwardTableReference forwardTableReference : forwardReferences.values()) {
Table childTable = forwardTableReference.childTable();
Table parentTable = ais.getTable(forwardTableReference
.parentTableName().getSchemaName(), forwardTableReference
.parentTableName().getTableName());
if (parentTable != null){
Join join = Join.create(ais, forwardTableReference.joinName(),
parentTable, childTable);
for (ForwardColumnReference forwardColumnReference : forwardTableReference
.forwardColumnReferences()) {
Column childColumn = forwardColumnReference.childColumn();
Column parentColumn = parentTable
.getColumn(forwardColumnReference.parentColumnName());
checkFound(childColumn, "marking basic schema complete",
"parent column",
forwardColumnReference.parentColumnName());
join.addJoinColumn(parentColumn, childColumn);
}
}
}
forwardReferences.clear();
}
// API for describing groups
public void createGroup(String groupName, String groupSchemaName) {
createGroup(groupName, groupSchemaName, null);
}
public void createGroup(String groupName, String groupSchemaName,
StorageDescription copyStorage) {
LOG.trace("createGroup: {} in {}", groupName, groupSchemaName);
Group group = Group.create(ais, groupSchemaName, groupName);
finishStorageDescription(group, copyStorage);
}
// deprecate?
public void deleteGroup(String groupName) {
deleteGroup(findFullGroupName(groupName));
}
public void deleteGroup(TableName groupName) {
LOG.trace("deleteGroup: " + groupName);
Group group = ais.getGroup(groupName);
checkFound(group, "deleting group", "group", groupName);
boolean groupEmpty = true;
for (Table table : ais.getTables().values()) {
if (table.getGroup() == group) {
groupEmpty = false;
}
}
if (groupEmpty) {
ais.deleteGroup(group);
} else {
throw new GroupNotEmptyException(group);
}
}
// deprecate?
public void addTableToGroup(String groupName, String schemaName,
String tableName) {
addTableToGroup(findFullGroupName(groupName), schemaName, tableName);
}
public void addTableToGroup(TableName groupName, String schemaName, String tableName) {
LOG.trace("addTableToGroup: " + groupName + ": " + schemaName + "."
+ tableName);
// group
Group group = ais.getGroup(groupName);
checkFound(group, "adding table to group", "group", groupName);
// table
Table table = ais.getTable(schemaName, tableName);
checkFound(table, "adding table to group", "table",
concat(schemaName, tableName));
checkGroupAddition(group, table.getGroup(),
concat(schemaName, tableName));
setTablesGroup(table, group);
}
// addJoinToGroup and removeJoinFromGroup identify a join based on parent
// and child tables. This is OK for
// removeJoinFromGroup because of the restrictions on group structure. It
// DOES NOT WORK for addJoinToGroup,
// because there could be multiple candidate joins between a pair of tables.
// deprecate?
public void addJoinToGroup(String groupName, String joinName, Integer weight) {
addJoinToGroup(findFullGroupName(groupName), joinName, weight);
}
public void addJoinToGroup(TableName groupName, String joinName, Integer weight) {
LOG.trace("addJoinToGroup: " + groupName + ": " + joinName);
// join
Join join = ais.getJoin(joinName);
checkFound(join, "adding join to group", "join", joinName);
// group
Group group = ais.getGroup(groupName);
checkFound(group, "adding join to group", "group", groupName);
// parent
String parentSchemaName = join.getParent().getName().getSchemaName();
String parentTableName = join.getParent().getName().getTableName();
Table parent = ais.getTable(parentSchemaName, parentTableName);
checkFound(parent, "adding join to group", "parent table",
concat(parentSchemaName, parentTableName));
checkGroupAddition(group, parent.getGroup(),
concat(parentSchemaName, parentTableName));
setTablesGroup(parent, group);
// child
String childSchemaName = join.getChild().getName().getSchemaName();
String childTableName = join.getChild().getName().getTableName();
Table child = ais.getTable(childSchemaName, childTableName);
checkFound(child, "adding join to group", "child table",
concat(childSchemaName, childTableName));
checkGroupAddition(group, child.getGroup(),
concat(childSchemaName, childTableName));
checkCycle(child, group);
setTablesGroup(child, group);
join.setGroup(group);
assert join.getParent() == parent : join;
checkGroupAddition(group, join.getGroup(), joinName);
}
public void removeTableFromGroup(String groupName, String schemaName,
String tableName) {
removeTableFromGroup(findFullGroupName(groupName), schemaName, tableName);
}
public void removeTableFromGroup(TableName groupName, String schemaName, String tableName) {
LOG.trace("removeTableFromGroup: " + groupName + ": " + schemaName + "."
+ tableName);
// This is only valid for a single-table group.
// group
Group group = ais.getGroup(groupName);
checkFound(group, "removing join from group", "group", groupName);
// table
Table table = ais.getTable(schemaName, tableName);
checkFound(table, "removing join from group", "table table",
concat(schemaName, tableName));
checkInGroup(group, table, "removing join from group", "table table");
if (table.getParentJoin() != null || !table.getChildJoins().isEmpty()) {
throw new GroupStructureException(
"Cannot remove table from a group unless "
+ "it is the only table in the group, group "
+ group.getName() + ", table " + table.getName());
}
setTablesGroup(table, null);
}
// deprecate?
public void removeJoinFromGroup(String groupName, String joinName) {
removeJoinFromGroup(findFullGroupName(groupName), joinName);
}
public void removeJoinFromGroup(TableName groupName, String joinName) {
LOG.trace("removeJoinFromGroup: " + groupName + ": " + joinName);
// join
Join join = ais.getJoin(joinName);
checkFound(join, "removing join from group", "join", joinName);
// group
Group group = ais.getGroup(groupName);
checkFound(group, "removing join from group", "group", groupName);
checkInGroup(group, join, "removing join from group", "child table");
// parent
String parentSchemaName = join.getParent().getName().getSchemaName();
String parentTableName = join.getParent().getName().getTableName();
Table parent = ais.getTable(parentSchemaName, parentTableName);
checkFound(parent, "removing join from group", "parent table",
concat(parentSchemaName, parentTableName));
checkInGroup(group, parent, "removing join from group", "parent table");
// child
String childSchemaName = join.getChild().getName().getSchemaName();
String childTableName = join.getChild().getName().getTableName();
Table child = ais.getTable(childSchemaName, childTableName);
checkFound(child, "removing join from group", "child table",
concat(childSchemaName, childTableName));
checkInGroup(group, child, "removing join from group", "child table");
// Remove the join from the group
join.setGroup(null);
// Remove the parent from the group if it isn't involved in any other
// joins in this group.
if (parent.getChildJoins().size() == 0
&& parent.getParentJoin() == null) {
setTablesGroup(parent, null);
}
// Same for the child (except we know that parent is null)
assert child.getParentJoin() == null;
if (child.getChildJoins().size() == 0) {
setTablesGroup(child, null);
}
}
// deprecate?
public void moveTreeToGroup(String schemaName, String tableName,
String groupName, String joinName) {
moveTreeToGroup(schemaName, tableName, findFullGroupName(groupName), joinName);
}
public void moveTreeToGroup(String schemaName, String tableName, TableName groupName, String joinName) {
LOG.trace("moveTree: " + schemaName + "." + tableName + " -> "
+ groupName + " via join " + joinName);
// table
Table table = ais.getTable(schemaName, tableName);
checkFound(table, "moving tree", "table", concat(schemaName, tableName));
// group
Group group = ais.getGroup(groupName);
checkFound(group, "moving tree", "group", groupName);
// join
Join join = ais.getJoin(joinName);
checkFound(join, "moving tree", "join", joinName);
// Remove table's parent join from its current group (if there is a
// parent)
Join parentJoin = table.getParentJoin();
if (parentJoin != null) {
parentJoin.setGroup(null);
}
// Move table to group. Get the children first, because moving the table
// to another group will cause
// getChildJoins() to return empty.
List<Join> children = table.getChildJoins();
setTablesGroup(table, group);
// Move the join to the group
join.setGroup(group);
moveTree(children, group);
}
public void moveTreeToEmptyGroup(String schemaName, String tableName,
String groupName) {
moveTreeToEmptyGroup(schemaName, tableName, findFullGroupName(groupName));
}
public void moveTreeToEmptyGroup(String schemaName, String tableName, TableName groupName) {
LOG.trace("moveTree: " + schemaName + "." + tableName
+ " -> empty group " + groupName);
// table
Table table = ais.getTable(schemaName, tableName);
checkFound(table, "moving tree", "table", concat(schemaName, tableName));
// group
Group group = ais.getGroup(groupName);
checkFound(group, "moving tree", "group", groupName);
// Remove table's parent join from its current group (if there is a
// parent)
Join parentJoin = table.getParentJoin();
if (parentJoin != null) {
parentJoin.setGroup(null);
}
// Move table to group. Get the children first (see comment in
// moveTreeToGroup).
List<Join> children = table.getChildJoins();
setTablesGroup(table, group);
moveTree(children, group);
}
public void groupingIsComplete() {
LOG.trace("groupingIsComplete");
// Hook up root tables
for(Group group : ais.getGroups().values()) {
setRootIfNeeded(group);
finishStorageDescription(group);
}
// Create hidden PKs if needed. Needs group hooked up before it can be called (to generate index id).
for (Table table : ais.getTables().values()) {
table.endTable(nameGenerator);
// endTable may have created new index, set its tree name if so
Index index = table.getPrimaryKeyIncludingInternal().getIndex();
finishStorageDescription(index);
// endTable on non-virtual tables may have created a new sequence, set its tree name if so
if (!table.isVirtual()) {
Column column = index.getKeyColumns().get(0).getColumn();
if (column.isAkibanPKColumn()) {
Sequence sequence = column.getIdentityGenerator();
finishStorageDescription (sequence);
}
}
}
}
public void finishStorageDescription(HasStorage object) {
finishStorageDescription(object, null);
}
public void finishStorageDescription(HasStorage object, StorageDescription copyStorage) {
if (copyStorage != null) {
assert (object.getStorageDescription() == null);
object.setStorageDescription(copyStorage.cloneForObject(object));
}
if (storageFormatRegistry != null) {
storageFormatRegistry.finishStorageDescription(object, nameGenerator);
}
}
public void clearGroupings() {
LOG.trace("clear groupings");
ais.getGroups().clear();
for (Table table : ais.getTables().values()) {
setTablesGroup(table, null);
}
for (Join join : ais.getJoins().values()) {
join.setGroup(null);
}
}
public void foreignKey(String referencingSchemaName, String referencingTableName, List<String> referencingColumnNames,
String referencedSchemaName, String referencedTableName, List<String> referencedColumnNames,
ForeignKey.Action deleteAction, ForeignKey.Action updateAction,
boolean deferrable, boolean initiallyDeferred,
String name) {
LOG.trace("foreign key: " + referencingSchemaName + "." + referencingTableName + referencingColumnNames
+ " references " + referencedSchemaName + "." + referencedTableName + referencedColumnNames);
Table referencingTable = ais.getTable(referencingSchemaName, referencingTableName);
checkFound(referencingTable, "creating foreign key", "referencing table", concat(referencingSchemaName, referencingTableName));
List<Column> referencingColumns = new ArrayList<>(referencingColumnNames.size());
for (String columnName : referencingColumnNames) {
Column column = referencingTable.getColumn(columnName);
checkFound(column, "creating foreign key", "referencing column",
concat(referencingSchemaName, referencingTableName, columnName));
referencingColumns.add(column);
}
Table referencedTable = ais.getTable(referencedSchemaName, referencedTableName);
checkFound(referencedTable, "creating foreign key", "referenced table", concat(referencedSchemaName, referencedTableName));
List<Column> referencedColumns = new ArrayList<>(referencedColumnNames.size());
for (String columnName : referencedColumnNames) {
Column column = referencedTable.getColumn(columnName);
checkFound(column, "creating foreign key", "referenced column",
concat(referencedSchemaName, referencedTableName, columnName));
referencedColumns.add(column);
}
ForeignKey.create(ais, name,
referencingTable, referencingColumns,
referencedTable, referencedColumns,
deleteAction, updateAction,
deferrable, initiallyDeferred);
}
// API for getting the created AIS
public AkibanInformationSchema akibanInformationSchema() {
LOG.trace("getting AIS");
return ais;
}
private Table findRoot(Group group) {
Table root = null;
for(Table table : ais.getTables().values()) {
if((table.getGroup() == group) && table.isRoot()) {
if(root != null) {
return null; // Multiple roots
}
root = table;
}
}
return root;
}
private void setRootIfNeeded(Group group) {
if(group.getRoot() == null) {
group.setRootTable(findRoot(group));
}
}
private void moveTree(List<Join> joins, Group group) {
LOG.debug("moving tree " + joins + " to group " + group);
for (Join join : joins) {
List<Join> children = join.getChild().getChildJoins();
setTablesGroup(join.getChild(), group);
join.setGroup(group);
moveTree(children, group);
}
}
private void checkFound(Object object, String action, String needed, TableName name) {
checkFound(object, action, needed, name.toString());
}
private void checkFound(Object object, String action, String needed,
String name) {
if (object == null) {
throw new NoSuchObjectException(action, needed, name);
}
}
private void checkGroupAddition(Group group, Group existingGroup,
String name) {
if (existingGroup != null && existingGroup != group) {
throw new GroupStructureException(group, existingGroup, name);
}
}
private void checkInGroup(Group group, HasGroup object, String action,
String objectDescription) {
if (object.getGroup() != group) {
throw new NotInGroupException(group, object, action,
objectDescription);
}
}
private void checkCycle(Table table, Group group) {
if (table.getGroup() == group) {
String exception = table + " is already in " + group
+ ". Group must be acyclic";
throw new GroupStructureException(exception);
}
}
private String concat(String... strings) {
StringBuilder buffer = new StringBuilder();
for (int i = 0; i < strings.length; i++) {
if (i > 0) {
buffer.append(".");
}
buffer.append(strings[i]);
}
return buffer.toString();
}
private void setTablesGroup(Table table, Group group) {
table.setGroup(group);
}
private int getRooTableID(Table table) {
if(table == null) {
return -1;
}
if(table.getGroup() != null && table.getGroup().getRoot() != null) {
return table.getGroup().getRoot().getTableId();
}
return table.getTableId();
}
private TableName findFullGroupName(String groupName) {
Group group = ais.getGroup(groupName);
checkFound(group, "looking up group without schema", "group", groupName);
return group.getName();
}
// State
static final class ColumnName {
private final TableName table;
private final String columnName;
public ColumnName(TableName table, String columnName) {
this.table = table;
this.columnName = columnName;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((table == null) ? 0 : table.hashCode());
result = prime * result
+ ((columnName == null) ? 0 : columnName.hashCode());
return result;
}
@Override
public boolean equals(Object object) {
if (object == this)
return true;
if (!(object instanceof ColumnName))
return false;
ColumnName other = (ColumnName) object;
if (this.table == null && other.table != null)
return false;
if (!this.table.equals(other.table))
return false;
return (this.columnName == null) ? other.columnName == null
: this.columnName.equals(other.columnName);
}
}
public final static int MAX_COLUMN_NAME_LENGTH = 64;
private final AkibanInformationSchema ais;
private final Map<String, ForwardTableReference> forwardReferences = // join name
// ->
// ForwardTableReference
new LinkedHashMap<>();
private final NameGenerator nameGenerator;
private final StorageFormatRegistry storageFormatRegistry;
// Inner classes
private class ForwardTableReference {
public ForwardTableReference(String joinName,
TableName parentTableName, Table childTable) {
this.joinName = joinName;
this.parentTableName = parentTableName;
this.childTable = childTable;
}
public String joinName() {
return joinName;
}
public TableName parentTableName() {
return parentTableName;
}
public Table childTable() {
return childTable;
}
public void addColumnReference(String parentColumnName,
Column childColumn) {
forwardColumnReferences.add(new ForwardColumnReference(
parentColumnName, childColumn));
}
public List<ForwardColumnReference> forwardColumnReferences() {
return forwardColumnReferences;
}
private final String joinName;
private final Table childTable;
private final TableName parentTableName;
private final List<ForwardColumnReference> forwardColumnReferences = new ArrayList<>();
}
private class ForwardColumnReference {
public ForwardColumnReference(String parentColumnName,
Column childColumn) {
this.parentColumnName = parentColumnName;
this.childColumn = childColumn;
}
public String parentColumnName() {
return parentColumnName;
}
public Column childColumn() {
return childColumn;
}
private final String parentColumnName;
private final Column childColumn;
}
public static class NoSuchObjectException extends RuntimeException {
public NoSuchObjectException(String action, String needed, String name) {
super("While " + action + ", could not find " + needed + " " + name);
}
}
public static class GroupStructureException extends RuntimeException {
public GroupStructureException(Group group, Group existingGroup,
String name) {
super(name + " already belongs to group " + existingGroup.getName()
+ " so it cannot be associated with group "
+ group.getName());
}
public GroupStructureException(String message) {
super(message);
}
}
public static class GroupNotEmptyException extends RuntimeException {
public GroupNotEmptyException(Group group) {
super(
"Group "
+ group.getName()
+ " cannot be deleted because it contains at least one user table.");
}
}
public class NotInGroupException extends RuntimeException {
public NotInGroupException(Group group, HasGroup object, String action,
String objectDescription) {
super("While " + action + ", found " + objectDescription
+ " not in " + group + ", but in " + object.getGroup()
+ " instead.");
}
}
}