Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.hazelcast.jet.sql.impl.SqlPlanImpl Maven / Gradle / Ivy
/*
* Copyright 2024 Hazelcast Inc.
*
* Licensed under the Hazelcast Community License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://hazelcast.com/hazelcast-community-license
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.hazelcast.jet.sql.impl;
import com.hazelcast.config.IndexType;
import com.hazelcast.jet.config.DeltaJobConfig;
import com.hazelcast.jet.config.JobConfig;
import com.hazelcast.jet.config.ProcessingGuarantee;
import com.hazelcast.jet.core.DAG;
import com.hazelcast.jet.core.Vertex;
import com.hazelcast.jet.sql.impl.connector.keyvalue.KvRowProjector;
import com.hazelcast.jet.sql.impl.connector.map.UpdatingEntryProcessor;
import com.hazelcast.jet.sql.impl.opt.physical.PhysicalRel;
import com.hazelcast.jet.sql.impl.parse.SqlAlterJob.AlterJobOperation;
import com.hazelcast.jet.sql.impl.parse.SqlShowStatement.ShowStatementTarget;
import com.hazelcast.jet.sql.impl.schema.TypeDefinitionColumn;
import com.hazelcast.security.permission.MapPermission;
import com.hazelcast.security.permission.SqlPermission;
import com.hazelcast.sql.SqlResult;
import com.hazelcast.sql.SqlRowMetadata;
import com.hazelcast.sql.impl.QueryException;
import com.hazelcast.sql.impl.QueryId;
import com.hazelcast.sql.impl.QueryParameterMetadata;
import com.hazelcast.sql.impl.expression.Expression;
import com.hazelcast.sql.impl.expression.ExpressionEvalContext;
import com.hazelcast.sql.impl.expression.ParameterExpression;
import com.hazelcast.sql.impl.optimizer.PlanCheckContext;
import com.hazelcast.sql.impl.optimizer.PlanKey;
import com.hazelcast.sql.impl.optimizer.PlanObjectKey;
import com.hazelcast.sql.impl.optimizer.SqlPlan;
import com.hazelcast.sql.impl.schema.Mapping;
import com.hazelcast.sql.impl.security.SqlSecurityContext;
import com.hazelcast.shaded.org.apache.calcite.rel.core.TableModify;
import com.hazelcast.shaded.org.apache.calcite.rel.core.TableModify.Operation;
import javax.annotation.Nonnull;
import java.security.Permission;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Function;
import static com.hazelcast.security.permission.ActionConstants.ACTION_CREATE;
import static com.hazelcast.security.permission.ActionConstants.ACTION_CREATE_DATACONNECTION;
import static com.hazelcast.security.permission.ActionConstants.ACTION_CREATE_TYPE;
import static com.hazelcast.security.permission.ActionConstants.ACTION_CREATE_VIEW;
import static com.hazelcast.security.permission.ActionConstants.ACTION_DESTROY;
import static com.hazelcast.security.permission.ActionConstants.ACTION_DROP_DATACONNECTION;
import static com.hazelcast.security.permission.ActionConstants.ACTION_DROP_TYPE;
import static com.hazelcast.security.permission.ActionConstants.ACTION_DROP_VIEW;
import static com.hazelcast.security.permission.ActionConstants.ACTION_INDEX;
import static com.hazelcast.security.permission.ActionConstants.ACTION_PUT;
import static com.hazelcast.security.permission.ActionConstants.ACTION_READ;
import static com.hazelcast.security.permission.ActionConstants.ACTION_REMOVE;
import static com.hazelcast.security.permission.ActionConstants.ACTION_VIEW_DATACONNECTION;
abstract class SqlPlanImpl extends SqlPlan {
protected SqlPlanImpl(PlanKey planKey) {
super(planKey);
}
public boolean isPlanValid(PlanCheckContext context) {
throw new UnsupportedOperationException(isCacheable()
? "override this method"
: "method should not be called for non-cacheable plans");
}
protected void checkPermissions(SqlSecurityContext context, DAG dag) {
if (!context.isSecurityEnabled()) {
return;
}
for (Vertex vertex : dag) {
Permission permission = vertex.getMetaSupplier().getRequiredPermission();
if (permission != null) {
context.checkPermission(permission);
}
}
}
@Override
public void checkPermissions(SqlSecurityContext context) {
}
static class CreateMappingPlan extends SqlPlanImpl {
private final Mapping mapping;
private final boolean replace;
private final boolean ifNotExists;
private final PlanExecutor planExecutor;
CreateMappingPlan(
PlanKey planKey,
Mapping mapping,
boolean replace,
boolean ifNotExists,
PlanExecutor planExecutor
) {
super(planKey);
this.mapping = mapping;
this.replace = replace;
this.ifNotExists = ifNotExists;
this.planExecutor = planExecutor;
}
Mapping mapping() {
return mapping;
}
boolean replace() {
return replace;
}
boolean ifNotExists() {
return ifNotExists;
}
@Override
public boolean isCacheable() {
return false;
}
@Override
public void checkPermissions(SqlSecurityContext context) {
if (mapping.dataConnection() != null) {
context.checkPermission(new SqlPermission(mapping.dataConnection(), ACTION_VIEW_DATACONNECTION));
}
context.checkPermission(new SqlPermission(mapping.name(), ACTION_CREATE));
}
@Override
public boolean producesRows() {
return false;
}
@Override
public SqlResult execute(QueryId queryId, List arguments, long timeout, SqlSecurityContext ssc) {
SqlPlanImpl.ensureNoArguments("CREATE MAPPING", arguments);
SqlPlanImpl.ensureNoTimeout("CREATE MAPPING", timeout);
return planExecutor.execute(this, ssc);
}
}
static class DropMappingPlan extends SqlPlanImpl {
private final String name;
private final boolean ifExists;
private final PlanExecutor planExecutor;
DropMappingPlan(
PlanKey planKey,
String name,
boolean ifExists,
PlanExecutor planExecutor
) {
super(planKey);
this.name = name;
this.ifExists = ifExists;
this.planExecutor = planExecutor;
}
String name() {
return name;
}
boolean ifExists() {
return ifExists;
}
@Override
public boolean isCacheable() {
return false;
}
@Override
public void checkPermissions(SqlSecurityContext context) {
context.checkPermission(new SqlPermission(name, ACTION_DESTROY));
}
@Override
public boolean producesRows() {
return false;
}
@Override
public SqlResult execute(QueryId queryId, List arguments, long timeout, SqlSecurityContext ssc) {
SqlPlanImpl.ensureNoArguments("DROP MAPPING", arguments);
SqlPlanImpl.ensureNoTimeout("DROP MAPPING", timeout);
return planExecutor.execute(this);
}
}
static class CreateDataConnectionPlan extends SqlPlanImpl {
private final boolean replace;
private final boolean ifNotExists;
private final String name;
private final String type;
private final boolean shared;
private final Map options;
private final PlanExecutor planExecutor;
CreateDataConnectionPlan(
PlanKey planKey,
boolean replace,
boolean ifNotExists,
String name,
String type,
boolean shared,
Map options,
PlanExecutor planExecutor
) {
super(planKey);
this.ifNotExists = ifNotExists;
this.replace = replace;
this.name = name;
this.type = type;
this.shared = shared;
this.options = options;
this.planExecutor = planExecutor;
}
boolean ifNotExists() {
return ifNotExists;
}
public boolean isReplace() {
return replace;
}
public String name() {
return name;
}
public String type() {
return type;
}
public boolean shared() {
return shared;
}
public Map options() {
return options;
}
@Override
public boolean isCacheable() {
return false;
}
@Override
public boolean producesRows() {
return false;
}
@Override
public void checkPermissions(SqlSecurityContext context) {
if (isReplace()) {
context.checkPermission(new SqlPermission(name, ACTION_CREATE_DATACONNECTION, ACTION_DROP_DATACONNECTION));
} else {
context.checkPermission(new SqlPermission(name, ACTION_CREATE_DATACONNECTION));
}
}
@Override
public SqlResult execute(QueryId queryId, List arguments, long timeout, SqlSecurityContext ssc) {
SqlPlanImpl.ensureNoArguments("CREATE DATA CONNECTION", arguments);
SqlPlanImpl.ensureNoTimeout("CREATE DATA CONNECTION", timeout);
return planExecutor.execute(this);
}
}
static class DropDataConnectionPlan extends SqlPlanImpl {
private final String name;
private final boolean ifExists;
private final PlanExecutor planExecutor;
DropDataConnectionPlan(
PlanKey planKey,
String name,
boolean ifExists,
PlanExecutor planExecutor
) {
super(planKey);
this.name = name;
this.ifExists = ifExists;
this.planExecutor = planExecutor;
}
String name() {
return name;
}
boolean ifExists() {
return ifExists;
}
@Override
public boolean isCacheable() {
return false;
}
@Override
public void checkPermissions(SqlSecurityContext context) {
context.checkPermission(new SqlPermission(name, ACTION_VIEW_DATACONNECTION, ACTION_DROP_DATACONNECTION));
}
@Override
public boolean producesRows() {
return false;
}
@Override
public SqlResult execute(QueryId queryId, List arguments, long timeout, SqlSecurityContext ssc) {
SqlPlanImpl.ensureNoTimeout("DROP DATA CONNECTION", timeout);
return planExecutor.execute(this);
}
}
static class CreateIndexPlan extends SqlPlanImpl {
private final String name;
private final String mapName;
private final String[] attributes;
private final Map options;
private final IndexType indexType;
private final boolean ifNotExists;
private final PlanExecutor planExecutor;
CreateIndexPlan(
PlanKey planKey,
String name,
String mapName,
IndexType indexType,
List attributes,
Map options,
boolean ifNotExists,
PlanExecutor planExecutor
) {
super(planKey);
this.name = name;
this.mapName = mapName;
this.indexType = indexType;
this.attributes = attributes.toArray(new String[0]);
this.options = options;
this.ifNotExists = ifNotExists;
this.planExecutor = planExecutor;
}
public String indexName() {
return name;
}
public String mapName() {
return mapName;
}
public String[] attributes() {
return attributes;
}
public IndexType indexType() {
return indexType;
}
public Map options() {
return options;
}
boolean ifNotExists() {
return ifNotExists;
}
@Override
public boolean isCacheable() {
return false;
}
@Override
public void checkPermissions(SqlSecurityContext context) {
context.checkPermission(new SqlPermission(name, ACTION_INDEX));
}
@Override
public boolean producesRows() {
return false;
}
@Override
public SqlResult execute(QueryId queryId, List arguments, long timeout, SqlSecurityContext ssc) {
SqlPlanImpl.ensureNoArguments("CREATE INDEX", arguments);
SqlPlanImpl.ensureNoTimeout("CREATE INDEX", timeout);
return planExecutor.execute(this);
}
}
static class DropIndexPlan extends SqlPlanImpl {
private final String name;
private final boolean ifExists;
DropIndexPlan(
PlanKey planKey,
String name,
boolean ifExists
) {
super(planKey);
this.name = name;
this.ifExists = ifExists;
}
String name() {
return name;
}
boolean ifExists() {
return ifExists;
}
@Override
public boolean isCacheable() {
return false;
}
@Override
public void checkPermissions(SqlSecurityContext context) {
context.checkPermission(new SqlPermission(name, ACTION_DESTROY));
}
@Override
public boolean producesRows() {
return false;
}
@Override
public SqlResult execute(QueryId queryId, List arguments, long timeout, SqlSecurityContext ssc) {
throw QueryException.error("DROP INDEX is not supported.");
}
}
static class CreateJobPlan extends SqlPlanImpl {
private final JobConfig jobConfig;
private final boolean ifNotExists;
private final DmlPlan dmlPlan;
private final String query;
private final boolean infiniteRows;
private final PlanExecutor planExecutor;
CreateJobPlan(
PlanKey planKey,
JobConfig jobConfig,
boolean ifNotExists,
DmlPlan dmlPlan,
String query,
boolean infiniteRows,
PlanExecutor planExecutor
) {
super(planKey);
assert dmlPlan.operation == Operation.INSERT : dmlPlan.operation;
this.jobConfig = jobConfig;
this.ifNotExists = ifNotExists;
this.dmlPlan = dmlPlan;
this.query = query;
this.infiniteRows = infiniteRows;
this.planExecutor = planExecutor;
}
public boolean isInfiniteRows() {
return infiniteRows;
}
public String getQuery() {
return query;
}
JobConfig getJobConfig() {
return jobConfig;
}
boolean isIfNotExists() {
return ifNotExists;
}
DmlPlan getExecutionPlan() {
return dmlPlan;
}
QueryParameterMetadata getParameterMetadata() {
return dmlPlan.getParameterMetadata();
}
@Override
public boolean isCacheable() {
return dmlPlan.isCacheable();
}
@Override
public boolean isPlanValid(PlanCheckContext context) {
return dmlPlan.isPlanValid(context);
}
@Override
public void checkPermissions(SqlSecurityContext context) {
dmlPlan.checkPermissions(context);
}
@Override
public boolean producesRows() {
return false;
}
@Override
public SqlResult execute(QueryId queryId, List arguments, long timeout, SqlSecurityContext ssc) {
SqlPlanImpl.ensureNoTimeout("CREATE JOB", timeout);
if (!infiniteRows) {
SqlPlanImpl.ensureNoneGuaranteesForBatchJob(jobConfig);
}
return planExecutor.execute(this, arguments, ssc);
}
}
static class AlterJobPlan extends SqlPlanImpl {
private final String jobName;
private final DeltaJobConfig deltaConfig;
private final AlterJobOperation operation;
private final PlanExecutor planExecutor;
AlterJobPlan(
PlanKey planKey,
String jobName,
DeltaJobConfig deltaConfig,
AlterJobOperation operation,
PlanExecutor planExecutor
) {
super(planKey);
this.jobName = jobName;
this.deltaConfig = deltaConfig;
this.operation = operation;
this.planExecutor = planExecutor;
}
String getJobName() {
return jobName;
}
DeltaJobConfig getDeltaConfig() {
return deltaConfig;
}
AlterJobOperation getOperation() {
return operation;
}
@Override
public boolean isCacheable() {
return false;
}
@Override
public boolean producesRows() {
return false;
}
@Override
public SqlResult execute(QueryId queryId, List arguments, long timeout, SqlSecurityContext ssc) {
SqlPlanImpl.ensureNoArguments("ALTER JOB", arguments);
SqlPlanImpl.ensureNoTimeout("ALTER JOB", timeout);
return planExecutor.execute(this);
}
}
static class DropJobPlan extends SqlPlanImpl {
private final String jobName;
private final boolean ifExists;
private final String withSnapshotName;
private final PlanExecutor planExecutor;
DropJobPlan(
PlanKey planKey,
String jobName,
boolean ifExists,
String withSnapshotName,
PlanExecutor planExecutor
) {
super(planKey);
this.jobName = jobName;
this.ifExists = ifExists;
this.withSnapshotName = withSnapshotName;
this.planExecutor = planExecutor;
}
String getJobName() {
return jobName;
}
boolean isIfExists() {
return ifExists;
}
String getWithSnapshotName() {
return withSnapshotName;
}
@Override
public boolean isCacheable() {
return false;
}
@Override
public boolean producesRows() {
return false;
}
@Override
public SqlResult execute(QueryId queryId, List arguments, long timeout, SqlSecurityContext ssc) {
SqlPlanImpl.ensureNoArguments("DROP JOB", arguments);
SqlPlanImpl.ensureNoTimeout("DROP JOB", timeout);
return planExecutor.execute(this);
}
}
static class CreateSnapshotPlan extends SqlPlanImpl {
private final String snapshotName;
private final String jobName;
private final PlanExecutor planExecutor;
CreateSnapshotPlan(
PlanKey planKey,
String snapshotName,
String jobName,
PlanExecutor planExecutor
) {
super(planKey);
this.snapshotName = snapshotName;
this.jobName = jobName;
this.planExecutor = planExecutor;
}
String getSnapshotName() {
return snapshotName;
}
String getJobName() {
return jobName;
}
@Override
public boolean isCacheable() {
return false;
}
@Override
public boolean producesRows() {
return false;
}
@Override
public SqlResult execute(QueryId queryId, List arguments, long timeout, SqlSecurityContext ssc) {
SqlPlanImpl.ensureNoArguments("CREATE SNAPSHOT", arguments);
SqlPlanImpl.ensureNoTimeout("CREATE SNAPSHOT", timeout);
return planExecutor.execute(this);
}
}
static class DropSnapshotPlan extends SqlPlanImpl {
private final String snapshotName;
private final boolean ifExists;
private final PlanExecutor planExecutor;
DropSnapshotPlan(
PlanKey planKey,
String snapshotName,
boolean ifExists,
PlanExecutor planExecutor
) {
super(planKey);
this.snapshotName = snapshotName;
this.ifExists = ifExists;
this.planExecutor = planExecutor;
}
String getSnapshotName() {
return snapshotName;
}
boolean isIfExists() {
return ifExists;
}
@Override
public boolean isCacheable() {
return false;
}
@Override
public boolean producesRows() {
return false;
}
@Override
public SqlResult execute(QueryId queryId, List arguments, long timeout, SqlSecurityContext ssc) {
SqlPlanImpl.ensureNoArguments("DROP SNAPSHOT", arguments);
SqlPlanImpl.ensureNoTimeout("DROP SNAPSHOT", timeout);
return planExecutor.execute(this);
}
}
static class CreateViewPlan extends SqlPlanImpl {
private final OptimizerContext context;
private final String viewName;
private final String viewQuery;
private final boolean replace;
private final boolean ifNotExists;
private final PlanExecutor planExecutor;
CreateViewPlan(
PlanKey planKey,
final OptimizerContext context,
String viewName,
String viewQuery,
boolean replace,
boolean ifNotExists,
PlanExecutor planExecutor
) {
super(planKey);
this.context = context;
this.viewName = viewName;
this.viewQuery = viewQuery;
this.replace = replace;
this.ifNotExists = ifNotExists;
this.planExecutor = planExecutor;
}
public OptimizerContext context() {
return context;
}
public String viewName() {
return viewName;
}
public String viewQuery() {
return viewQuery;
}
boolean isReplace() {
return replace;
}
public boolean ifNotExists() {
return ifNotExists;
}
@Override
public boolean isCacheable() {
return false;
}
@Override
public void checkPermissions(SqlSecurityContext context) {
context.checkPermission(new SqlPermission(viewName, ACTION_CREATE_VIEW));
}
@Override
public boolean producesRows() {
return false;
}
@Override
public SqlResult execute(QueryId queryId, List arguments, long timeout, SqlSecurityContext ssc) {
SqlPlanImpl.ensureNoArguments("CREATE VIEW", arguments);
SqlPlanImpl.ensureNoTimeout("CREATE VIEW", timeout);
return planExecutor.execute(this);
}
}
static class DropViewPlan extends SqlPlanImpl {
private final String viewName;
private final boolean ifExists;
private final PlanExecutor planExecutor;
DropViewPlan(
PlanKey planKey,
String viewName,
boolean ifExists,
PlanExecutor planExecutor
) {
super(planKey);
this.viewName = viewName;
this.ifExists = ifExists;
this.planExecutor = planExecutor;
}
String viewName() {
return viewName;
}
boolean isIfExists() {
return ifExists;
}
@Override
public boolean isCacheable() {
return false;
}
@Override
public boolean producesRows() {
return false;
}
@Override
public void checkPermissions(SqlSecurityContext context) {
context.checkPermission(new SqlPermission(viewName, ACTION_DROP_VIEW));
}
@Override
public SqlResult execute(QueryId queryId, List arguments, long timeout, SqlSecurityContext ssc) {
SqlPlanImpl.ensureNoArguments("DROP VIEW", arguments);
SqlPlanImpl.ensureNoTimeout("DROP VIEW", timeout);
return planExecutor.execute(this);
}
}
static class CreateTypePlan extends SqlPlanImpl {
private final String name;
private final boolean replace;
private final boolean ifNotExists;
private final List columns;
private final Map options;
private final PlanExecutor planExecutor;
CreateTypePlan(
final PlanKey planKey,
final String name,
final boolean replace,
final boolean ifNotExists,
final List columns,
final Map options,
final PlanExecutor planExecutor
) {
super(planKey);
this.name = name;
this.replace = replace;
this.ifNotExists = ifNotExists;
this.columns = columns;
this.options = options;
this.planExecutor = planExecutor;
}
public String name() {
return name;
}
public Map options() {
return options;
}
public String option(String name) {
return options.get(name);
}
public boolean replace() {
return replace;
}
public boolean ifNotExists() {
return ifNotExists;
}
public List columns() {
return columns;
}
@Override
public boolean isCacheable() {
return false;
}
@Override
public void checkPermissions(SqlSecurityContext context) {
context.checkPermission(new SqlPermission(name, ACTION_CREATE_TYPE));
}
@Override
public boolean producesRows() {
return false;
}
@Override
public SqlResult execute(QueryId queryId, List arguments, long timeout, SqlSecurityContext ssc) {
SqlPlanImpl.ensureNoArguments("CREATE TYPE", arguments);
SqlPlanImpl.ensureNoTimeout("CREATE TYPE", timeout);
return planExecutor.execute(this);
}
}
static class DropTypePlan extends SqlPlanImpl {
private final String typeName;
private final boolean ifExists;
private final PlanExecutor planExecutor;
DropTypePlan(
PlanKey planKey,
String typeName,
boolean ifExists,
PlanExecutor planExecutor
) {
super(planKey);
this.typeName = typeName;
this.ifExists = ifExists;
this.planExecutor = planExecutor;
}
String typeName() {
return typeName;
}
boolean isIfExists() {
return ifExists;
}
@Override
public boolean isCacheable() {
return false;
}
@Override
public boolean producesRows() {
return false;
}
@Override
public void checkPermissions(SqlSecurityContext context) {
context.checkPermission(new SqlPermission(typeName, ACTION_DROP_TYPE));
}
@Override
public SqlResult execute(QueryId queryId, List arguments, long timeout, SqlSecurityContext ssc) {
SqlPlanImpl.ensureNoArguments("DROP TYPE", arguments);
SqlPlanImpl.ensureNoTimeout("DROP TYPE", timeout);
return planExecutor.execute(this);
}
}
static class ShowStatementPlan extends SqlPlanImpl {
private final ShowStatementTarget showTarget;
private final String dataConnectionName;
private final PlanExecutor planExecutor;
ShowStatementPlan(
PlanKey planKey,
ShowStatementTarget showTarget,
String dataConnectionName,
PlanExecutor planExecutor
) {
super(planKey);
this.showTarget = showTarget;
this.dataConnectionName = dataConnectionName;
this.planExecutor = planExecutor;
}
ShowStatementTarget getShowTarget() {
return showTarget;
}
String getDataConnectionName() {
return dataConnectionName;
}
@Override
public boolean isCacheable() {
return false;
}
@Override
public boolean producesRows() {
return true;
}
@Override
public SqlResult execute(QueryId queryId, List arguments, long timeout, SqlSecurityContext ssc) {
SqlPlanImpl.ensureNoArguments("SHOW " + showTarget, arguments);
SqlPlanImpl.ensureNoTimeout("SHOW " + showTarget, timeout);
return planExecutor.execute(this);
}
}
static class ExplainStatementPlan extends SqlPlanImpl {
private final PhysicalRel rel;
private final PlanExecutor planExecutor;
private final List permissions;
ExplainStatementPlan(
PlanKey planKey,
PhysicalRel rel,
List permissions,
PlanExecutor planExecutor
) {
super(planKey);
this.rel = rel;
this.planExecutor = planExecutor;
this.permissions = permissions;
}
public PhysicalRel getRel() {
return rel;
}
@Override
public boolean isCacheable() {
return false;
}
@Override
public boolean producesRows() {
return true;
}
@Override
public void checkPermissions(SqlSecurityContext context) {
permissions.forEach(context::checkPermission);
}
@Override
public SqlResult execute(QueryId queryId, List arguments, long timeout, @Nonnull SqlSecurityContext ssc) {
SqlPlanImpl.ensureNoTimeout("EXPLAIN", timeout);
return planExecutor.execute(this);
}
}
static class SelectPlan extends SqlPlanImpl {
private final Set objectKeys;
private final QueryParameterMetadata parameterMetadata;
private final DAG dag;
private final String query;
private final boolean isStreaming;
private final SqlRowMetadata rowMetadata;
private final PlanExecutor planExecutor;
private final List permissions;
// map of per-table partition pruning candidates, structured as
// mapName -> { columnName -> RexLiteralOrDynamicParam }
private final Map>>> partitionStrategyCandidates;
private final boolean analyzed;
private final JobConfig analyzeJobConfig;
@SuppressWarnings("checkstyle:ParameterNumber")
SelectPlan(
PlanKey planKey,
QueryParameterMetadata parameterMetadata,
Set objectKeys,
DAG dag,
String query,
boolean isStreaming,
SqlRowMetadata rowMetadata,
PlanExecutor planExecutor,
List permissions,
Map>>> partitionStrategyCandidates,
final boolean analyzed,
final JobConfig analyzeJobConfig
) {
super(planKey);
this.objectKeys = objectKeys;
this.parameterMetadata = parameterMetadata;
this.dag = dag;
this.query = query;
this.isStreaming = isStreaming;
this.rowMetadata = rowMetadata;
this.planExecutor = planExecutor;
this.permissions = permissions;
this.partitionStrategyCandidates = partitionStrategyCandidates;
this.analyzed = analyzed;
this.analyzeJobConfig = analyzeJobConfig;
}
QueryParameterMetadata getParameterMetadata() {
return parameterMetadata;
}
DAG getDag() {
return dag;
}
boolean isStreaming() {
return isStreaming;
}
SqlRowMetadata getRowMetadata() {
return rowMetadata;
}
public String getQuery() {
return query;
}
@Override
public boolean isCacheable() {
// Do not cache ANALYZE plan to give always the most up-to-date result
// and avoid race conditions due to shared, mutable analyzeJobConfig instance.
return !isAnalyzed() && !objectKeys.contains(PlanObjectKey.NON_CACHEABLE_OBJECT_KEY);
}
@Override
public boolean isPlanValid(PlanCheckContext context) {
return context.isValid(objectKeys);
}
public Map>>> getPartitionStrategyCandidates() {
return partitionStrategyCandidates;
}
public boolean isAnalyzed() {
return analyzed;
}
public JobConfig analyzeJobConfig() {
return analyzeJobConfig;
}
@Override
public void checkPermissions(SqlSecurityContext context) {
checkPermissions(context, dag);
permissions.forEach(context::checkPermission);
}
@Override
public boolean producesRows() {
return true;
}
@Override
public SqlResult execute(QueryId queryId, List arguments, long timeout, SqlSecurityContext ssc) {
return planExecutor.execute(this, queryId, arguments, timeout, ssc);
}
}
static class DmlPlan extends SqlPlanImpl {
private final TableModify.Operation operation;
private final Set objectKeys;
private final QueryParameterMetadata parameterMetadata;
private final DAG dag;
private final String query;
private final boolean infiniteRows;
private final PlanExecutor planExecutor;
private final List permissions;
private final boolean analyzed;
private final JobConfig analyzeJobConfig;
@SuppressWarnings("checkstyle:ParameterNumber")
DmlPlan(
Operation operation,
PlanKey planKey,
QueryParameterMetadata parameterMetadata,
Set objectKeys,
DAG dag,
String query,
boolean infiniteRows,
PlanExecutor planExecutor,
List permissions,
boolean analyzed,
JobConfig analyzeJobConfig) {
super(planKey);
this.operation = operation;
this.objectKeys = objectKeys;
this.parameterMetadata = parameterMetadata;
this.dag = dag;
this.query = query;
this.infiniteRows = infiniteRows;
this.planExecutor = planExecutor;
this.permissions = permissions;
this.analyzed = analyzed;
this.analyzeJobConfig = analyzeJobConfig;
}
Operation getOperation() {
return operation;
}
QueryParameterMetadata getParameterMetadata() {
return parameterMetadata;
}
DAG getDag() {
return dag;
}
public String getQuery() {
return query;
}
public boolean isInfiniteRows() {
return infiniteRows;
}
@Override
public boolean isCacheable() {
// Do not cache ANALYZE plan to give always the most up-to-date result
// and avoid race conditions due to shared, mutable analyzeJobConfig instance.
return !isAnalyzed() && !objectKeys.contains(PlanObjectKey.NON_CACHEABLE_OBJECT_KEY);
}
@Override
public boolean isPlanValid(PlanCheckContext context) {
return context.isValid(objectKeys);
}
public boolean isAnalyzed() {
return analyzed;
}
public JobConfig analyzeJobConfig() {
return analyzeJobConfig;
}
@Override
public void checkPermissions(SqlSecurityContext context) {
checkPermissions(context, dag);
permissions.forEach(context::checkPermission);
}
@Override
public boolean producesRows() {
return false;
}
@Override
public SqlResult execute(QueryId queryId, List arguments, long timeout, SqlSecurityContext ssc) {
return planExecutor.execute(this, queryId, arguments, timeout, ssc);
}
}
static class IMapSelectPlan extends SqlPlanImpl {
private final Set objectKeys;
private final QueryParameterMetadata parameterMetadata;
private final String mapName;
private final Expression> keyCondition;
private final KvRowProjector.Supplier rowProjectorSupplier;
private final SqlRowMetadata rowMetadata;
private final PlanExecutor planExecutor;
private final List permissions;
private final int keyConditionParamIndex;
IMapSelectPlan(
PlanKey planKey,
PlanObjectKey objectKey,
QueryParameterMetadata parameterMetadata,
String mapName,
Expression> keyCondition,
KvRowProjector.Supplier rowProjectorSupplier,
SqlRowMetadata rowMetadata,
PlanExecutor planExecutor,
List permissions
) {
super(planKey);
this.objectKeys = Collections.singleton(objectKey);
this.parameterMetadata = parameterMetadata;
this.mapName = mapName;
this.keyCondition = keyCondition;
this.rowProjectorSupplier = rowProjectorSupplier;
this.rowMetadata = rowMetadata;
this.planExecutor = planExecutor;
this.permissions = permissions;
this.keyConditionParamIndex = keyCondition instanceof ParameterExpression
? ((ParameterExpression>) keyCondition).getIndex()
: -1;
}
QueryParameterMetadata parameterMetadata() {
return parameterMetadata;
}
String mapName() {
return mapName;
}
Expression> keyCondition() {
return keyCondition;
}
KvRowProjector.Supplier rowProjectorSupplier() {
return rowProjectorSupplier;
}
SqlRowMetadata rowMetadata() {
return rowMetadata;
}
int keyConditionParamIndex() {
return keyConditionParamIndex;
}
@Override
public boolean isCacheable() {
return true;
}
@Override
public boolean isPlanValid(PlanCheckContext context) {
return context.isValid(objectKeys);
}
@Override
public void checkPermissions(SqlSecurityContext context) {
context.checkPermission(new MapPermission(mapName, ACTION_CREATE, ACTION_READ));
permissions.forEach(context::checkPermission);
}
@Override
public boolean producesRows() {
return true;
}
@Override
public SqlResult execute(QueryId queryId, List arguments, long timeout, SqlSecurityContext ssc) {
return planExecutor.execute(this, queryId, arguments, timeout, ssc);
}
}
static class IMapInsertPlan extends SqlPlanImpl {
private final Set objectKeys;
private final QueryParameterMetadata parameterMetadata;
private final String mapName;
private final Function>> entriesFn;
private final PlanExecutor planExecutor;
private final List permissions;
private final int keyParamIndex;
IMapInsertPlan(
PlanKey planKey,
PlanObjectKey objectKey,
QueryParameterMetadata parameterMetadata,
String mapName,
Function>> entriesFn,
PlanExecutor planExecutor,
List permissions,
int keyParamIndex
) {
super(planKey);
// TODO: extract key index here
this.objectKeys = Collections.singleton(objectKey);
this.parameterMetadata = parameterMetadata;
this.mapName = mapName;
this.entriesFn = entriesFn;
this.planExecutor = planExecutor;
this.permissions = permissions;
this.keyParamIndex = keyParamIndex;
}
QueryParameterMetadata parameterMetadata() {
return parameterMetadata;
}
String mapName() {
return mapName;
}
Function>> entriesFn() {
return entriesFn;
}
int keyParamIndex() {
return keyParamIndex;
}
@Override
public boolean isCacheable() {
return true;
}
@Override
public boolean isPlanValid(PlanCheckContext context) {
return context.isValid(objectKeys);
}
@Override
public void checkPermissions(SqlSecurityContext context) {
context.checkPermission(new MapPermission(mapName, ACTION_CREATE, ACTION_PUT));
permissions.forEach(context::checkPermission);
}
@Override
public boolean producesRows() {
return false;
}
@Override
public SqlResult execute(QueryId queryId, List arguments, long timeout, SqlSecurityContext ssc) {
return planExecutor.execute(this, arguments, timeout, ssc);
}
}
static class IMapSinkPlan extends SqlPlanImpl {
private final Set objectKeys;
private final QueryParameterMetadata parameterMetadata;
private final String mapName;
private final Function> entriesFn;
private final PlanExecutor planExecutor;
private final List permissions;
IMapSinkPlan(
PlanKey planKey,
PlanObjectKey objectKey,
QueryParameterMetadata parameterMetadata,
String mapName,
Function> entriesFn,
PlanExecutor planExecutor,
List permissions
) {
super(planKey);
this.objectKeys = Collections.singleton(objectKey);
this.parameterMetadata = parameterMetadata;
this.mapName = mapName;
this.entriesFn = entriesFn;
this.planExecutor = planExecutor;
this.permissions = permissions;
}
QueryParameterMetadata parameterMetadata() {
return parameterMetadata;
}
String mapName() {
return mapName;
}
Function> entriesFn() {
return entriesFn;
}
@Override
public boolean isCacheable() {
return true;
}
@Override
public boolean isPlanValid(PlanCheckContext context) {
return context.isValid(objectKeys);
}
@Override
public void checkPermissions(SqlSecurityContext context) {
context.checkPermission(new MapPermission(mapName, ACTION_CREATE, ACTION_PUT));
permissions.forEach(context::checkPermission);
}
@Override
public boolean producesRows() {
return false;
}
@Override
public SqlResult execute(QueryId queryId, List arguments, long timeout, SqlSecurityContext ssc) {
return planExecutor.execute(this, arguments, timeout, ssc);
}
}
static class IMapUpdatePlan extends SqlPlanImpl {
private final Set objectKeys;
private final QueryParameterMetadata parameterMetadata;
private final String mapName;
private final Expression> keyCondition;
private final UpdatingEntryProcessor.Supplier updaterSupplier;
private final PlanExecutor planExecutor;
private final List permissions;
private final int keyConditionParamIndex;
IMapUpdatePlan(
PlanKey planKey,
PlanObjectKey objectKey,
QueryParameterMetadata parameterMetadata,
String mapName,
Expression> keyCondition,
UpdatingEntryProcessor.Supplier updaterSupplier,
PlanExecutor planExecutor,
List permissions
) {
super(planKey);
this.objectKeys = Collections.singleton(objectKey);
this.parameterMetadata = parameterMetadata;
this.mapName = mapName;
this.keyCondition = keyCondition;
this.updaterSupplier = updaterSupplier;
this.planExecutor = planExecutor;
this.permissions = permissions;
this.keyConditionParamIndex = keyCondition instanceof ParameterExpression
? ((ParameterExpression>) keyCondition).getIndex()
: -1;
}
QueryParameterMetadata parameterMetadata() {
return parameterMetadata;
}
String mapName() {
return mapName;
}
Expression> keyCondition() {
return keyCondition;
}
UpdatingEntryProcessor.Supplier updaterSupplier() {
return updaterSupplier;
}
int keyConditionParamIndex() {
return keyConditionParamIndex;
}
@Override
public boolean isCacheable() {
return true;
}
@Override
public boolean isPlanValid(PlanCheckContext context) {
return context.isValid(objectKeys);
}
@Override
public void checkPermissions(SqlSecurityContext context) {
// We are checking ACTION_CREATE and ACTION_READ permissions to align with DmlPlan.
context.checkPermission(new MapPermission(mapName, ACTION_CREATE, ACTION_READ, ACTION_PUT, ACTION_REMOVE));
permissions.forEach(context::checkPermission);
}
@Override
public boolean producesRows() {
return false;
}
@Override
public SqlResult execute(QueryId queryId, List arguments, long timeout, SqlSecurityContext ssc) {
return planExecutor.execute(this, arguments, timeout, ssc);
}
}
static class IMapDeletePlan extends SqlPlanImpl {
private final Set objectKeys;
private final QueryParameterMetadata parameterMetadata;
private final String mapName;
private final Expression> keyCondition;
private final PlanExecutor planExecutor;
private final List permissions;
private final int keyConditionParamIndex;
IMapDeletePlan(
PlanKey planKey,
PlanObjectKey objectKey,
QueryParameterMetadata parameterMetadata,
String mapName,
Expression> keyCondition,
PlanExecutor planExecutor,
List permissions
) {
super(planKey);
this.objectKeys = Collections.singleton(objectKey);
this.parameterMetadata = parameterMetadata;
this.mapName = mapName;
this.keyCondition = keyCondition;
this.planExecutor = planExecutor;
this.permissions = permissions;
this.keyConditionParamIndex = keyCondition instanceof ParameterExpression
? ((ParameterExpression>) keyCondition).getIndex()
: -1;
}
QueryParameterMetadata parameterMetadata() {
return parameterMetadata;
}
String mapName() {
return mapName;
}
Expression> keyCondition() {
return keyCondition;
}
int keyConditionParamIndex() {
return keyConditionParamIndex;
}
@Override
public boolean isCacheable() {
return true;
}
@Override
public boolean isPlanValid(PlanCheckContext context) {
return context.isValid(objectKeys);
}
@Override
public void checkPermissions(SqlSecurityContext context) {
// We are checking ACTION_CREATE and ACTION_READ permissions to align with DmlPlan.
context.checkPermission(new MapPermission(mapName, ACTION_CREATE, ACTION_READ, ACTION_PUT, ACTION_REMOVE));
permissions.forEach(context::checkPermission);
}
@Override
public boolean producesRows() {
return false;
}
@Override
public SqlResult execute(QueryId queryId, List arguments, long timeout, SqlSecurityContext ssc) {
return planExecutor.execute(this, arguments, timeout, ssc);
}
}
private static void ensureNoArguments(String name, List arguments) {
if (!arguments.isEmpty()) {
throw QueryException.error(name + " does not support dynamic parameters");
}
}
private static void ensureNoTimeout(String name, long timeout) {
if (timeout > 0) {
throw QueryException.error(name + " does not support timeout");
}
}
private static void ensureNoneGuaranteesForBatchJob(JobConfig jobConfig) {
if (jobConfig.getProcessingGuarantee() != ProcessingGuarantee.NONE) {
throw QueryException.error("Only NONE guarantee is allowed for batch job");
}
}
}