List of usage examples for org.apache.commons.lang3.mutable Mutable getValue
T getValue();
From source file:org.apache.asterix.optimizer.rules.InjectTypeCastForUnionRule.java
private boolean injectCast(UnionAllOperator op, int childIndex, IOptimizationContext context) throws AlgebricksException { // Gets the type environments for the union all operator and its child operator with the right child index. IVariableTypeEnvironment env = context.getOutputTypeEnvironment(op); Mutable<ILogicalOperator> branchOpRef = op.getInputs().get(childIndex); IVariableTypeEnvironment childEnv = context.getOutputTypeEnvironment(branchOpRef.getValue()); // The two lists are used for the assign operator that calls cast functions. List<LogicalVariable> varsToCast = new ArrayList<>(); List<Mutable<ILogicalExpression>> castFunctionsForLeft = new ArrayList<>(); // Iterate through all triples. List<Triple<LogicalVariable, LogicalVariable, LogicalVariable>> triples = op.getVariableMappings(); for (Triple<LogicalVariable, LogicalVariable, LogicalVariable> triple : triples) { LogicalVariable producedVar = triple.third; IAType producedType = (IAType) env.getVarType(producedVar); LogicalVariable varToCast = childIndex == 0 ? triple.first : triple.second; IAType inputType = (IAType) childEnv.getVarType(varToCast); if (!TypeResolverUtil.needsCast(producedType, inputType)) { // Continues to the next triple if no cast is neeeded. continue; }/*from w w w . j a va 2 s.com*/ LogicalVariable castedVar = context.newVar(); // Resets triple variables to new variables that bind to the results of type casting. triple.first = childIndex == 0 ? castedVar : triple.first; triple.second = childIndex > 0 ? castedVar : triple.second; ScalarFunctionCallExpression castFunc = new ScalarFunctionCallExpression( FunctionUtil.getFunctionInfo(AsterixBuiltinFunctions.CAST_TYPE), new ArrayList<>(Collections .singletonList(new MutableObject<>(new VariableReferenceExpression(varToCast))))); TypeCastUtils.setRequiredAndInputTypes(castFunc, producedType, inputType); // Adds the variable and function expression into lists, for the assign operator. varsToCast.add(castedVar); castFunctionsForLeft.add(new MutableObject<>(castFunc)); } if (castFunctionsForLeft.isEmpty()) { return false; } // Injects an assign operator to perform type casts. AssignOperator assignOp = new AssignOperator(varsToCast, castFunctionsForLeft); assignOp.getInputs().add(new MutableObject<>(branchOpRef.getValue())); branchOpRef.setValue(assignOp); context.computeAndSetTypeEnvironmentForOperator(assignOp); // Returns true to indicate that rewriting happens. return true; }
From source file:org.apache.asterix.optimizer.rules.InlineUnnestFunctionRule.java
@Override public boolean rewritePost(Mutable<ILogicalOperator> opRef, IOptimizationContext context) throws AlgebricksException { AbstractLogicalOperator op1 = (AbstractLogicalOperator) opRef.getValue(); if (context.checkIfInDontApplySet(this, op1)) { return false; }/*from www.j a v a 2 s . c om*/ context.addToDontApplySet(this, op1); if (op1.getOperatorTag() != LogicalOperatorTag.UNNEST) { return false; } UnnestOperator unnestOperator = (UnnestOperator) op1; AbstractFunctionCallExpression expr = (AbstractFunctionCallExpression) unnestOperator.getExpressionRef() .getValue(); //we only inline for the scan-collection function if (expr.getFunctionIdentifier() != AsterixBuiltinFunctions.SCAN_COLLECTION) { return false; } // inline all variables from an unnesting function call AbstractFunctionCallExpression funcExpr = expr; List<Mutable<ILogicalExpression>> args = funcExpr.getArguments(); for (int i = 0; i < args.size(); i++) { ILogicalExpression argExpr = args.get(i).getValue(); if (argExpr.getExpressionTag() == LogicalExpressionTag.VARIABLE) { VariableReferenceExpression varExpr = (VariableReferenceExpression) argExpr; inlineVariable(varExpr.getVariableReference(), unnestOperator); } } return true; }
From source file:org.apache.asterix.optimizer.rules.IntroduceAutogenerateIDRule.java
@Override public boolean rewritePost(Mutable<ILogicalOperator> opRef, IOptimizationContext context) throws AlgebricksException { // match: commit OR distribute-result OR SINK - ... followed by: // [insert to internal dataset with autogenerated id] - assign - project // produce: insert - assign - assign* - project // **//from w w w .j av a 2 s. co m // OR [insert to internal dataset with autogenerated id] - assign - [datasource scan] // produce insert - assign - assign* - datasource scan AbstractLogicalOperator currentOp = (AbstractLogicalOperator) opRef.getValue(); if (currentOp.getOperatorTag() == LogicalOperatorTag.DELEGATE_OPERATOR) { DelegateOperator dOp = (DelegateOperator) currentOp; if (!(dOp.getDelegate() instanceof CommitOperator)) { return false; } else if (!((CommitOperator) dOp.getDelegate()).isSink()) { return false; } } else if (currentOp.getOperatorTag() != LogicalOperatorTag.DISTRIBUTE_RESULT && currentOp.getOperatorTag() != LogicalOperatorTag.SINK) { return false; } ArrayDeque<AbstractLogicalOperator> opStack = new ArrayDeque<>(); opStack.push(currentOp); while (currentOp.getInputs().size() == 1) { currentOp = (AbstractLogicalOperator) currentOp.getInputs().get(0).getValue(); if (currentOp.getOperatorTag() == LogicalOperatorTag.INSERT_DELETE_UPSERT) { break; } opStack.push(currentOp); } if (currentOp.getOperatorTag() != LogicalOperatorTag.INSERT_DELETE_UPSERT) { return false; } InsertDeleteUpsertOperator insertOp = (InsertDeleteUpsertOperator) currentOp; if (insertOp.getOperation() != Kind.INSERT) { return false; } DatasetDataSource dds = (DatasetDataSource) insertOp.getDataSource(); boolean autogenerated = ((InternalDatasetDetails) dds.getDataset().getDatasetDetails()).isAutogenerated(); if (!autogenerated) { return false; } if (((AqlDataSource) insertOp.getDataSource()).getDatasourceType() != AqlDataSourceType.INTERNAL_DATASET) { return false; } AbstractLogicalOperator parentOp = (AbstractLogicalOperator) currentOp.getInputs().get(0).getValue(); if (parentOp.getOperatorTag() != LogicalOperatorTag.ASSIGN) { return false; } AssignOperator assignOp = (AssignOperator) parentOp; LogicalVariable inputRecord; //TODO: bug here. will not work for internal datasets with filters since the pattern becomes //[project-assign-assign-insert] AbstractLogicalOperator grandparentOp = (AbstractLogicalOperator) parentOp.getInputs().get(0).getValue(); if (grandparentOp.getOperatorTag() == LogicalOperatorTag.PROJECT) { ProjectOperator projectOp = (ProjectOperator) grandparentOp; inputRecord = projectOp.getVariables().get(0); } else if (grandparentOp.getOperatorTag() == LogicalOperatorTag.DATASOURCESCAN) { DataSourceScanOperator dssOp = (DataSourceScanOperator) grandparentOp; inputRecord = dssOp.getVariables().get(0); } else { return false; } List<String> pkFieldName = ((InternalDatasetDetails) dds.getDataset().getDatasetDetails()).getPrimaryKey() .get(0); ILogicalExpression rec0 = new VariableReferenceExpression(inputRecord); ILogicalExpression rec1 = createPrimaryKeyRecordExpression(pkFieldName); ILogicalExpression mergedRec = createRecordMergeFunction(rec0, rec1); ILogicalExpression nonNullMergedRec = createNotNullFunction(mergedRec); LogicalVariable v = context.newVar(); AssignOperator newAssign = new AssignOperator(v, new MutableObject<ILogicalExpression>(nonNullMergedRec)); newAssign.getInputs().add(new MutableObject<ILogicalOperator>(grandparentOp)); assignOp.getInputs().set(0, new MutableObject<ILogicalOperator>(newAssign)); VariableUtilities.substituteVariables(assignOp, inputRecord, v, context); VariableUtilities.substituteVariables(insertOp, inputRecord, v, context); context.computeAndSetTypeEnvironmentForOperator(newAssign); context.computeAndSetTypeEnvironmentForOperator(assignOp); context.computeAndSetTypeEnvironmentForOperator(insertOp); ; for (AbstractLogicalOperator op : opStack) { VariableUtilities.substituteVariables(op, inputRecord, v, context); context.computeAndSetTypeEnvironmentForOperator(op); } return true; }
From source file:org.apache.asterix.optimizer.rules.IntroduceDynamicTypeCastForExternalFunctionRule.java
@Override public boolean rewritePost(Mutable<ILogicalOperator> opRef, IOptimizationContext context) throws AlgebricksException { /**//w w w . j av a2 s .c o m * pattern match: distribute_result - project - assign (external function call) - assign (open_record_constructor) * resulting plan: distribute_result - project - assign (external function call) - assign (cast-record) - assign(open_record_constructor) */ AbstractLogicalOperator op1 = (AbstractLogicalOperator) opRef.getValue(); if (op1.getOperatorTag() != LogicalOperatorTag.DISTRIBUTE_RESULT) { return false; } AbstractLogicalOperator op2 = (AbstractLogicalOperator) op1.getInputs().get(0).getValue(); if (op2.getOperatorTag() != LogicalOperatorTag.PROJECT) { return false; } AbstractLogicalOperator op3 = (AbstractLogicalOperator) op2.getInputs().get(0).getValue(); if (op3.getOperatorTag() != LogicalOperatorTag.ASSIGN) { return false; } AbstractLogicalOperator op4 = (AbstractLogicalOperator) op3.getInputs().get(0).getValue(); if (op4.getOperatorTag() != LogicalOperatorTag.ASSIGN) { return false; } // Op1 : assign (external function call), Op2 : assign (open_record_constructor) AssignOperator assignOp1 = (AssignOperator) op3; AssignOperator assignOp2 = (AssignOperator) op4; // Checks whether open-record-constructor is called to create a record in the first assign operator - assignOp2 FunctionIdentifier fid = null; ILogicalExpression assignExpr = assignOp2.getExpressions().get(0).getValue(); if (assignExpr.getExpressionTag() == LogicalExpressionTag.FUNCTION_CALL) { ScalarFunctionCallExpression funcExpr = (ScalarFunctionCallExpression) assignOp2.getExpressions().get(0) .getValue(); fid = funcExpr.getFunctionIdentifier(); if (fid != AsterixBuiltinFunctions.OPEN_RECORD_CONSTRUCTOR) { return false; } } else { return false; } // Checks whether an external function is called in the second assign operator - assignOp1 assignExpr = assignOp1.getExpressions().get(0).getValue(); ScalarFunctionCallExpression funcExpr = null; if (assignExpr.getExpressionTag() == LogicalExpressionTag.FUNCTION_CALL) { funcExpr = (ScalarFunctionCallExpression) assignOp1.getExpressions().get(0).getValue(); fid = funcExpr.getFunctionIdentifier(); // Checks whether this is an internal function call. Then, we return false. if (AsterixBuiltinFunctions.getBuiltinFunctionIdentifier(fid) != null) { return false; } } else { return false; } AsterixExternalScalarFunctionInfo finfo = (AsterixExternalScalarFunctionInfo) funcExpr.getFunctionInfo(); ARecordType requiredRecordType = (ARecordType) finfo.getArgumenTypes().get(0); List<LogicalVariable> recordVar = new ArrayList<LogicalVariable>(); recordVar.addAll(assignOp2.getVariables()); IVariableTypeEnvironment env = assignOp2.computeOutputTypeEnvironment(context); IAType inputRecordType = (IAType) env.getVarType(recordVar.get(0)); /** the input record type can be an union type -- for the case when it comes from a subplan or left-outer join */ boolean checkUnknown = false; while (NonTaggedFormatUtil.isOptional(inputRecordType)) { /** while-loop for the case there is a nested multi-level union */ inputRecordType = ((AUnionType) inputRecordType).getActualType(); checkUnknown = true; } /** see whether the input record type needs to be casted */ boolean cast = !IntroduceDynamicTypeCastRule.compatible(requiredRecordType, inputRecordType); if (checkUnknown) { recordVar.set(0, IntroduceDynamicTypeCastRule.addWrapperFunction(requiredRecordType, recordVar.get(0), assignOp1, context, AsterixBuiltinFunctions.CHECK_UNKNOWN)); } if (cast) { IntroduceDynamicTypeCastRule.addWrapperFunction(requiredRecordType, recordVar.get(0), assignOp1, context, AsterixBuiltinFunctions.CAST_TYPE); } return cast || checkUnknown; }
From source file:org.apache.asterix.optimizer.rules.IntroduceDynamicTypeCastRule.java
@Override public boolean rewritePost(Mutable<ILogicalOperator> opRef, IOptimizationContext context) throws AlgebricksException { // Depending on the operator type, we need to extract the following pieces of information. AbstractLogicalOperator op;/*from w w w . j a v a2s . c o m*/ ARecordType requiredRecordType; LogicalVariable recordVar; // We identify INSERT and DISTRIBUTE_RESULT operators. AbstractLogicalOperator op1 = (AbstractLogicalOperator) opRef.getValue(); switch (op1.getOperatorTag()) { case SINK: case DELEGATE_OPERATOR: { /** * pattern match: commit insert assign * resulting plan: commit-insert-project-assign */ if (op1.getOperatorTag() == LogicalOperatorTag.DELEGATE_OPERATOR) { DelegateOperator eOp = (DelegateOperator) op1; if (!(eOp.getDelegate() instanceof CommitOperator)) { return false; } } AbstractLogicalOperator op2 = (AbstractLogicalOperator) op1.getInputs().get(0).getValue(); if (op2.getOperatorTag() == LogicalOperatorTag.INSERT_DELETE_UPSERT) { InsertDeleteUpsertOperator insertDeleteOp = (InsertDeleteUpsertOperator) op2; if (insertDeleteOp.getOperation() == InsertDeleteUpsertOperator.Kind.DELETE) { return false; } // Remember this is the operator we need to modify op = insertDeleteOp; // Derive the required ARecordType based on the schema of the AqlDataSource InsertDeleteUpsertOperator insertDeleteOperator = (InsertDeleteUpsertOperator) op2; AqlDataSource dataSource = (AqlDataSource) insertDeleteOperator.getDataSource(); requiredRecordType = (ARecordType) dataSource.getItemType(); // Derive the Variable which we will potentially wrap with cast/null functions ILogicalExpression expr = insertDeleteOperator.getPayloadExpression().getValue(); List<LogicalVariable> payloadVars = new ArrayList<>(); expr.getUsedVariables(payloadVars); recordVar = payloadVars.get(0); } else { return false; } break; } case DISTRIBUTE_RESULT: { // First, see if there was an output-record-type specified requiredRecordType = (ARecordType) op1.getAnnotations().get("output-record-type"); if (requiredRecordType == null) { return false; } // Remember this is the operator we need to modify op = op1; recordVar = ((VariableReferenceExpression) ((DistributeResultOperator) op).getExpressions().get(0) .getValue()).getVariableReference(); break; } default: { return false; } } // Derive the statically-computed type of the record IVariableTypeEnvironment env = op.computeOutputTypeEnvironment(context); IAType inputRecordType = (IAType) env.getVarType(recordVar); /** the input record type can be an union type -- for the case when it comes from a subplan or left-outer join */ boolean checkUnknown = false; while (NonTaggedFormatUtil.isOptional(inputRecordType)) { /** while-loop for the case there is a nested multi-level union */ inputRecordType = ((AUnionType) inputRecordType).getActualType(); checkUnknown = true; } /** see whether the input record type needs to be casted */ boolean cast = !compatible(requiredRecordType, inputRecordType); if (checkUnknown) { recordVar = addWrapperFunction(requiredRecordType, recordVar, op, context, AsterixBuiltinFunctions.CHECK_UNKNOWN); } if (cast) { addWrapperFunction(requiredRecordType, recordVar, op, context, AsterixBuiltinFunctions.CAST_TYPE); } return cast || checkUnknown; }
From source file:org.apache.asterix.optimizer.rules.IntroduceDynamicTypeCastRule.java
/** * Inject a function to wrap a variable when necessary * * @param requiredRecordType//from w w w . ja v a 2 s. c om * the required record type * @param recordVar * the record variable * @param parent * the current parent operator to be rewritten * @param context * the optimization context * @param fd * the function to be injected * @return true if cast is injected; false otherwise. * @throws AlgebricksException */ public static LogicalVariable addWrapperFunction(ARecordType requiredRecordType, LogicalVariable recordVar, ILogicalOperator parent, IOptimizationContext context, FunctionIdentifier fd) throws AlgebricksException { List<Mutable<ILogicalOperator>> opRefs = parent.getInputs(); for (int index = 0; index < opRefs.size(); index++) { Mutable<ILogicalOperator> opRef = opRefs.get(index); ILogicalOperator op = opRef.getValue(); /** get produced vars */ List<LogicalVariable> producedVars = new ArrayList<LogicalVariable>(); VariableUtilities.getProducedVariables(op, producedVars); IVariableTypeEnvironment env = op.computeOutputTypeEnvironment(context); for (int i = 0; i < producedVars.size(); i++) { LogicalVariable var = producedVars.get(i); if (var.equals(recordVar)) { /** insert an assign operator to call the function on-top-of the variable */ IAType actualType = (IAType) env.getVarType(var); AbstractFunctionCallExpression cast = new ScalarFunctionCallExpression( FunctionUtil.getFunctionInfo(fd)); cast.getArguments() .add(new MutableObject<ILogicalExpression>(new VariableReferenceExpression(var))); /** enforce the required record type */ TypeCastUtils.setRequiredAndInputTypes(cast, requiredRecordType, actualType); LogicalVariable newAssignVar = context.newVar(); AssignOperator newAssignOperator = new AssignOperator(newAssignVar, new MutableObject<ILogicalExpression>(cast)); newAssignOperator.getInputs().add(new MutableObject<ILogicalOperator>(op)); opRef.setValue(newAssignOperator); context.computeAndSetTypeEnvironmentForOperator(newAssignOperator); newAssignOperator.computeOutputTypeEnvironment(context); VariableUtilities.substituteVariables(parent, recordVar, newAssignVar, context); return newAssignVar; } } /** recursive descend to the operator who produced the recordVar */ LogicalVariable replacedVar = addWrapperFunction(requiredRecordType, recordVar, op, context, fd); if (replacedVar != null) { /** substitute the recordVar by the replacedVar for operators who uses recordVar */ VariableUtilities.substituteVariables(parent, recordVar, replacedVar, context); return replacedVar; } } return null; }
From source file:org.apache.asterix.optimizer.rules.IntroduceEnforcedListTypeRule.java
private boolean rewriteExpressions(List<Mutable<ILogicalExpression>> expressions, IVariableTypeEnvironment env) throws AlgebricksException { boolean changed = false; for (Mutable<ILogicalExpression> exprRef : expressions) { ILogicalExpression expr = exprRef.getValue(); if (expr.getExpressionTag() == LogicalExpressionTag.FUNCTION_CALL) { AbstractFunctionCallExpression argFuncExpr = (AbstractFunctionCallExpression) expr; IAType exprType = (IAType) env.getType(argFuncExpr); if (StaticTypeCastUtil.rewriteListExpr(argFuncExpr, exprType, exprType, env)) { TypeCastUtils.resetRequiredAndInputTypes(argFuncExpr); changed = true;/*w w w .jav a2 s. c o m*/ } } } return changed; }
From source file:org.apache.asterix.optimizer.rules.IntroduceMaterializationForInsertWithSelfScanRule.java
@Override public boolean rewritePost(Mutable<ILogicalOperator> opRef, IOptimizationContext context) throws AlgebricksException { AbstractLogicalOperator op = (AbstractLogicalOperator) opRef.getValue(); if (op.getOperatorTag() != LogicalOperatorTag.INSERT_DELETE_UPSERT) { return false; }/*from w w w. ja va2 s. c o m*/ InsertDeleteUpsertOperator insertOp = (InsertDeleteUpsertOperator) op; boolean sameDataset = checkIfInsertAndScanDatasetsSame(op, ((DatasetDataSource) insertOp.getDataSource()).getDataset().getDatasetName()); if (sameDataset) { MaterializeOperator materializeOperator = new MaterializeOperator(); MaterializePOperator materializePOperator = new MaterializePOperator(true); materializeOperator.setPhysicalOperator(materializePOperator); materializeOperator.getInputs() .add(new MutableObject<ILogicalOperator>(insertOp.getInputs().get(0).getValue())); context.computeAndSetTypeEnvironmentForOperator(materializeOperator); insertOp.getInputs().clear(); insertOp.getInputs().add(new MutableObject<ILogicalOperator>(materializeOperator)); context.computeAndSetTypeEnvironmentForOperator(insertOp); return true; } else { return false; } }
From source file:org.apache.asterix.optimizer.rules.IntroduceRandomPartitioningFeedComputationRule.java
@Override public boolean rewritePre(Mutable<ILogicalOperator> opRef, IOptimizationContext context) throws AlgebricksException { ILogicalOperator op = opRef.getValue(); if (!op.getOperatorTag().equals(LogicalOperatorTag.ASSIGN)) { return false; }//from w w w . j av a 2 s .co m ILogicalOperator opChild = op.getInputs().get(0).getValue(); if (!opChild.getOperatorTag().equals(LogicalOperatorTag.DATASOURCESCAN)) { return false; } DataSourceScanOperator scanOp = (DataSourceScanOperator) opChild; AqlDataSource dataSource = (AqlDataSource) scanOp.getDataSource(); if (dataSource.getDatasourceType() != AqlDataSourceType.FEED) { return false; } final FeedDataSource feedDataSource = (FeedDataSource) dataSource; Feed feed = feedDataSource.getFeed(); if (feed.getAppliedFunction() == null) { return false; } ExchangeOperator exchangeOp = new ExchangeOperator(); INodeDomain domain = new INodeDomain() { @Override public boolean sameAs(INodeDomain domain) { return domain == this; } @Override public Integer cardinality() { return feedDataSource.getComputeCardinality(); } }; exchangeOp.setPhysicalOperator(new RandomPartitionExchangePOperator(domain)); op.getInputs().get(0).setValue(exchangeOp); exchangeOp.getInputs().add(new MutableObject<ILogicalOperator>(scanOp)); ExecutionMode em = ((AbstractLogicalOperator) scanOp).getExecutionMode(); exchangeOp.setExecutionMode(em); exchangeOp.computeDeliveredPhysicalProperties(context); context.computeAndSetTypeEnvironmentForOperator(exchangeOp); AssignOperator assignOp = (AssignOperator) opRef.getValue(); AssignPOperator assignPhyOp = (AssignPOperator) assignOp.getPhysicalOperator(); assignPhyOp.setCardinalityConstraint(domain.cardinality()); return true; }
From source file:org.apache.asterix.optimizer.rules.IntroduceSecondaryIndexInsertDeleteRule.java
@Override public boolean rewritePost(Mutable<ILogicalOperator> opRef, IOptimizationContext context) throws AlgebricksException { AbstractLogicalOperator op0 = (AbstractLogicalOperator) opRef.getValue(); if (op0.getOperatorTag() != LogicalOperatorTag.DELEGATE_OPERATOR && op0.getOperatorTag() != LogicalOperatorTag.SINK) { return false; }//from w w w . j a v a2 s . c om if (op0.getOperatorTag() == LogicalOperatorTag.DELEGATE_OPERATOR) { DelegateOperator eOp = (DelegateOperator) op0; if (!(eOp.getDelegate() instanceof CommitOperator)) { return false; } } AbstractLogicalOperator op1 = (AbstractLogicalOperator) op0.getInputs().get(0).getValue(); if (op1.getOperatorTag() != LogicalOperatorTag.INSERT_DELETE_UPSERT) { return false; } /** find the record variable */ InsertDeleteUpsertOperator primaryIndexModificationOp = (InsertDeleteUpsertOperator) op0.getInputs().get(0) .getValue(); boolean isBulkload = primaryIndexModificationOp.isBulkload(); ILogicalExpression newRecordExpr = primaryIndexModificationOp.getPayloadExpression().getValue(); List<Mutable<ILogicalExpression>> newMetaExprs = primaryIndexModificationOp .getAdditionalNonFilteringExpressions(); LogicalVariable newRecordVar; LogicalVariable newMetaVar = null; /** * inputOp is the assign operator which extracts primary keys from the input * variables (record or meta) */ AbstractLogicalOperator inputOp = (AbstractLogicalOperator) primaryIndexModificationOp.getInputs().get(0) .getValue(); newRecordVar = getRecordVar(context, inputOp, newRecordExpr, 0); if (newMetaExprs != null && !newMetaExprs.isEmpty()) { if (newMetaExprs.size() > 1) { throw new AlgebricksException( "Number of meta records can't be more than 1. Number of meta records found = " + newMetaExprs.size()); } newMetaVar = getRecordVar(context, inputOp, newMetaExprs.get(0).getValue(), 1); } /* * At this point, we have the record variable and the insert/delete/upsert operator * Note: We have two operators: * 1. An InsertDeleteOperator (primary) * 2. An IndexInsertDeleteOperator (secondary) * The current primaryIndexModificationOp is of the first type */ AqlDataSource datasetSource = (AqlDataSource) primaryIndexModificationOp.getDataSource(); AqlMetadataProvider mp = (AqlMetadataProvider) context.getMetadataProvider(); String dataverseName = datasetSource.getId().getDataverseName(); String datasetName = datasetSource.getId().getDatasourceName(); Dataset dataset = mp.findDataset(dataverseName, datasetName); if (dataset == null) { throw new AlgebricksException("Unknown dataset " + datasetName + " in dataverse " + dataverseName); } if (dataset.getDatasetType() == DatasetType.EXTERNAL) { return false; } // Create operators for secondary index insert / delete. String itemTypeName = dataset.getItemTypeName(); IAType itemType = mp.findType(dataset.getItemTypeDataverseName(), itemTypeName); if (itemType.getTypeTag() != ATypeTag.RECORD) { throw new AlgebricksException("Only record types can be indexed."); } ARecordType recType = (ARecordType) itemType; // meta type ARecordType metaType = null; if (dataset.hasMetaPart()) { metaType = (ARecordType) mp.findType(dataset.getMetaItemTypeDataverseName(), dataset.getMetaItemTypeName()); } List<Index> indexes = mp.getDatasetIndexes(dataset.getDataverseName(), dataset.getDatasetName()); // Set the top operator pointer to the primary IndexInsertDeleteOperator ILogicalOperator currentTop = primaryIndexModificationOp; boolean hasSecondaryIndex = false; // Put an n-gram or a keyword index in the later stage of index-update, // since TokenizeOperator needs to be involved. Collections.sort(indexes, (o1, o2) -> o1.getIndexType().ordinal() - o2.getIndexType().ordinal()); // At this point, we have the data type info, and the indexes info as well int secondaryIndexTotalCnt = indexes.size() - 1; if (secondaryIndexTotalCnt > 0) { op0.getInputs().clear(); } else { return false; } // Initialize inputs to the SINK operator Op0 (The SINK) is now without input // Prepare filtering field information (This is the filter created using the "filter with" key word in the // create dataset ddl) List<String> filteringFields = ((InternalDatasetDetails) dataset.getDatasetDetails()).getFilterField(); List<LogicalVariable> filteringVars; List<Mutable<ILogicalExpression>> filteringExpressions = null; if (filteringFields != null) { // The filter field var already exists. we can simply get it from the insert op filteringVars = new ArrayList<>(); filteringExpressions = new ArrayList<>(); for (Mutable<ILogicalExpression> filteringExpression : primaryIndexModificationOp .getAdditionalFilteringExpressions()) { filteringExpression.getValue().getUsedVariables(filteringVars); for (LogicalVariable var : filteringVars) { filteringExpressions .add(new MutableObject<ILogicalExpression>(new VariableReferenceExpression(var))); } } } // Replicate Operator is applied only when doing the bulk-load. ReplicateOperator replicateOp = null; if (secondaryIndexTotalCnt > 1 && primaryIndexModificationOp.isBulkload()) { // Split the logical plan into "each secondary index update branch" // to replicate each <PK,RECORD> pair. replicateOp = new ReplicateOperator(secondaryIndexTotalCnt); replicateOp.getInputs().add(new MutableObject<ILogicalOperator>(currentTop)); replicateOp.setExecutionMode(ExecutionMode.PARTITIONED); context.computeAndSetTypeEnvironmentForOperator(replicateOp); currentTop = replicateOp; } /* * The two maps are used to store variables to which [casted] field access is assigned. * One for the beforeOp record and the other for the new record. * There are two uses for these maps: * 1. used for shared fields in indexes with overlapping keys. * 2. used for setting variables of secondary keys for each secondary index operator. */ Map<IndexFieldId, LogicalVariable> fieldVarsForBeforeOperation = new HashMap<>(); Map<IndexFieldId, LogicalVariable> fieldVarsForNewRecord = new HashMap<>(); /* * if the index is enforcing field types (For open indexes), We add a cast * operator to ensure type safety */ try { if (primaryIndexModificationOp.getOperation() == Kind.INSERT || primaryIndexModificationOp.getOperation() == Kind.UPSERT /* Actually, delete should not be here but it is now until issue * https://issues.apache.org/jira/browse/ASTERIXDB-1507 * is solved */ || primaryIndexModificationOp.getOperation() == Kind.DELETE) { injectFieldAccessesForIndexes(context, dataset, indexes, fieldVarsForNewRecord, recType, metaType, newRecordVar, newMetaVar, primaryIndexModificationOp, false); if (replicateOp != null) { context.computeAndSetTypeEnvironmentForOperator(replicateOp); } } if (primaryIndexModificationOp.getOperation() == Kind.UPSERT /* Actually, delete should be here but it is not until issue * https://issues.apache.org/jira/browse/ASTERIXDB-1507 * is solved */) { List<LogicalVariable> beforeOpMetaVars = primaryIndexModificationOp .getBeforeOpAdditionalNonFilteringVars(); LogicalVariable beforeOpMetaVar = beforeOpMetaVars == null ? null : beforeOpMetaVars.get(0); currentTop = injectFieldAccessesForIndexes(context, dataset, indexes, fieldVarsForBeforeOperation, recType, metaType, primaryIndexModificationOp.getBeforeOpRecordVar(), beforeOpMetaVar, currentTop, true); } } catch (AsterixException e) { throw new AlgebricksException(e); } // Iterate each secondary index and applying Index Update operations. // At first, op1 is the index insert op insertOp for (Index index : indexes) { if (!index.isSecondaryIndex()) { continue; } hasSecondaryIndex = true; // Get the secondary fields names and types List<List<String>> secondaryKeyFields = index.getKeyFieldNames(); List<LogicalVariable> secondaryKeyVars = new ArrayList<>(); List<Mutable<ILogicalExpression>> secondaryExpressions = new ArrayList<>(); List<Mutable<ILogicalExpression>> beforeOpSecondaryExpressions = new ArrayList<>(); ILogicalOperator replicateOutput; for (int i = 0; i < secondaryKeyFields.size(); i++) { IndexFieldId indexFieldId = new IndexFieldId(index.getKeyFieldSourceIndicators().get(i), secondaryKeyFields.get(i)); LogicalVariable skVar = fieldVarsForNewRecord.get(indexFieldId); secondaryKeyVars.add(skVar); secondaryExpressions .add(new MutableObject<ILogicalExpression>(new VariableReferenceExpression(skVar))); if (primaryIndexModificationOp.getOperation() == Kind.UPSERT) { beforeOpSecondaryExpressions.add(new MutableObject<ILogicalExpression>( new VariableReferenceExpression(fieldVarsForBeforeOperation.get(indexFieldId)))); } } IndexInsertDeleteUpsertOperator indexUpdate; if (index.getIndexType() != IndexType.RTREE) { // Create an expression per key Mutable<ILogicalExpression> filterExpression = (primaryIndexModificationOp .getOperation() == Kind.UPSERT) ? null : createFilterExpression(secondaryKeyVars, context.getOutputTypeEnvironment(currentTop), index.isEnforcingKeyFileds()); AqlIndex dataSourceIndex = new AqlIndex(index, dataverseName, datasetName, mp); // Introduce the TokenizeOperator only when doing bulk-load, // and index type is keyword or n-gram. if (index.getIndexType() != IndexType.BTREE && primaryIndexModificationOp.isBulkload()) { // Note: Bulk load case, we don't need to take care of it for upsert operation // Check whether the index is length-partitioned or not. // If partitioned, [input variables to TokenizeOperator, // token, number of token] pairs will be generated and // fed into the IndexInsertDeleteOperator. // If not, [input variables, token] pairs will be generated // and fed into the IndexInsertDeleteOperator. // Input variables are passed since TokenizeOperator is not an // filtering operator. boolean isPartitioned = index.getIndexType() == IndexType.LENGTH_PARTITIONED_WORD_INVIX || index.getIndexType() == IndexType.LENGTH_PARTITIONED_NGRAM_INVIX; // Create a new logical variable - token List<LogicalVariable> tokenizeKeyVars = new ArrayList<>(); List<Mutable<ILogicalExpression>> tokenizeKeyExprs = new ArrayList<>(); LogicalVariable tokenVar = context.newVar(); tokenizeKeyVars.add(tokenVar); tokenizeKeyExprs .add(new MutableObject<ILogicalExpression>(new VariableReferenceExpression(tokenVar))); // Check the field type of the secondary key. IAType secondaryKeyType; Pair<IAType, Boolean> keyPairType = Index.getNonNullableOpenFieldType( index.getKeyFieldTypes().get(0), secondaryKeyFields.get(0), recType); secondaryKeyType = keyPairType.first; List<Object> varTypes = new ArrayList<>(); varTypes.add(NonTaggedFormatUtil.getTokenType(secondaryKeyType)); // If the index is a length-partitioned, then create // additional variable - number of token. // We use a special type for the length-partitioned index. // The type is short, and this does not contain type info. if (isPartitioned) { LogicalVariable lengthVar = context.newVar(); tokenizeKeyVars.add(lengthVar); tokenizeKeyExprs.add( new MutableObject<ILogicalExpression>(new VariableReferenceExpression(lengthVar))); varTypes.add(BuiltinType.SHORTWITHOUTTYPEINFO); } // TokenizeOperator to tokenize [SK, PK] pairs TokenizeOperator tokenUpdate = new TokenizeOperator(dataSourceIndex, primaryIndexModificationOp.getPrimaryKeyExpressions(), secondaryExpressions, tokenizeKeyVars, filterExpression, primaryIndexModificationOp.getOperation(), primaryIndexModificationOp.isBulkload(), isPartitioned, varTypes); tokenUpdate.getInputs().add(new MutableObject<ILogicalOperator>(currentTop)); context.computeAndSetTypeEnvironmentForOperator(tokenUpdate); replicateOutput = tokenUpdate; indexUpdate = new IndexInsertDeleteUpsertOperator(dataSourceIndex, primaryIndexModificationOp.getPrimaryKeyExpressions(), tokenizeKeyExprs, filterExpression, primaryIndexModificationOp.getOperation(), primaryIndexModificationOp.isBulkload(), primaryIndexModificationOp.getAdditionalNonFilteringExpressions() == null ? 0 : primaryIndexModificationOp.getAdditionalNonFilteringExpressions().size()); indexUpdate.setAdditionalFilteringExpressions(filteringExpressions); indexUpdate.getInputs().add(new MutableObject<ILogicalOperator>(tokenUpdate)); } else { // When TokenizeOperator is not needed indexUpdate = new IndexInsertDeleteUpsertOperator(dataSourceIndex, primaryIndexModificationOp.getPrimaryKeyExpressions(), secondaryExpressions, filterExpression, primaryIndexModificationOp.getOperation(), primaryIndexModificationOp.isBulkload(), primaryIndexModificationOp.getAdditionalNonFilteringExpressions() == null ? 0 : primaryIndexModificationOp.getAdditionalNonFilteringExpressions().size()); indexUpdate.setAdditionalFilteringExpressions(filteringExpressions); replicateOutput = indexUpdate; // We add the necessary expressions for upsert if (primaryIndexModificationOp.getOperation() == Kind.UPSERT) { indexUpdate.setBeforeOpSecondaryKeyExprs(beforeOpSecondaryExpressions); if (filteringFields != null) { indexUpdate.setBeforeOpAdditionalFilteringExpression( new MutableObject<ILogicalExpression>(new VariableReferenceExpression( primaryIndexModificationOp.getBeforeOpFilterVar()))); } } indexUpdate.getInputs().add(new MutableObject<ILogicalOperator>(currentTop)); } } else { // Get type, dimensions and number of keys Pair<IAType, Boolean> keyPairType = Index.getNonNullableOpenFieldType( index.getKeyFieldTypes().get(0), secondaryKeyFields.get(0), recType); IAType spatialType = keyPairType.first; boolean isPointMBR = spatialType.getTypeTag() == ATypeTag.POINT || spatialType.getTypeTag() == ATypeTag.POINT3D; int dimension = NonTaggedFormatUtil.getNumDimensions(spatialType.getTypeTag()); int numKeys = (isPointMBR && isBulkload) ? dimension : dimension * 2; // Get variables and expressions List<LogicalVariable> keyVarList = new ArrayList<>(); List<Mutable<ILogicalExpression>> keyExprList = new ArrayList<>(); for (int i = 0; i < numKeys; i++) { LogicalVariable keyVar = context.newVar(); keyVarList.add(keyVar); AbstractFunctionCallExpression createMBR = new ScalarFunctionCallExpression( FunctionUtil.getFunctionInfo(AsterixBuiltinFunctions.CREATE_MBR)); createMBR.getArguments().add(new MutableObject<ILogicalExpression>( new VariableReferenceExpression(secondaryKeyVars.get(0)))); createMBR.getArguments().add(new MutableObject<ILogicalExpression>( new ConstantExpression(new AsterixConstantValue(new AInt32(dimension))))); createMBR.getArguments().add(new MutableObject<ILogicalExpression>( new ConstantExpression(new AsterixConstantValue(new AInt32(i))))); keyExprList.add(new MutableObject<ILogicalExpression>(createMBR)); } secondaryExpressions.clear(); for (LogicalVariable secondaryKeyVar : keyVarList) { secondaryExpressions.add(new MutableObject<ILogicalExpression>( new VariableReferenceExpression(secondaryKeyVar))); } if (isPointMBR && isBulkload) { //for PointMBR optimization: see SecondaryRTreeOperationsHelper.buildLoadingJobSpec() and //createFieldPermutationForBulkLoadOp(int) for more details. for (LogicalVariable secondaryKeyVar : keyVarList) { secondaryExpressions.add(new MutableObject<ILogicalExpression>( new VariableReferenceExpression(secondaryKeyVar))); } } AssignOperator assignCoordinates = new AssignOperator(keyVarList, keyExprList); assignCoordinates.getInputs().add(new MutableObject<ILogicalOperator>(currentTop)); context.computeAndSetTypeEnvironmentForOperator(assignCoordinates); replicateOutput = assignCoordinates; Mutable<ILogicalExpression> filterExpression = null; AssignOperator originalAssignCoordinates = null; // We do something similar for beforeOp key if the operation is an upsert if (primaryIndexModificationOp.getOperation() == Kind.UPSERT) { List<LogicalVariable> originalKeyVarList = new ArrayList<>(); List<Mutable<ILogicalExpression>> originalKeyExprList = new ArrayList<>(); // we don't do any filtering since nulls are expected here and there for (int i = 0; i < numKeys; i++) { LogicalVariable keyVar = context.newVar(); originalKeyVarList.add(keyVar); AbstractFunctionCallExpression createMBR = new ScalarFunctionCallExpression( FunctionUtil.getFunctionInfo(AsterixBuiltinFunctions.CREATE_MBR)); createMBR.getArguments().add(beforeOpSecondaryExpressions.get(0)); createMBR.getArguments().add(new MutableObject<ILogicalExpression>( new ConstantExpression(new AsterixConstantValue(new AInt32(dimension))))); createMBR.getArguments().add(new MutableObject<ILogicalExpression>( new ConstantExpression(new AsterixConstantValue(new AInt32(i))))); originalKeyExprList.add(new MutableObject<ILogicalExpression>(createMBR)); } beforeOpSecondaryExpressions.clear(); for (LogicalVariable secondaryKeyVar : originalKeyVarList) { beforeOpSecondaryExpressions.add(new MutableObject<ILogicalExpression>( new VariableReferenceExpression(secondaryKeyVar))); } originalAssignCoordinates = new AssignOperator(originalKeyVarList, originalKeyExprList); originalAssignCoordinates.getInputs() .add(new MutableObject<ILogicalOperator>(assignCoordinates)); context.computeAndSetTypeEnvironmentForOperator(originalAssignCoordinates); } else { // We must enforce the filter if the originating spatial type is // nullable. boolean forceFilter = keyPairType.second; filterExpression = createFilterExpression(keyVarList, context.getOutputTypeEnvironment(assignCoordinates), forceFilter); } AqlIndex dataSourceIndex = new AqlIndex(index, dataverseName, datasetName, mp); indexUpdate = new IndexInsertDeleteUpsertOperator(dataSourceIndex, primaryIndexModificationOp.getPrimaryKeyExpressions(), secondaryExpressions, filterExpression, primaryIndexModificationOp.getOperation(), primaryIndexModificationOp.isBulkload(), primaryIndexModificationOp.getAdditionalNonFilteringExpressions() == null ? 0 : primaryIndexModificationOp.getAdditionalNonFilteringExpressions().size()); indexUpdate.setAdditionalFilteringExpressions(filteringExpressions); if (primaryIndexModificationOp.getOperation() == Kind.UPSERT) { // set before op secondary key expressions if (filteringFields != null) { indexUpdate.setBeforeOpAdditionalFilteringExpression( new MutableObject<ILogicalExpression>(new VariableReferenceExpression( primaryIndexModificationOp.getBeforeOpFilterVar()))); } // set filtering expressions indexUpdate.setBeforeOpSecondaryKeyExprs(beforeOpSecondaryExpressions); // assign --> assign beforeOp values --> secondary index upsert indexUpdate.getInputs().add(new MutableObject<ILogicalOperator>(originalAssignCoordinates)); } else { indexUpdate.getInputs().add(new MutableObject<ILogicalOperator>(assignCoordinates)); } } context.computeAndSetTypeEnvironmentForOperator(indexUpdate); if (!primaryIndexModificationOp.isBulkload() || secondaryIndexTotalCnt == 1) { currentTop = indexUpdate; } else { replicateOp.getOutputs().add(new MutableObject<>(replicateOutput)); } if (primaryIndexModificationOp.isBulkload()) { // For bulk load, we connect all fanned out insert operator to a single SINK operator op0.getInputs().add(new MutableObject<ILogicalOperator>(indexUpdate)); } } if (!hasSecondaryIndex) { return false; } if (!primaryIndexModificationOp.isBulkload()) { // If this is an upsert, we need to // Remove the current input to the SINK operator (It is actually already removed above) op0.getInputs().clear(); // Connect the last index update to the SINK op0.getInputs().add(new MutableObject<ILogicalOperator>(currentTop)); } return true; }