Index index = foreignKey.getReferencingIndex();
IndexRowType indexRowType = plan.schema.indexRowType(index);
List<TPreparedExpression> vars = new ArrayList<>(plan.ncols);
for (int i = 0; i < plan.ncols; i++) {
// Convert from index column position to parameter number.
Column indexedColumn = crossReferencedColumns.get(i);
int fkpos = referencedColumns.indexOf(indexedColumn);
vars.add(new TPreparedParameter(fkpos, indexedColumn.getType()));
}
UnboundExpressions indexExprs = new RowBasedUnboundExpressions(indexRowType, vars);
IndexBound indexBound = new IndexBound(indexExprs, plan);
IndexKeyRange indexKeyRange = IndexKeyRange.bounded(indexRowType, indexBound, true, indexBound, true);
input = API.indexScan_Default(indexRowType, indexKeyRange, 1);
input = API.groupLookup_Default(input, group, indexRowType,
Collections.singletonList(tableRowType),
API.InputPreservationOption.DISCARD_INPUT,
groupLookupPipelineQuantum);
}
else {
// referencing WHERE fk IS NOT NULL AND...
List<Column> referencingColumns = foreignKey.getReferencingColumns();
TPreptimeValue emptyTPV = new TPreptimeValue();
TValidatedScalar isNull = typesRegistryService.getScalarsResolver().get("IsNull", Collections.nCopies(1, emptyTPV)).getOverload();
TValidatedScalar not = typesRegistryService.getScalarsResolver().get("NOT", Collections.nCopies(1, emptyTPV)).getOverload();
TValidatedScalar and = typesRegistryService.getScalarsResolver().get("AND", Collections.nCopies(2, emptyTPV)).getOverload();
TInstance boolType = AkBool.INSTANCE.instance(false);
TPreparedExpression predicate = null;
for (int i = 0; i < plan.ncols; i++) {
Column referencingColumn = referencingColumns.get(i);
TPreparedField field = new TPreparedField(referencingColumn.getType(), referencingColumn.getPosition());
TPreparedExpression clause = new TPreparedFunction(isNull, boolType, Arrays.asList(field));
clause = new TPreparedFunction(not, boolType, Arrays.asList(clause));
if (predicate == null) {
predicate = clause;
}
else {
predicate = new TPreparedFunction(and, boolType, Arrays.asList(predicate, clause));
}
}
input = API.groupScan_Default(group);
input = API.filter_Default(input, Collections.singletonList(tableRowType));
input = API.select_HKeyOrdered(input, tableRowType, predicate);
}
ForeignKey.Action action;
takeAction: {
if (hasNewRow) {
action = foreignKey.getUpdateAction();
}
else {
action = foreignKey.getDeleteAction();
if (action == ForeignKey.Action.CASCADE) {
// DELETE FROM referencing ...
plan.plannable = API.delete_Default(input);
break takeAction;
}
}
// UPDATE referencing SET fk = $2 ...
switch (action) {
case SET_NULL:
case SET_DEFAULT:
plan.bindValues = new ValueSource[plan.ncols];
for (int i = 0; i < plan.ncols; i++) {
Column column = foreignKey.getReferencingColumns().get(i);
plan.bindValues[i] = ValueSources.fromObject((action == ForeignKey.Action.SET_NULL) ?
null :
column.getDefaultValue(),
column.getType()).value();
}
break;
case CASCADE:
plan.bindNewRow = true;
break;