use of org.exist.dom.persistent.DocumentSet in project exist by eXist-db.
the class RootNode method eval.
public Sequence eval(Sequence contextSequence, Item contextItem) throws XPathException {
if (context.getProfiler().isEnabled()) {
context.getProfiler().start(this);
context.getProfiler().message(this, Profiler.DEPENDENCIES, "DEPENDENCIES", Dependency.getDependenciesName(this.getDependencies()));
if (contextSequence != null) {
context.getProfiler().message(this, Profiler.START_SEQUENCES, "CONTEXT SEQUENCE", contextSequence);
}
if (contextItem != null) {
context.getProfiler().message(this, Profiler.START_SEQUENCES, "CONTEXT ITEM", contextItem.toSequence());
}
}
// first, if we have been explicitly given a context item or context sequence, we can just use that
if (contextItem != null) {
return new ValueSequence(contextItem);
} else if (contextSequence != null && contextSequence != Sequence.EMPTY_SEQUENCE) {
return contextSequence;
}
// second, check if a context item is declared
final ContextItemDeclaration decl = context.getContextItemDeclartion();
if (decl != null) {
final Sequence seq = decl.eval(null, null);
if (!seq.isEmpty()) {
final Item item = seq.itemAt(0);
// context item must be a node
if (!Type.subTypeOf(item.getType(), Type.NODE)) {
throw new XPathException(this, ErrorCodes.XPTY0020, "Context item is not a node");
}
final NodeValue node = (NodeValue) item;
// return fn:root(self::node()) treat as document-node()
if (node.getImplementationType() == NodeValue.PERSISTENT_NODE) {
return new NodeProxy(((NodeProxy) item).getOwnerDocument());
} else {
if (node.getType() == Type.DOCUMENT) {
return node;
}
return (org.exist.dom.memtree.DocumentImpl) node.getOwnerDocument();
}
}
return Sequence.EMPTY_SEQUENCE;
}
// get statically known documents from the context
DocumentSet ds = context.getStaticallyKnownDocuments();
if (ds == null || ds.getDocumentCount() == 0) {
return Sequence.EMPTY_SEQUENCE;
}
// fix for util:eval-with-context
if (contextSequence != null) {
if (!contextSequence.isEmpty()) {
final Item item = contextSequence.itemAt(0);
// context item must be a node
if (!Type.subTypeOf(item.getType(), Type.NODE)) {
throw new XPathException(this, ErrorCodes.XPTY0020, "Context item is not a node");
}
final NodeValue node = (NodeValue) item;
// return fn:root(self::node()) treat as document-node()
if (node.getImplementationType() == NodeValue.PERSISTENT_NODE) {
return new NodeProxy(((NodeProxy) item).getOwnerDocument());
} else {
if (node.getType() == Type.DOCUMENT) {
return node;
}
return (org.exist.dom.memtree.DocumentImpl) node.getOwnerDocument();
}
} else {
return Sequence.EMPTY_SEQUENCE;
}
}
// // if the expression occurs in a nested context, we might have cached the
// // document set
// // TODO: disabled cache for now as it may cause concurrency issues
// // better use compile-time inspection and maybe a pragma to mark those
// // sections in the query that can be safely cached
// if (cachedDocs != null && cachedDocs.equalDocs(ds)) return cached;
// check if the loaded documents should remain locked
NewArrayNodeSet result = new NewArrayNodeSet();
// NOTE(AR) locking the documents here does not actually do anything useful, eXist-db will still exhibit weak isolation with concurrent updates
// ManagedLocks<ManagedDocumentLock> docLocks = null;
// try {
// // wait for pending updates
// if (!context.inProtectedMode()) {
// docLocks = ds.lock(context.getBroker(), false);
// }
DocumentImpl doc;
for (final Iterator<DocumentImpl> i = ds.getDocumentIterator(); i.hasNext(); ) {
doc = i.next();
if (context.inProtectedMode() && !context.getProtectedDocs().containsKey(doc.getDocId())) {
continue;
}
if (doc.getResourceType() == DocumentImpl.XML_FILE) {
// skip binary resources
result.add(new NodeProxy(doc));
}
}
cached = result;
cachedDocs = ds;
// result.updateNoSort();
if (context.getProfiler().isEnabled()) {
context.getProfiler().end(this, "", result);
}
registerUpdateListener();
return result;
}
use of org.exist.dom.persistent.DocumentSet in project exist by eXist-db.
the class PathExpr method eval.
@Override
public Sequence eval(Sequence contextSequence, final Item contextItem) throws XPathException {
if (context.getProfiler().isEnabled()) {
context.getProfiler().start(this);
context.getProfiler().message(this, Profiler.DEPENDENCIES, "DEPENDENCIES", Dependency.getDependenciesName(this.getDependencies()));
if (contextSequence != null) {
context.getProfiler().message(this, Profiler.START_SEQUENCES, "CONTEXT SEQUENCE", contextSequence);
}
if (contextItem != null) {
context.getProfiler().message(this, Profiler.START_SEQUENCES, "CONTEXT ITEM", contextItem.toSequence());
}
}
if (contextItem != null) {
contextSequence = contextItem.toSequence();
}
Sequence result = null;
if (steps.size() == 0) {
result = Sequence.EMPTY_SEQUENCE;
} else {
// we will filter out nodes from the contextSequence
result = contextSequence;
Sequence currentContext = contextSequence;
DocumentSet contextDocs = null;
Expression expr = steps.get(0);
if (expr instanceof VariableReference) {
final Variable var = ((VariableReference) expr).getVariable(new AnalyzeContextInfo(parent, 0));
// TOUNDERSTAND : how null could be possible here ? -pb
if (var != null) {
contextDocs = var.getContextDocs();
}
}
// contextDocs == null *is* significant
setContextDocSet(contextDocs);
// To prevent processing nodes after atomic values...
// TODO : let the parser do it ? -pb
boolean gotAtomicResult = false;
Expression prev = null;
for (Expression step : steps) {
prev = expr;
expr = step;
context.getWatchDog().proceed(expr);
// TODO : maybe this could be detected by the parser ? -pb
if (gotAtomicResult && !Type.subTypeOf(expr.returnsType(), Type.NODE) && // Ugly workaround to allow preceding *text* nodes.
!(expr instanceof EnclosedExpr)) {
throw new XPathException(this, ErrorCodes.XPTY0019, "left operand of '/' must be a node. Got '" + Type.getTypeName(result.getItemType()) + " " + result.getCardinality().getHumanDescription() + "'");
}
// contextDocs == null *is* significant
expr.setContextDocSet(contextDocs);
// switch into single step mode if we are processing in-memory nodes only
final boolean inMemProcessing = currentContext != null && Type.subTypeOf(currentContext.getItemType(), Type.NODE) && !currentContext.isPersistentSet();
// DESIGN : first test the dependency then the result
final int exprDeps = expr.getDependencies();
if (inMemProcessing || ((Dependency.dependsOn(exprDeps, Dependency.CONTEXT_ITEM) || Dependency.dependsOn(exprDeps, Dependency.CONTEXT_POSITION)) && // TODO : reconsider since that may be expensive (type evaluation)
!(this instanceof Predicate && Type.subTypeOfUnion(this.returnsType(), Type.NUMBER)) && currentContext != null && !currentContext.isEmpty())) {
Sequence exprResult = new ValueSequence(Type.subTypeOf(expr.returnsType(), Type.NODE));
((ValueSequence) exprResult).keepUnOrdered(unordered);
// Restore a position which may have been modified by inner expressions
int p = context.getContextPosition();
final Sequence seq = context.getContextSequence();
for (final SequenceIterator iterInner = currentContext.iterate(); iterInner.hasNext(); p++) {
context.setContextSequencePosition(p, seq);
context.getWatchDog().proceed(expr);
final Item current = iterInner.nextItem();
// 0 or 1 item
if (!currentContext.hasMany()) {
exprResult = expr.eval(currentContext, current);
} else {
exprResult.addAll(expr.eval(currentContext, current));
}
}
result = exprResult;
} else {
try {
result = expr.eval(currentContext);
} catch (XPathException ex) {
// enrich exception when information is available
if (ex.getLine() < 1 || ex.getColumn() < 1) {
ex.setLocation(expr.getLine(), expr.getColumn());
}
throw ex;
}
}
// well, no so stupid I think...
if (result != null) {
if (steps.size() > 1 && !(result instanceof VirtualNodeSet) && !(expr instanceof EnclosedExpr) && !result.isEmpty() && !Type.subTypeOf(result.getItemType(), Type.NODE)) {
gotAtomicResult = true;
}
if (steps.size() > 1 && getLastExpression() instanceof Step) {
// remove duplicate nodes if this is a path
// expression with more than one step
result.removeDuplicates();
}
}
if (!staticContext) {
currentContext = result;
}
}
final boolean allowMixedNodesInReturn;
if (prev != null) {
allowMixedNodesInReturn = prev.allowMixedNodesInReturn() | expr.allowMixedNodesInReturn();
} else {
allowMixedNodesInReturn = expr.allowMixedNodesInReturn();
}
if (gotAtomicResult && result != null && !allowMixedNodesInReturn && !Type.subTypeOf(result.getItemType(), Type.ATOMIC)) {
throw new XPathException(this, ErrorCodes.XPTY0018, "Cannot mix nodes and atomic values in the result of a path expression.");
}
}
if (context.getProfiler().isEnabled()) {
context.getProfiler().end(this, "", result);
}
return result;
}
use of org.exist.dom.persistent.DocumentSet in project exist by eXist-db.
the class Eval method doEval.
private Sequence doEval(final XQueryContext evalContext, final Sequence contextSequence, final Sequence[] args) throws XPathException {
if (evalContext.getProfiler().isEnabled()) {
evalContext.getProfiler().start(this);
evalContext.getProfiler().message(this, Profiler.DEPENDENCIES, "DEPENDENCIES", Dependency.getDependenciesName(this.getDependencies()));
if (contextSequence != null) {
evalContext.getProfiler().message(this, Profiler.START_SEQUENCES, "CONTEXT SEQUENCE", contextSequence);
}
}
int argCount = 0;
Sequence exprContext = null;
if (isCalledAs(FS_EVAL_INLINE_NAME)) {
// the current expression context
exprContext = args[argCount++];
}
// get the query expression
final Item expr = args[argCount++].itemAt(0);
final Source querySource;
if (Type.subTypeOf(expr.getType(), Type.ANY_URI)) {
querySource = loadQueryFromURI(expr);
} else {
final String queryStr = expr.getStringValue();
if (queryStr.trim().isEmpty()) {
return new EmptySequence();
}
querySource = new StringSource(queryStr);
}
final NodeValue contextInit;
if (isCalledAs(FS_EVAL_WITH_CONTEXT_NAME)) {
// set the context initialization param for later use
contextInit = (NodeValue) args[argCount++].itemAt(0);
} else {
contextInit = null;
}
// should the compiled query be cached?
final boolean cache;
if (isCalledAs(FS_EVAL_AND_SERIALIZE_NAME)) {
cache = true;
} else if (argCount < getArgumentCount()) {
cache = ((BooleanValue) args[argCount++].itemAt(0)).effectiveBooleanValue();
} else {
cache = false;
}
// save some context properties
evalContext.pushNamespaceContext();
final LocalVariable mark = evalContext.markLocalVariables(false);
// save the static document set of the current context, so it can be restored later
final DocumentSet oldDocs = evalContext.getStaticDocs();
if (exprContext != null) {
evalContext.setStaticallyKnownDocuments(exprContext.getDocumentSet());
}
if (evalContext.isProfilingEnabled(2)) {
evalContext.getProfiler().start(this, "eval: " + expr);
}
// fixme! - hook for debugger here /ljo
final XQuery xqueryService = evalContext.getBroker().getBrokerPool().getXQueryService();
final XQueryContext innerContext;
final Sequence initContextSequence;
if (contextInit != null) {
// eval-with-context: initialize a new context
innerContext = new XQueryContext(context.getBroker().getBrokerPool());
initContextSequence = initContext(contextInit.getNode(), innerContext);
} else {
// use the existing outer context
// TODO: check if copying the static context would be sufficient???
innerContext = evalContext.copyContext();
innerContext.setShared(true);
// innerContext = context;
initContextSequence = null;
}
// set module load path
if (Type.subTypeOf(expr.getType(), Type.ANY_URI)) {
String uri = null;
if (querySource instanceof DBSource) {
final XmldbURI documentPath = ((DBSource) querySource).getDocumentPath();
uri = XmldbURI.EMBEDDED_SERVER_URI.append(documentPath).removeLastSegment().toString();
} else if (querySource instanceof FileSource) {
uri = ((FileSource) querySource).getPath().getParent().toString();
}
if (uri != null) {
innerContext.setModuleLoadPath(uri);
}
}
// bind external vars?
if (isCalledAs(FS_EVAL_NAME) && getArgumentCount() >= 3) {
final Sequence externalVars = args[argCount++];
for (int i = 0; i < externalVars.getItemCount(); i++) {
final Item varName = externalVars.itemAt(i);
if (varName.getType() == Type.QNAME) {
final Item varValue = externalVars.itemAt(++i);
innerContext.declareVariable(((QNameValue) varName).getQName(), varValue);
}
}
}
// determine if original line/column number are passed on
final boolean pass;
if (isCalledAs(FS_EVAL_NAME) && getArgumentCount() == 4) {
pass = args[3].itemAt(0).toJavaObject(Boolean.class);
} else if (isCalledAs(FS_EVAL_WITH_CONTEXT_NAME) && getArgumentCount() == 5) {
pass = args[4].itemAt(0).toJavaObject(Boolean.class);
} else if (isCalledAs(FS_EVAL_INLINE_NAME) && getArgumentCount() == 4) {
pass = args[3].itemAt(0).toJavaObject(Boolean.class);
} else if (isCalledAs(FS_EVAL_AND_SERIALIZE_NAME) && getArgumentCount() == 5) {
pass = args[4].itemAt(0).toJavaObject(Boolean.class);
} else {
// default
pass = false;
}
// fixme! - hook for debugger here /ljo
try {
if (isCalledAs(FS_EVAL_WITH_CONTEXT_NAME) && getArgumentCount() >= 4) {
final Item contextItem = args[argCount++].itemAt(0);
if (contextItem != null) {
// TODO : sort this out
if (exprContext != null) {
LOG.warn("exprContext and contextItem are not null");
}
exprContext = contextItem.toSequence();
}
}
if (initContextSequence != null) {
exprContext = initContextSequence;
}
Sequence result = null;
try {
if (!isCalledAs(FS_EVAL_AND_SERIALIZE_NAME)) {
result = execute(evalContext.getBroker(), xqueryService, querySource, innerContext, exprContext, cache, null);
return result;
} else {
// get the default serialization options
final Properties defaultOutputOptions;
if (getArgumentCount() >= 2 && !args[1].isEmpty()) {
defaultOutputOptions = FunSerialize.getSerializationProperties(this, args[1].itemAt(0));
} else {
defaultOutputOptions = new Properties();
}
// execute the query, XQuery prolog serialization options are collected into `xqueryOutputProperties`
final Properties xqueryOutputProperties = new Properties();
result = execute(evalContext.getBroker(), xqueryService, querySource, innerContext, exprContext, cache, xqueryOutputProperties);
// do we need to subsequence the results?
if (getArgumentCount() > 2) {
result = FunSubSequence.subsequence(result, ((DoubleValue) getArgument(2).eval(contextSequence, null).convertTo(Type.DOUBLE)), getArgumentCount() == 3 ? null : ((DoubleValue) getArgument(3).eval(contextSequence, null).convertTo(Type.DOUBLE)));
}
// override the default options with the ones from the xquery prolog
final Properties serializationProperties = new Properties();
serializationProperties.putAll(defaultOutputOptions);
serializationProperties.putAll(xqueryOutputProperties);
// serialize the results
try (final StringWriter writer = new StringWriter()) {
final XQuerySerializer xqSerializer = new XQuerySerializer(context.getBroker(), serializationProperties, writer);
final Sequence seq;
if (xqSerializer.normalize()) {
seq = FunSerialize.normalize(this, context, result);
} else {
seq = result;
}
xqSerializer.serialize(seq);
return new StringValue(writer.toString());
} catch (final IOException | SAXException e) {
throw new XPathException(this, FnModule.SENR0001, e.getMessage());
}
}
} finally {
cleanup(evalContext, innerContext, oldDocs, mark, expr, result);
}
} catch (final XPathException e) {
try {
e.prependMessage("Error while evaluating expression: " + querySource.getContent() + ". ");
} catch (final IOException e1) {
}
if (!pass) {
e.setLocation(line, column);
}
throw e;
}
}
use of org.exist.dom.persistent.DocumentSet in project exist by eXist-db.
the class IndexKeys method eval.
/*
* (non-Javadoc)
*
* @see org.exist.xquery.BasicFunction#eval(org.exist.xquery.value.Sequence[],
* org.exist.xquery.value.Sequence)
*/
public Sequence eval(Sequence[] args, Sequence contextSequence) throws XPathException {
if (args[0].isEmpty()) {
return Sequence.EMPTY_SEQUENCE;
}
NodeSet nodes = null;
DocumentSet docs = null;
Sequence qnames = null;
if (isCalledAs("index-keys-by-qname")) {
qnames = args[0];
docs = contextSequence == null ? context.getStaticallyKnownDocuments() : contextSequence.getDocumentSet();
} else {
nodes = args[0].toNodeSet();
docs = nodes.getDocumentSet();
}
final Sequence result = new ValueSequence();
try (final FunctionReference ref = (FunctionReference) args[2].itemAt(0)) {
int max = -1;
if (args[3].hasOne()) {
max = ((IntegerValue) args[3].itemAt(0)).getInt();
}
// if we have 5 arguments, query the user-specified index
if (this.getArgumentCount() == 5) {
final IndexWorker indexWorker = context.getBroker().getIndexController().getWorkerByIndexName(args[4].itemAt(0).getStringValue());
// IndexWorker indexWorker = context.getBroker().getBrokerPool().getIndexManager().getIndexByName(args[4].itemAt(0).getStringValue()).getWorker();
if (indexWorker == null) {
throw new XPathException(this, "Unknown index: " + args[4].itemAt(0).getStringValue());
}
final Map<String, Object> hints = new HashMap<>();
if (max != -1) {
hints.put(IndexWorker.VALUE_COUNT, new IntegerValue(max));
}
if (indexWorker instanceof OrderedValuesIndex) {
hints.put(OrderedValuesIndex.START_VALUE, args[1].getStringValue());
} else {
logger.warn("{} isn't an instance of org.exist.indexing.OrderedValuesIndex. Start value '{}' ignored.", indexWorker.getClass().getName(), args[1]);
}
if (qnames != null) {
final List<QName> qnameList = new ArrayList<>(qnames.getItemCount());
for (final SequenceIterator i = qnames.iterate(); i.hasNext(); ) {
final QNameValue qv = (QNameValue) i.nextItem();
qnameList.add(qv.getQName());
}
hints.put(QNamedKeysIndex.QNAMES_KEY, qnameList);
}
final Occurrences[] occur = indexWorker.scanIndex(context, docs, nodes, hints);
// TODO : add an extra argument to pass the END_VALUE ?
final int len = (max != -1 && occur.length > max ? max : occur.length);
final Sequence[] params = new Sequence[2];
ValueSequence data = new ValueSequence();
for (int j = 0; j < len; j++) {
params[0] = new StringValue(occur[j].getTerm().toString());
data.add(new IntegerValue(occur[j].getOccurrences(), Type.UNSIGNED_INT));
data.add(new IntegerValue(occur[j].getDocuments(), Type.UNSIGNED_INT));
data.add(new IntegerValue(j + 1, Type.UNSIGNED_INT));
params[1] = data;
result.addAll(ref.evalFunction(Sequence.EMPTY_SEQUENCE, null, params));
data.clear();
}
// no index specified: use the range index
} else {
final Indexable indexable = (Indexable) args[1].itemAt(0);
ValueOccurrences[] occur = null;
// First check for indexes defined on qname
final QName[] allQNames = getDefinedIndexes(context.getBroker(), docs);
if (allQNames.length > 0) {
occur = context.getBroker().getValueIndex().scanIndexKeys(docs, nodes, allQNames, indexable);
}
// Also check if there's an index defined by path
ValueOccurrences[] occur2 = context.getBroker().getValueIndex().scanIndexKeys(docs, nodes, indexable);
// Merge the two results
if (occur == null || occur.length == 0) {
occur = occur2;
} else {
ValueOccurrences[] t = new ValueOccurrences[occur.length + occur2.length];
System.arraycopy(occur, 0, t, 0, occur.length);
System.arraycopy(occur2, 0, t, occur.length, occur2.length);
occur = t;
}
final int len = (max != -1 && occur.length > max ? max : occur.length);
final Sequence[] params = new Sequence[2];
ValueSequence data = new ValueSequence();
for (int j = 0; j < len; j++) {
params[0] = occur[j].getValue();
data.add(new IntegerValue(occur[j].getOccurrences(), Type.UNSIGNED_INT));
data.add(new IntegerValue(occur[j].getDocuments(), Type.UNSIGNED_INT));
data.add(new IntegerValue(j + 1, Type.UNSIGNED_INT));
params[1] = data;
result.addAll(ref.evalFunction(Sequence.EMPTY_SEQUENCE, null, params));
data.clear();
}
}
}
logger.debug("Returning: {}", result.getItemCount());
return result;
}
use of org.exist.dom.persistent.DocumentSet in project exist by eXist-db.
the class LockFunction method eval.
/* (non-Javadoc)
* @see org.exist.xquery.Function#eval(org.exist.xquery.value.Sequence, org.exist.xquery.value.Item)
*/
public Sequence eval(Sequence contextSequence, Item contextItem) throws XPathException {
final Sequence docsArg = getArgument(0).eval(contextSequence, contextItem);
final DocumentSet docs = docsArg.getDocumentSet();
try (final ManagedLocks<ManagedDocumentLock> managedLocks = docs.lock(context.getBroker(), exclusive)) {
return getArgument(1).eval(contextSequence, contextItem);
} catch (final LockException e) {
throw new XPathException(this, "Could not lock document set", e);
}
}
Aggregations