/* * ToroDB * Copyright © 2014 8Kdata Technology (www.8kdata.com) * * 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.torodb.torod.impl.sql; import com.google.common.base.Preconditions; import com.torodb.core.TableRef; import com.torodb.core.cursors.Cursor; import com.torodb.core.exceptions.user.CollectionNotFoundException; import com.torodb.core.exceptions.user.DatabaseNotFoundException; import com.torodb.core.exceptions.user.UnsupportedCompoundIndexException; import com.torodb.core.exceptions.user.UnsupportedUniqueIndexException; import com.torodb.core.exceptions.user.UserException; import com.torodb.core.language.AttributeReference; import com.torodb.core.transaction.RollbackException; import com.torodb.core.transaction.WriteInternalTransaction; import com.torodb.core.transaction.metainf.FieldIndexOrdering; import com.torodb.core.transaction.metainf.FieldType; import com.torodb.core.transaction.metainf.MetaCollection; import com.torodb.core.transaction.metainf.MetaDatabase; import com.torodb.core.transaction.metainf.MetaDocPart; import com.torodb.core.transaction.metainf.MetaField; import com.torodb.core.transaction.metainf.MetaIndex; import com.torodb.core.transaction.metainf.MutableMetaCollection; import com.torodb.core.transaction.metainf.MutableMetaDatabase; import com.torodb.core.transaction.metainf.MutableMetaIndex; import com.torodb.core.transaction.metainf.MutableMetaSnapshot; import com.torodb.kvdocument.values.KvDocument; import com.torodb.kvdocument.values.KvValue; import com.torodb.torod.IndexFieldInfo; import com.torodb.torod.SharedWriteTorodTransaction; import com.torodb.torod.pipeline.InsertPipeline; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import org.jooq.lambda.Seq; import org.jooq.lambda.tuple.Tuple3; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.stream.Stream; import javax.annotation.Nonnull; /** * */ public abstract class SqlWriteTorodTransaction<T extends WriteInternalTransaction<?>> extends SqlTorodTransaction<T> implements SharedWriteTorodTransaction { private final boolean concurrent; public SqlWriteTorodTransaction(SqlTorodConnection connection, boolean concurrent) { super(connection); this.concurrent = concurrent; } @Override public void insert(String db, String collection, Stream<KvDocument> documents) throws RollbackException, UserException { Preconditions.checkState(!isClosed()); MutableMetaDatabase metaDb = getOrCreateMetaDatabase(db); MutableMetaCollection metaCol = getOrCreateMetaCollection(metaDb, collection); //TODO: here we can not use a pipeline InsertPipeline pipeline = getConnection().getServer() .getInsertPipelineFactory() .createInsertPipeline( getConnection().getServer().getD2RTranslatorFactory(), metaDb, metaCol, getInternalTransaction().getBackendTransaction(), concurrent ); pipeline.insert(documents); } @Override public void delete(String dbName, String colName, Cursor<Integer> cursor) { MetaDatabase db = getInternalTransaction().getMetaSnapshot().getMetaDatabaseByName(dbName); if (db == null) { return; } MetaCollection col = db.getMetaCollectionByName(colName); if (col == null) { return; } getInternalTransaction().getBackendTransaction().deleteDids(db, col, cursor.getRemaining()); } @Override public long deleteAll(String dbName, String colName) { MetaDatabase db = getInternalTransaction().getMetaSnapshot().getMetaDatabaseByName(dbName); if (db == null) { return 0; } MetaCollection col = db.getMetaCollectionByName(colName); if (col == null) { return 0; } Collection<Integer> dids = getInternalTransaction().getBackendTransaction() .findAll(db, col) .asDidCursor() .getRemaining(); getInternalTransaction().getBackendTransaction().deleteDids(db, col, dids); return dids.size(); } @Override public long deleteByAttRef(String dbName, String colName, AttributeReference attRef, KvValue<?> value) { MetaDatabase db = getInternalTransaction().getMetaSnapshot().getMetaDatabaseByName(dbName); if (db == null) { return 0; } MetaCollection col = db.getMetaCollectionByName(colName); if (col == null) { return 0; } TableRef tableRef = extractTableRef(attRef); String lastKey = extractKeyName(attRef.getKeys().get(attRef.getKeys().size() - 1)); MetaDocPart docPart = col.getMetaDocPartByTableRef(tableRef); if (docPart == null) { return 0; } MetaField field = docPart.getMetaFieldByNameAndType(lastKey, FieldType.from(value.getType())); if (field == null) { return 0; } Collection<Integer> dids = getInternalTransaction().getBackendTransaction() .findByField(db, col, docPart, field, value) .asDidCursor() .getRemaining(); getInternalTransaction().getBackendTransaction().deleteDids(db, col, dids); return dids.size(); } @Override public void dropCollection(String db, String collection) throws RollbackException, UserException { MutableMetaDatabase metaDb = getMetaDatabaseOrThrowException(db); MutableMetaCollection metaColl = getMetaCollectionOrThrowException(metaDb, collection); getInternalTransaction().getBackendTransaction().dropCollection(metaDb, metaColl); metaDb.removeMetaCollectionByName(collection); } @Override public void createCollection(String db, String collection) throws RollbackException, UserException { MutableMetaDatabase metaDb = getOrCreateMetaDatabase(db); getOrCreateMetaCollection(metaDb, collection); } @Override public void dropDatabase(String db) throws RollbackException, UserException { MutableMetaDatabase metaDb = getMetaDatabaseOrThrowException(db); getInternalTransaction().getBackendTransaction().dropDatabase(metaDb); getInternalTransaction().getMetaSnapshot().removeMetaDatabaseByName(db); } @Override public boolean createIndex(String dbName, String colName, String indexName, List<IndexFieldInfo> fields, boolean unique) throws UserException { if (fields.size() > 1) { throw new UnsupportedCompoundIndexException(dbName, colName, indexName); } MutableMetaDatabase metaDb = getOrCreateMetaDatabase(dbName); MutableMetaCollection metaColl = getOrCreateMetaCollection(metaDb, colName); List<Tuple3<TableRef, String, FieldIndexOrdering>> indexFieldDefs = new ArrayList<>(fields .size()); for (IndexFieldInfo field : fields) { AttributeReference attRef = field.getAttributeReference(); FieldIndexOrdering ordering = field.isAscending() ? FieldIndexOrdering.ASC : FieldIndexOrdering.DESC; TableRef tableRef = extractTableRef(attRef); String lastKey = extractKeyName(attRef.getKeys().get(attRef.getKeys().size() - 1)); indexFieldDefs.add(new Tuple3<>(tableRef, lastKey, ordering)); } if (unique) { TableRef anyIndexTableRef = indexFieldDefs.stream() .findAny().get().v1(); boolean isUniqueIndexWithMutlipleTableRefs = indexFieldDefs.stream() .anyMatch(t -> !t.v1().equals(anyIndexTableRef)); if (isUniqueIndexWithMutlipleTableRefs) { throw new UnsupportedUniqueIndexException(dbName, colName, indexName); } } boolean indexExists = metaColl.streamContainedMetaIndexes() .anyMatch(index -> index.getName().equals(indexName) || (index.isUnique() == unique && index.size() == indexFieldDefs.size() && Seq.seq(index.iteratorFields()) .allMatch(indexField -> { Tuple3<TableRef, String, FieldIndexOrdering> indexFieldDef = indexFieldDefs.get(indexField.getPosition()); return indexFieldDef != null && indexFieldDef.v1().equals(indexField.getTableRef()) && indexFieldDef.v2().equals(indexField.getName()) && indexFieldDef.v3() == indexField.getOrdering(); }))); if (!indexExists) { MutableMetaIndex metaIndex = metaColl.addMetaIndex(indexName, unique); for (Tuple3<TableRef, String, FieldIndexOrdering> indexFieldDef : indexFieldDefs) { metaIndex.addMetaIndexField(indexFieldDef.v1(), indexFieldDef.v2(), indexFieldDef.v3()); } getInternalTransaction().getBackendTransaction().createIndex(metaDb, metaColl, metaIndex); } return !indexExists; } @SuppressFBWarnings(value = "RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT", justification = "Findbugs thinks MutableMetaCollection#removeMetaIndexByName" + "has no side effect") @Override public boolean dropIndex(String dbName, String colName, String indexName) { MutableMetaDatabase db = getInternalTransaction().getMetaSnapshot() .getMetaDatabaseByName(dbName); if (db == null) { return false; } MutableMetaCollection col = db.getMetaCollectionByName(colName); if (col == null) { return false; } MetaIndex index = col.getMetaIndexByName(indexName); if (index == null) { return false; } col.removeMetaIndexByName(indexName); getInternalTransaction().getBackendTransaction().dropIndex(db, col, index); return true; } @Nonnull protected MutableMetaDatabase getOrCreateMetaDatabase(String dbName) { MutableMetaSnapshot metaSnapshot = getInternalTransaction().getMetaSnapshot(); MutableMetaDatabase metaDb = metaSnapshot.getMetaDatabaseByName(dbName); if (metaDb == null) { metaDb = createMetaDatabase(dbName); } return metaDb; } private MutableMetaDatabase createMetaDatabase(String dbName) { Preconditions.checkState(!isClosed()); MutableMetaSnapshot metaSnapshot = getInternalTransaction().getMetaSnapshot(); MutableMetaDatabase metaDb = metaSnapshot.addMetaDatabase( dbName, getConnection().getServer().getIdentifierFactory().toDatabaseIdentifier( metaSnapshot, dbName) ); getInternalTransaction().getBackendTransaction().addDatabase(metaDb); return metaDb; } protected MutableMetaCollection getOrCreateMetaCollection(@Nonnull MutableMetaDatabase metaDb, String colName) { MutableMetaCollection metaCol = metaDb.getMetaCollectionByName(colName); if (metaCol == null) { metaCol = createMetaCollection(metaDb, colName); } return metaCol; } protected MutableMetaCollection createMetaCollection(MutableMetaDatabase metaDb, String colName) { MutableMetaCollection metaCol; Preconditions.checkState(!isClosed()); metaCol = metaDb.addMetaCollection( colName, getConnection().getServer().getIdentifierFactory().toCollectionIdentifier( getInternalTransaction().getMetaSnapshot(), metaDb.getName(), colName) ); getInternalTransaction().getBackendTransaction().addCollection(metaDb, metaCol); return metaCol; } @Nonnull protected MutableMetaDatabase getMetaDatabaseOrThrowException(@Nonnull String dbName) throws DatabaseNotFoundException { MutableMetaSnapshot metaSnapshot = getInternalTransaction().getMetaSnapshot(); MutableMetaDatabase metaDb = metaSnapshot.getMetaDatabaseByName(dbName); if (metaDb == null) { throw new DatabaseNotFoundException(dbName); } return metaDb; } @Nonnull protected MutableMetaCollection getMetaCollectionOrThrowException( @Nonnull MutableMetaDatabase metaDb, @Nonnull String colName) throws CollectionNotFoundException { MutableMetaCollection metaCol = metaDb.getMetaCollectionByName(colName); if (metaCol == null) { throw new CollectionNotFoundException(metaDb.getName(), colName); } return metaCol; } @Override public void commit() throws RollbackException, UserException { getInternalTransaction().commit(); } }