/** * Constructs the element iff requested, saves the rule reference iff requested, and clones and * saves the bindingSet iff requested. The requested behavior depends on {@link * IJoinNexus#solutionFlags()}. When requested, the element is created using {@link * IRelation#newElement(IPredicate, IBindingSet)} for the {@link IRelation} that is named by the * head of the rule. * * @param e The element. * @param rule The rule. * @param bindingSet The binding set for this solution. If {@link IJoinNexus#BINDINGS} was * specified, then the binding set will be <em>cloned</em> by this ctor. * @throws IllegalArgumentException if any parameter is <code>null</code>. */ @SuppressWarnings("unchecked") public Solution(final IJoinNexus joinNexus, final IRule<E> rule, final IBindingSet bindingSet) { if (joinNexus == null) throw new IllegalArgumentException(); if (rule == null) throw new IllegalArgumentException(); if (bindingSet == null) throw new IllegalArgumentException(); final int flags = joinNexus.solutionFlags(); if ((flags & IJoinNexus.ELEMENT) != 0) { /* * The relation is responsible for how the elements are materialized * from the bindings. * * Note: Caching for this relation is very important! */ // the head of the rule. final IPredicate head = rule.getHead(); // the relation for the head of the rule. final IRelation relation = joinNexus.getHeadRelationView(head); // use the relation's element factory. this.e = (E) relation.newElement(head.args(), bindingSet); // TODO BOp.args() is not efficient! // // use the relation's element factory. // this.e = (E) relation.newElement(head, bindingSet); } else { this.e = null; } if ((flags & IJoinNexus.BINDINGS) != 0) { this.bindingSet = bindingSet.clone(); } else { this.bindingSet = null; } if ((flags & IJoinNexus.RULE) != 0) { this.rule = rule; } else { this.rule = null; } }
/** * The head relation is what we write on for mutation operations and is also responsible for * minting new elements from computed {@link ISolution}s. This method depends solely on the name * of the head relation and the timestamp of interest for the view. */ public IRelation getHeadRelationView(final IPredicate pred) { if (pred.getRelationCount() != 1) throw new IllegalArgumentException(); final String relationName = pred.getOnlyRelationName(); final long timestamp = (getAction().isMutation() ? getWriteTimestamp() : getReadTimestamp(/*relationName*/ )); return (IRelation<?>) resourceLocator.locate(relationName, timestamp); }
public Iterator<PartitionLocator> locatorScan( final AbstractScaleOutFederation<?> fed, final IPredicate<?> predicate) { final long timestamp = getReadTimestamp(); // Note: assumes that we are NOT using a view of two relations. final IRelation<?> relation = (IRelation<?>) fed.getResourceLocator().locate(predicate.getOnlyRelationName(), timestamp); /* * Find the best access path for the predicate for that relation. * * Note: All we really want is the [fromKey] and [toKey] for that * predicate and index. This MUST NOT layer on expanders since the * layering also hides the [fromKey] and [toKey]. */ @SuppressWarnings("unchecked") final AccessPath<?> accessPath = (AccessPath<?>) relation.getAccessPath((IPredicate) predicate); // Note: assumes scale-out (EDS or JDS). final IClientIndex ndx = (IClientIndex) accessPath.getIndex(); /* * Note: could also be formed from relationName + "." + * keyOrder.getIndexName(), which is cheaper unless the index metadata * is cached. */ final String name = ndx.getIndexMetadata().getName(); return fed.locatorScan( name, timestamp, accessPath.getFromKey(), accessPath.getToKey(), false /* reverse */); }
@SuppressWarnings("unchecked") private final void copyValues( final IElement e, final IPredicate<?> pred, final IBindingSet bindingSet) { for (int i = 0; i < pred.arity(); i++) { final IVariableOrConstant<?> t = pred.get(i); if (t.isVar()) { final IVariable<?> var = (IVariable<?>) t; final Constant<?> newval = new Constant(e.get(i)); bindingSet.set(var, newval); } } }
@SuppressWarnings("unchecked") public IRelation getTailRelationView(final IPredicate pred) { final int nsources = pred.getRelationCount(); if (nsources == 1) { return (IRelation) resourceLocator.locate(pred.getOnlyRelationName(), getReadTimestamp()); } else if (nsources == 2) { final IRelation<?> relation0 = (IRelation) resourceLocator.locate(pred.getRelationName(0), readTimestamp); final IRelation<?> relation1 = (IRelation) resourceLocator.locate(pred.getRelationName(1), readTimestamp); return new RelationFusedView(relation0, relation1).init(); } else { throw new UnsupportedOperationException(); } }
/** Do a hash join of the buffered solutions with the access path. */ private void doHashJoin() { if (state.isEmpty()) return; final IBindingSetAccessPath<?> accessPath = getAccessPath(); if (log.isInfoEnabled()) log.info("accessPath=" + accessPath); stats.accessPathCount.increment(); stats.accessPathRangeCount.add(accessPath.rangeCount(false /* exact */)); final UnsyncLocalOutputBuffer<IBindingSet> unsyncBuffer = new UnsyncLocalOutputBuffer<IBindingSet>(op.getChunkCapacity(), sink); final long cutoffLimit = pred.getProperty( IPredicate.Annotations.CUTOFF_LIMIT, IPredicate.Annotations.DEFAULT_CUTOFF_LIMIT); // Obtain the iterator for the current join dimension. final ICloseableIterator<IBindingSet[]> itr = accessPath.solutions(cutoffLimit, stats); /* * Note: The [stats] are NOT passed in here since the chunksIn and * unitsIn were updated when the pipeline solutions were accepted * into the hash index. If we passed in stats here, they would be * double counted when we executed the hash join against the access * path. */ state.hashJoin( itr, // left null, // stats unsyncBuffer // out ); switch (state.getJoinType()) { case Normal: /* * Nothing to do. */ break; case Optional: case NotExists: { /* * Output the optional solutions. */ // where to write the optional solutions. final AbstractUnsynchronizedArrayBuffer<IBindingSet> unsyncBuffer2 = sink2 == null ? unsyncBuffer : new UnsyncLocalOutputBuffer<IBindingSet>(op.getChunkCapacity(), sink2); state.outputOptionals(unsyncBuffer2); unsyncBuffer2.flush(); if (sink2 != null) sink2.flush(); break; } case Exists: { /* * Output the join set. */ state.outputJoinSet(unsyncBuffer); break; } default: throw new AssertionError(); } unsyncBuffer.flush(); sink.flush(); }