/**
* 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.store;
import com.foundationdb.Transaction;
import com.foundationdb.ais.model.AkibanInformationSchema;
import com.foundationdb.ais.model.FullTextIndex;
import com.foundationdb.ais.model.Index;
import com.foundationdb.ais.model.IndexName;
import com.foundationdb.ais.model.JoinColumn;
import com.foundationdb.ais.model.NameGenerator;
import com.foundationdb.ais.model.Sequence;
import com.foundationdb.ais.model.TableName;
import com.foundationdb.directory.DirectorySubspace;
import com.foundationdb.qp.storeadapter.FDBAdapter;
import com.foundationdb.server.store.FDBTransactionService.TransactionState;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
/**
* FDB-aware NameGenerator that generates tree names based off of a
* {@link DirectorySubspace}.
*
* <p>
* NameGenerator has distinct methods that generate trees for groups,
* indexes and sequences. Each of these have their own, distinct scopes
* of enforced uniqueness (schema, table and schema, respectively).
* </p>
*
* <p>
* The combination of these will produce a set of directories under
* {@link #DATA_PATH_NAME} for COI created in the test schema like:
*
* <pre>
* root_dir/
* data/
* sequence/
* test/
* customers/
* _c_pk_seq/
* orders/
* _o_pk_seq/
* items/
* _i_pk_seq/
* table/
* test/
* customers/
* PRIMARY/
* idx_name/
* g_idx_odate_cname/
* orders/
* PRIMARY
* idx_date/
* items/
* PRIMARY/
* idx_sku/
* </pre>
* </p>
*
* <p>
* The above example shows a directory layout after a schema has been
* created. Trees names are also required to be generated, and distinct,
* during an online change. This class provides another second level
* sub-directory, {@link #ONLINE_PATH_NAME}, which is used for that purpose.
* </p>
*
*/
public class FDBNameGenerator implements NameGenerator
{
static final String DATA_PATH_NAME = "data";
static final String ONLINE_PATH_NAME = "dataOnline";
static final String TABLE_PATH_NAME = "table";
static final String SEQUENCE_PATH_NAME = "sequence";
static final String ONLINE_LOB_PATH_NAME = "lobOnline";
private final TransactionState txn;
private final DirectorySubspace directory;
private final String pathPrefix;
private final NameGenerator wrapped;
private FDBNameGenerator(TransactionState txn, DirectorySubspace dir, String pathPrefix, NameGenerator wrapped) {
this.txn = txn;
this.directory = dir;
this.pathPrefix = pathPrefix;
this.wrapped = wrapped;
}
public static FDBNameGenerator createForDataPath(TransactionState txn, DirectorySubspace dir, NameGenerator wrapped) {
return new FDBNameGenerator(txn, dir, DATA_PATH_NAME, wrapped);
}
public static FDBNameGenerator createForOnlinePath(TransactionState txn, DirectorySubspace dir, NameGenerator wrapped) {
return new FDBNameGenerator(txn, dir, ONLINE_PATH_NAME, wrapped);
}
//
// DATA_PATH_NAME helpers
//
public static List<String> dataPath(TableName tableName) {
return dataPath(tableName.getSchemaName(), tableName.getTableName());
}
public static List<String> dataPathSchemaTable(String schemaName) {
return dataPathSchema(schemaName, TABLE_PATH_NAME);
}
public static List<String> dataPathSchemaSequence(String schemaName) {
return dataPathSchema(schemaName, SEQUENCE_PATH_NAME);
}
public static List<String> dataPath(String schemaName, String tableName) {
return makeTablePath(DATA_PATH_NAME, schemaName, tableName);
}
public static List<String> dataPath(Index index) {
return makeIndexPath(DATA_PATH_NAME, index);
}
public static List<String> dataPath(Sequence sequence) {
TableName seqName = sequence.getSequenceName();
return dataPathSequence(seqName.getSchemaName(), seqName.getTableName());
}
public static List<String> dataPathSequence(String schemaName, String sequenceName) {
return makeSequencePath(DATA_PATH_NAME, schemaName, sequenceName);
}
public static List<String> dataPathSchema(String schemaName, String typeName) {
return Arrays.asList(DATA_PATH_NAME, typeName, schemaName);
}
//
// ONLINE_PATH_NAME helpers
//
public static List<String> onlinePath(TableName tableName) {
return onlinePath(tableName.getSchemaName(), tableName.getTableName());
}
public static List<String> onlinePath(String schemaName, String tableName) {
return makeTablePath(ONLINE_PATH_NAME, schemaName, tableName);
}
public static List<String> onlinePath(Index index) {
return makeIndexPath(ONLINE_PATH_NAME, index);
}
public static List<String> onlinePath(Sequence sequence) {
TableName seqName = sequence.getSequenceName();
return onlinePathSequence(seqName.getSchemaName(), seqName.getTableName());
}
public static List<String> onlinePathSequence(String schemaName, String sequenceName) {
return makeSequencePath(ONLINE_PATH_NAME, schemaName, sequenceName);
}
public static List<String> onlineLobPath( String schemaName, String tableName) {
return makeLobPath(schemaName, tableName);
}
//
// Directory based generation
//
public synchronized byte[] generateIndexPrefixBytes(Index index) {
return generate(makeIndexPath(pathPrefix, index));
}
public synchronized byte[] generateGroupPrefixBytes(String schemaName, String groupName) {
return generate(makeTablePath(pathPrefix, schemaName, groupName));
}
public synchronized byte[] generateSequencePrefixBytes(Sequence sequence) {
TableName seqName = sequence.getSequenceName();
return generate(makeSequencePath(pathPrefix, seqName.getSchemaName(), seqName.getTableName()));
}
//
// Trivially wrapped
//
@Override
public int generateTableID(TableName name) {
return wrapped.generateTableID(name);
}
@Override
public int generateIndexID(int rootTableID) {
return wrapped.generateIndexID(rootTableID);
}
@Override
public TableName generateIdentitySequenceName(AkibanInformationSchema ais, TableName table, String column) {
return wrapped.generateIdentitySequenceName(ais, table, column);
}
@Override
public String generateJoinName(TableName parentTable, TableName childTable, String[] pkColNames, String[] fkColNames) {
return wrapped.generateJoinName(parentTable, childTable, pkColNames, fkColNames);
}
@Override
public String generateJoinName(TableName parentTable, TableName childTable, List<JoinColumn> joinIndex) {
return wrapped.generateJoinName(parentTable, childTable, joinIndex);
}
@Override
public String generateJoinName(TableName parentTable,
TableName childTable,
List<String> pkColNames,
List<String> fkColNames) {
return wrapped.generateJoinName(parentTable, childTable, pkColNames, fkColNames);
}
@Override
public String generateFullTextIndexPath(FullTextIndex index) {
return wrapped.generateFullTextIndexPath(index);
}
@Override
public void mergeAIS(AkibanInformationSchema ais) {
wrapped.mergeAIS(ais);
}
@Override
public void removeTableID(int tableID) {
wrapped.removeTableID(tableID);
}
@Override
public Set<String> getStorageNames() {
return wrapped.getStorageNames();
}
@Override
public TableName generateFKConstraintName(String schemaName, String tableName) {
return wrapped.generateFKConstraintName(schemaName, tableName);
}
@Override
public TableName generatePKConstraintName(String schemaName, String tableName) {
return wrapped.generatePKConstraintName(schemaName, tableName);
}
@Override
public TableName generateUniqueConstraintName(String schemaName, String tableName) {
return wrapped.generateUniqueConstraintName(schemaName, tableName);
}
//
// Internal
//
private byte[] generate(List<String> path) {
// Directory should always hand out unique prefixes.
// So use createOrOpen() and do not pass to wrapped for unique check as AISValidation confirms
try {
Transaction txn = this.txn.getTransaction();
DirectorySubspace indexDir = directory.createOrOpen(txn, path).get();
return indexDir.pack();
} catch (RuntimeException e) {
throw FDBAdapter.wrapFDBException(this.txn.session, e);
}
}
//
// Helpers
//
public static List<String> makeTablePath(String pathPrefix, String schemaName, String tableName) {
return Arrays.asList(pathPrefix, TABLE_PATH_NAME, schemaName, tableName);
}
public static List<String> makeIndexPath(String pathPrefix, Index index) {
IndexName name = index.getIndexName();
return Arrays.asList(pathPrefix, TABLE_PATH_NAME, name.getSchemaName(), name.getTableName(), name.getName());
}
public static List<String> makeSequencePath(String pathPrefix, String schemaName, String sequenceName) {
return Arrays.asList(pathPrefix, SEQUENCE_PATH_NAME, schemaName, sequenceName);
}
private static List<String> makeLobPath( String schemaName, String tableName) {
return Arrays.asList(ONLINE_LOB_PATH_NAME, schemaName, tableName);
}
}