/*
* Licensed to CRATE Technology GmbH ("Crate") under one or more contributor
* license agreements. See the NOTICE file distributed with this work for
* additional information regarding copyright ownership. Crate licenses
* this file to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may
* obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* However, if you have executed another commercial license agreement
* with Crate these terms will supersede the license and you may use the
* software solely pursuant to the terms of the relevant commercial agreement.
*/
package io.crate.action.sql;
import io.crate.action.FutureActionListener;
import io.crate.analyze.*;
import io.crate.blob.v2.BlobAdminClient;
import io.crate.data.Row;
import io.crate.executor.transport.AlterTableOperation;
import io.crate.executor.transport.RepositoryService;
import io.crate.executor.transport.SnapshotRestoreDDLDispatcher;
import io.crate.executor.transport.TableCreator;
import io.crate.operation.udf.UserDefinedFunctionDDLClient;
import org.elasticsearch.action.admin.indices.forcemerge.ForceMergeRequest;
import org.elasticsearch.action.admin.indices.forcemerge.ForceMergeResponse;
import org.elasticsearch.action.admin.indices.forcemerge.TransportForceMergeAction;
import org.elasticsearch.action.admin.indices.refresh.RefreshRequest;
import org.elasticsearch.action.admin.indices.refresh.RefreshResponse;
import org.elasticsearch.action.admin.indices.refresh.TransportRefreshAction;
import org.elasticsearch.action.admin.indices.upgrade.post.TransportUpgradeAction;
import org.elasticsearch.action.admin.indices.upgrade.post.UpgradeRequest;
import org.elasticsearch.action.admin.indices.upgrade.post.UpgradeResponse;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.inject.Provider;
import org.elasticsearch.common.inject.Singleton;
import java.util.Locale;
import java.util.concurrent.CompletableFuture;
/**
* visitor that dispatches requests based on Analysis class to different actions.
* <p>
* Its methods return a future returning a Long containing the response rowCount.
* If the future returns <code>null</code>, no row count shall be created.
*/
@Singleton
public class DDLStatementDispatcher {
private final Provider<BlobAdminClient> blobAdminClient;
private final TableCreator tableCreator;
private final AlterTableOperation alterTableOperation;
private final RepositoryService repositoryService;
private final SnapshotRestoreDDLDispatcher snapshotRestoreDDLDispatcher;
private final UserDefinedFunctionDDLClient udfDDLClient;
private final Provider<TransportUpgradeAction> transportUpgradeActionProvider;
private final Provider<TransportForceMergeAction> transportForceMergeActionProvider;
private final Provider<TransportRefreshAction> transportRefreshActionProvider;
private final InnerVisitor innerVisitor = new InnerVisitor();
@Inject
public DDLStatementDispatcher(Provider<BlobAdminClient> blobAdminClient,
TableCreator tableCreator,
AlterTableOperation alterTableOperation,
RepositoryService repositoryService,
SnapshotRestoreDDLDispatcher snapshotRestoreDDLDispatcher,
UserDefinedFunctionDDLClient udfDDLClient,
Provider<TransportUpgradeAction> transportUpgradeActionProvider,
Provider<TransportForceMergeAction> transportForceMergeActionProvider,
Provider<TransportRefreshAction> transportRefreshActionProvider) {
this.blobAdminClient = blobAdminClient;
this.tableCreator = tableCreator;
this.alterTableOperation = alterTableOperation;
this.repositoryService = repositoryService;
this.snapshotRestoreDDLDispatcher = snapshotRestoreDDLDispatcher;
this.udfDDLClient = udfDDLClient;
this.transportUpgradeActionProvider = transportUpgradeActionProvider;
this.transportForceMergeActionProvider = transportForceMergeActionProvider;
this.transportRefreshActionProvider = transportRefreshActionProvider;
}
public CompletableFuture<Long> dispatch(AnalyzedStatement analyzedStatement, Row parameters) {
return innerVisitor.process(analyzedStatement, parameters);
}
private class InnerVisitor extends AnalyzedStatementVisitor<Row, CompletableFuture<Long>> {
@Override
protected CompletableFuture<Long> visitAnalyzedStatement(AnalyzedStatement analyzedStatement, Row parameters) {
throw new UnsupportedOperationException(String.format(Locale.ENGLISH, "Can't handle \"%s\"", analyzedStatement));
}
@Override
public CompletableFuture<Long> visitCreateTableStatement(CreateTableAnalyzedStatement analysis, Row parameters) {
return tableCreator.create(analysis);
}
@Override
public CompletableFuture<Long> visitAlterTableStatement(final AlterTableAnalyzedStatement analysis, Row parameters) {
return alterTableOperation.executeAlterTable(analysis);
}
@Override
public CompletableFuture<Long> visitAddColumnStatement(AddColumnAnalyzedStatement analysis, Row parameters) {
return alterTableOperation.executeAlterTableAddColumn(analysis);
}
@Override
public CompletableFuture<Long> visitOptimizeTableStatement(OptimizeTableAnalyzedStatement analysis, Row parameters) {
if (analysis.settings().getAsBoolean(OptimizeSettings.UPGRADE_SEGMENTS.name(),
OptimizeSettings.UPGRADE_SEGMENTS.defaultValue())) {
return executeUpgradeSegments(analysis, transportUpgradeActionProvider.get());
} else {
return executeMergeSegments(analysis, transportForceMergeActionProvider.get());
}
}
@Override
public CompletableFuture<Long> visitRefreshTableStatement(RefreshTableAnalyzedStatement analysis, Row parameters) {
if (analysis.indexNames().isEmpty()) {
return CompletableFuture.completedFuture(null);
}
RefreshRequest request = new RefreshRequest(analysis.indexNames().toArray(
new String[analysis.indexNames().size()]));
request.indicesOptions(IndicesOptions.lenientExpandOpen());
FutureActionListener<RefreshResponse, Long> listener =
new FutureActionListener<>(r -> (long) analysis.indexNames().size());
transportRefreshActionProvider.get().execute(request, listener);
return listener;
}
@Override
public CompletableFuture<Long> visitCreateBlobTableStatement(CreateBlobTableAnalyzedStatement analysis,
Row parameters) {
return blobAdminClient.get().createBlobTable(analysis.tableName(), analysis.tableParameter().settings());
}
@Override
public CompletableFuture<Long> visitAlterBlobTableStatement(AlterBlobTableAnalyzedStatement analysis, Row parameters) {
return blobAdminClient.get().alterBlobTable(analysis.table().ident().name(), analysis.tableParameter().settings());
}
@Override
public CompletableFuture<Long> visitDropBlobTableStatement(DropBlobTableAnalyzedStatement analysis, Row parameters) {
return blobAdminClient.get().dropBlobTable(analysis.table().ident().name());
}
@Override
public CompletableFuture<Long> visitDropRepositoryAnalyzedStatement(DropRepositoryAnalyzedStatement analysis,
Row parameters) {
return repositoryService.execute(analysis);
}
@Override
public CompletableFuture<Long> visitCreateRepositoryAnalyzedStatement(CreateRepositoryAnalyzedStatement analysis,
Row parameters) {
return repositoryService.execute(analysis);
}
@Override
public CompletableFuture<Long> visitDropSnapshotAnalyzedStatement(DropSnapshotAnalyzedStatement analysis,
Row parameters) {
return snapshotRestoreDDLDispatcher.dispatch(analysis);
}
public CompletableFuture<Long> visitCreateSnapshotAnalyzedStatement(CreateSnapshotAnalyzedStatement analysis,
Row parameters) {
return snapshotRestoreDDLDispatcher.dispatch(analysis);
}
@Override
public CompletableFuture<Long> visitRestoreSnapshotAnalyzedStatement(RestoreSnapshotAnalyzedStatement analysis,
Row parameters) {
return snapshotRestoreDDLDispatcher.dispatch(analysis);
}
@Override
protected CompletableFuture<Long> visitCreateFunctionStatement(CreateFunctionAnalyzedStatement analysis,
Row parameters) {
return udfDDLClient.execute(analysis, parameters);
}
@Override
public CompletableFuture<Long> visitDropFunctionStatement(DropFunctionAnalyzedStatement analysis, Row parameters) {
return udfDDLClient.execute(analysis);
}
}
private static CompletableFuture<Long> executeMergeSegments(OptimizeTableAnalyzedStatement analysis,
TransportForceMergeAction transportForceMergeAction) {
ForceMergeRequest request = new ForceMergeRequest(analysis.indexNames().toArray(new String[0]));
// Pass parameters to ES request
request.maxNumSegments(analysis.settings().getAsInt(OptimizeSettings.MAX_NUM_SEGMENTS.name(),
ForceMergeRequest.Defaults.MAX_NUM_SEGMENTS));
request.onlyExpungeDeletes(analysis.settings().getAsBoolean(OptimizeSettings.ONLY_EXPUNGE_DELETES.name(),
ForceMergeRequest.Defaults.ONLY_EXPUNGE_DELETES));
request.flush(analysis.settings().getAsBoolean(OptimizeSettings.FLUSH.name(),
ForceMergeRequest.Defaults.FLUSH));
request.indicesOptions(IndicesOptions.lenientExpandOpen());
FutureActionListener<ForceMergeResponse, Long> listener =
new FutureActionListener<>(r -> (long) analysis.indexNames().size());
transportForceMergeAction.execute(request, listener);
return listener;
}
private static CompletableFuture<Long> executeUpgradeSegments(OptimizeTableAnalyzedStatement analysis,
TransportUpgradeAction transportUpgradeAction) {
UpgradeRequest request = new UpgradeRequest(analysis.indexNames().toArray(new String[0]));
FutureActionListener<UpgradeResponse, Long> listener =
new FutureActionListener<>(r -> (long) analysis.indexNames().size());
transportUpgradeAction.execute(request, listener);
return listener;
}
}