Return-Path: X-Original-To: apmail-jackrabbit-oak-commits-archive@minotaur.apache.org Delivered-To: apmail-jackrabbit-oak-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 2C8A0D5A3 for ; Tue, 11 Sep 2012 14:34:27 +0000 (UTC) Received: (qmail 62322 invoked by uid 500); 11 Sep 2012 14:34:27 -0000 Delivered-To: apmail-jackrabbit-oak-commits-archive@jackrabbit.apache.org Received: (qmail 62290 invoked by uid 500); 11 Sep 2012 14:34:26 -0000 Mailing-List: contact oak-commits-help@jackrabbit.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: oak-dev@jackrabbit.apache.org Delivered-To: mailing list oak-commits@jackrabbit.apache.org Received: (qmail 62274 invoked by uid 99); 11 Sep 2012 14:34:26 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 11 Sep 2012 14:34:26 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 11 Sep 2012 14:34:24 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 4A056238899C; Tue, 11 Sep 2012 14:33:41 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1383429 - in /jackrabbit/oak/trunk/oak-core/src: main/java/org/apache/jackrabbit/oak/plugins/index/ main/java/org/apache/jackrabbit/oak/query/ test/java/org/apache/jackrabbit/oak/query/ test/resources/org/apache/jackrabbit/oak/query/ Date: Tue, 11 Sep 2012 14:33:40 -0000 To: oak-commits@jackrabbit.apache.org From: thomasm@apache.org X-Mailer: svnmailer-1.0.8-patched Message-Id: <20120911143341.4A056238899C@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: thomasm Date: Tue Sep 11 14:33:40 2012 New Revision: 1383429 URL: http://svn.apache.org/viewvc?rev=1383429&view=rev Log: OAK-28 Query implementation: improved XPath compatibility Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/BTreeLeaf.java (props changed) jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/BTreeNode.java (props changed) jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/BTreePage.java (contents, props changed) jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/Indexer.java jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/PropertyIndex.java (props changed) jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/Query.java jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/XPathToSQL2Converter.java jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/QueryTest.java jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2.txt jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/xpath.txt Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/BTreeLeaf.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/BTreeNode.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/BTreePage.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/BTreePage.java?rev=1383429&r1=1383428&r2=1383429&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/BTreePage.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/BTreePage.java Tue Sep 11 14:33:40 2012 @@ -21,7 +21,7 @@ import org.apache.jackrabbit.oak.commons /** * An index page. */ -abstract public class BTreePage implements PropertyIndexConstants { +public abstract class BTreePage implements PropertyIndexConstants { protected final BTree tree; protected BTreeNode parent; Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/BTreePage.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/Indexer.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/Indexer.java?rev=1383429&r1=1383428&r2=1383429&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/Indexer.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/Indexer.java Tue Sep 11 14:33:40 2012 @@ -310,7 +310,7 @@ public class Indexer implements Property } } - synchronized public void updateUntil(String toRevision) { + public synchronized void updateUntil(String toRevision) { if (DISABLED) { return; } @@ -505,20 +505,20 @@ public class Indexer implements Property // actually not required, just to make sure if (PathUtils.getParentPath(path).equals(indexRootNode)) { String name = PathUtils.getName(path); - if (name.startsWith(Indexer.TYPE_PREFIX)) { - String prefix = name.substring(Indexer.TYPE_PREFIX.length()); + if (name.startsWith(PropertyIndexConstants.TYPE_PREFIX)) { + String prefix = name.substring(PropertyIndexConstants.TYPE_PREFIX.length()); if (remove) { removePrefixIndex(prefix); } if (add) { createPrefixIndex(prefix); } - } else if (name.startsWith(Indexer.TYPE_PROPERTY)) { - String property = name.substring(Indexer.TYPE_PROPERTY.length()); + } else if (name.startsWith(PropertyIndexConstants.TYPE_PROPERTY)) { + String property = name.substring(PropertyIndexConstants.TYPE_PROPERTY.length()); boolean unique = false; - if (property.endsWith("," + Indexer.UNIQUE)) { + if (property.endsWith("," + PropertyIndexConstants.UNIQUE)) { unique = true; - property = property.substring(0, property.length() - Indexer.UNIQUE.length() - 1); + property = property.substring(0, property.length() - PropertyIndexConstants.UNIQUE.length() - 1); } if (remove) { removePropertyIndex(property, unique); Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/PropertyIndex.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/Query.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/Query.java?rev=1383429&r1=1383428&r2=1383429&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/Query.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/Query.java Tue Sep 11 14:33:40 2012 @@ -629,7 +629,8 @@ public class Query { public int getColumnIndex(String columnName) { for (int i = 0, size = columns.length; i < size; i++) { ColumnImpl c = columns[i]; - if (c.getColumnName().equals(columnName)) { + String cn = c.getColumnName(); + if (cn != null && cn.equals(columnName)) { return i; } } Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/XPathToSQL2Converter.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/XPathToSQL2Converter.java?rev=1383429&r1=1383428&r2=1383429&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/XPathToSQL2Converter.java (original) +++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/query/XPathToSQL2Converter.java Tue Sep 11 14:33:40 2012 @@ -58,40 +58,55 @@ public class XPathToSQL2Converter { * @throws ParseException if parsing fails */ public String convert(String query) throws ParseException { + query = query.trim(); boolean explain = query.startsWith("explain "); if (explain) { - query = query.substring("explain ".length()); + query = query.substring("explain".length()).trim(); } boolean measure = query.startsWith("measure"); if (measure) { - query = query.substring("measure ".length()); + query = query.substring("measure".length()).trim(); } - // TODO verify this is correct - if (!query.startsWith("/")) { - query = "/jcr:root/" + query; + + if (query.isEmpty()) { + // special case, will always result in an empty result + query = "//jcr:root"; } + initialize(query); + expected = new ArrayList(); read(); + + if (currentTokenType == END) { + throw getSyntaxError("the query may not be empty"); + } currentSelector.name = "a"; - currentSelector.nodeType = "nt:base"; ArrayList columnList = new ArrayList(); - // TODO support one node matcher ('*' wildcard), example: - // /jcr:root/content/acme/*/jcr:content[@template='/apps/acme'] - - // TODO verify '//' is behaving correctly, as specified, example: - // /jcr:root/content/acme//jcr:content[@template='/apps/acme'] + // TODO support "..", example: + // /jcr:root/etc/.. String pathPattern = ""; + boolean startOfQuery = true; while (true) { + + // if true, path or nodeType conditions are not allowed boolean shortcut = false; boolean slash = readIf("/"); + if (!slash) { - break; + if (startOfQuery) { + // the query doesn't start with "/" + currentSelector.path = "/"; + pathPattern = "/"; + currentSelector.isChild = true; + } else { + break; + } } else if (readIf("jcr:root")) { // "/jcr:root" may only appear at the beginning if (!pathPattern.isEmpty()) { @@ -105,12 +120,13 @@ public class XPathToSQL2Converter { // "/jcr:root//" pathPattern = "//"; currentSelector.isDescendant = true; + } else { + currentSelector.isChild = true; } } else { // for example "/jcr:root[condition]" pathPattern = "/%"; currentSelector.path = "/"; - currentSelector.isDescendant = true; shortcut = true; } } else if (readIf("/")) { @@ -120,16 +136,26 @@ public class XPathToSQL2Converter { } else { // the token "/" was read pathPattern += "/"; + if (startOfQuery) { + currentSelector.path = "/"; + } else { + currentSelector.isChild = true; + } } if (shortcut) { - // query of the style: "/jcr:root[condition]" + // "*" and so on are not allowed now } else if (readIf("*")) { // "...*" pathPattern += "%"; - currentSelector.nodeType = "nt:base"; - currentSelector.isChild = true; + if (!currentSelector.isDescendant) { + if (selectors.size() == 0 && currentSelector.path.equals("")) { + // the query /* is special + currentSelector.path = "/"; + } + } } else if (readIf("text")) { // "...text()" + currentSelector.isChild = false; pathPattern += "jcr:xmltext"; read("("); read(")"); @@ -143,11 +169,10 @@ public class XPathToSQL2Converter { read("("); if (readIf(")")) { // any - currentSelector.isChild = true; + pathPattern += "%"; } else { if (readIf("*")) { // any - currentSelector.isChild = true; pathPattern += "%"; } else { currentSelector.isChild = false; @@ -157,8 +182,6 @@ public class XPathToSQL2Converter { } if (readIf(",")) { currentSelector.nodeType = readIdentifier(); - } else { - currentSelector.nodeType = "nt:base"; } read(")"); } @@ -176,31 +199,41 @@ public class XPathToSQL2Converter { currentSelector.path = ""; currentSelector.nodeName = null; } - if (currentSelector.nodeType == null) { - currentSelector.nodeType = "nt:base"; - } do { read("@"); Property p = readProperty(); columnList.add(p); } while (readIf("|")); read(")"); - } else { + } else if (currentTokenType == IDENTIFIER) { // path restriction String name = readIdentifier(); - if (currentSelector.isDescendant) { - pathPattern += name; + pathPattern += name; + if (!currentSelector.isChild) { currentSelector.nodeName = name; } else { - pathPattern += name; - currentSelector.path = PathUtils.concat(currentSelector.path, name); + if (selectors.size() > 0) { + // no explicit path restriction - so it's a node name restriction + currentSelector.isChild = true; + currentSelector.nodeName = name; + } else { + if (currentSelector.isChild) { + currentSelector.isChild = false; + String oldPath = currentSelector.path; + // further extending the path + currentSelector.path = PathUtils.concat(oldPath, name); + } + } } + } else { + throw getSyntaxError(); } if (readIf("[")) { Expression c = parseConstraint(); currentSelector.condition = add(currentSelector.condition, c); read("]"); } + startOfQuery = false; nextSelector(false); } if (selectors.size() == 0) { @@ -241,15 +274,26 @@ public class XPathToSQL2Converter { // select ... buff.append("select "); buff.append(new Property(currentSelector, "jcr:path").toString()); + if (selectors.size() > 1) { + buff.append(" as [jcr:path]"); + } buff.append(", "); buff.append(new Property(currentSelector, "jcr:score").toString()); + if (selectors.size() > 1) { + buff.append(" as [jcr:score]"); + } if (columnList.isEmpty()) { buff.append(", "); buff.append(new Property(currentSelector, "*").toString()); } else { for (int i = 0; i < columnList.size(); i++) { buff.append(", "); - buff.append(columnList.get(i).toString()); + Expression e = columnList.get(i); + String columnName = e.toString(); + buff.append(columnName); + if (selectors.size() > 1) { + buff.append(" as [").append(e.getColumnAliasName()).append("]"); + } } } @@ -260,7 +304,11 @@ public class XPathToSQL2Converter { if (i > 0) { buff.append(" inner join "); } - buff.append('[' + s.nodeType + ']').append(" as ").append(s.name); + String nodeType = s.nodeType; + if (nodeType == null) { + nodeType = "nt:base"; + } + buff.append('[' + nodeType + ']').append(" as ").append(s.name); if (s.joinCondition != null) { buff.append(" on ").append(s.joinCondition); } @@ -434,6 +482,9 @@ public class XPathToSQL2Converter { c = new Condition(left, "<=", parseExpression(), Expression.PRECEDENCE_CONDITION); } else if (readIf(">=")) { c = new Condition(left, ">=", parseExpression(), Expression.PRECEDENCE_CONDITION); + // TODO support "x eq y"? it seems this only matches for single value properties? + // } else if (readIf("eq")) { + // c = new Condition(left, "==", parseExpression(), Expression.PRECEDENCE_CONDITION); } else { c = new Condition(left, "is not null", null, Expression.PRECEDENCE_CONDITION); } @@ -898,7 +949,7 @@ public class XPathToSQL2Converter { } private ParseException getSyntaxError(String expected) { - int index = Math.min(parseIndex, statement.length() - 1); + int index = Math.max(0, Math.min(parseIndex, statement.length() - 1)); String query = statement.substring(0, index) + "(*)" + statement.substring(index).trim(); if (expected != null) { query += "; expected: " + expected; @@ -995,6 +1046,17 @@ public class XPathToSQL2Converter { int getPrecedence() { return PRECEDENCE_OPERAND; } + + /** + * Get the column alias name of an expression. For a property, this is the + * property name (no matter how many selectors the query contains); for + * other expressions it matches the toString() method. + * + * @return the simple column name + */ + String getColumnAliasName() { + return toString(); + } } @@ -1072,6 +1134,11 @@ public class XPathToSQL2Converter { } return buff.toString(); } + + @Override + public String getColumnAliasName() { + return name; + } } Modified: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/QueryTest.java URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/QueryTest.java?rev=1383429&r1=1383428&r2=1383429&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/QueryTest.java (original) +++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/query/QueryTest.java Tue Sep 11 14:33:40 2012 @@ -27,6 +27,7 @@ import java.text.ParseException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -100,6 +101,7 @@ public class QueryTest extends AbstractQ LineNumberReader r = new LineNumberReader(new InputStreamReader(in)); PrintWriter w = new PrintWriter(new OutputStreamWriter( new FileOutputStream("target/" + file))); + HashSet knownQueries = new HashSet(); boolean errors = false; try { while (true) { @@ -124,6 +126,9 @@ public class QueryTest extends AbstractQ // e.printStackTrace(); got = "error: " + e.toString().replace('\n', ' '); } + if (!knownQueries.add(line)) { + got = "duplicate xpath2sql query"; + } line = r.readLine().trim(); w.println(got); if (!line.equals(got)) { Modified: jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2.txt URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2.txt?rev=1383429&r1=1383428&r2=1383429&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2.txt (original) +++ jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/sql2.txt Tue Sep 11 14:33:40 2012 @@ -48,6 +48,9 @@ commit / + "test": { "a": { "id": "10" } select * from [nt:base] where id is not null and not id = '100' and id <> '20' /test/a +select * from [nt:base] where id < '1000' +/test/a + select * from [nt:base] where id is not null and not (id = '100' and id <> '20') /test/a /test/b @@ -77,6 +80,9 @@ commit / - "test" # other tests +select [jcr:path] from [nt:base] as a where issamenode(a, '/') +/ + commit / + "test": { "My Documents": { "x" : {}}} select [jcr:path] from [nt:base] where name() = 'My_x0020_Documents' Modified: jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/xpath.txt URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/xpath.txt?rev=1383429&r1=1383428&r2=1383429&view=diff ============================================================================== --- jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/xpath.txt (original) +++ jackrabbit/oak/trunk/oak-core/src/test/resources/org/apache/jackrabbit/oak/query/xpath.txt Tue Sep 11 14:33:40 2012 @@ -25,11 +25,76 @@ # jackrabbit test queries +xpath2sql /test +select [jcr:path], [jcr:score], * from [nt:base] as a where name(a) = 'test' and issamenode(a, '/') + +xpath2sql / +invalid: Query: (*)/; expected: jcr:root, /, *, text, element, @, ( + +xpath2sql /[@name='data'] +invalid: Query: /[(*)@name='data']; expected: jcr:root, /, *, text, element, @, ( + +xpath2sql //[@name='data'] +invalid: Query: //[(*)@name='data']; expected: *, text, element, @, ( + +xpath2sql //child/[@id='2.1'] +invalid: Query: //child/[(*)@id='2.1']; expected: jcr:root, /, *, text, element, @, ( + +xpath2sql // +invalid: Query: /(*)/; expected: *, text, element, @, ( + +xpath2sql [@name='data'] +invalid: Query: [(*)@name='data']; expected: /, *, text, element, @, ( + +xpath2sql test +select [jcr:path], [jcr:score], * from [nt:base] as a where issamenode(a, '/test') + +xpath2sql jcr:root +select [jcr:path], [jcr:score], * from [nt:base] as a where issamenode(a, '/jcr:root') + +xpath2sql /jcr:root +select [jcr:path], [jcr:score], * from [nt:base] as a where issamenode(a, '/') + +xpath2sql //jcr:root +select [jcr:path], [jcr:score], * from [nt:base] as a where name(a) = 'jcr:root' + +xpath2sql * +select [jcr:path], [jcr:score], * from [nt:base] as a where ischildnode(a, '/') + +xpath2sql /* +select [jcr:path], [jcr:score], * from [nt:base] as a where issamenode(a, '/') + +xpath2sql //* +select [jcr:path], [jcr:score], * from [nt:base] as a + +xpath2sql test/* +select [jcr:path], [jcr:score], * from [nt:base] as a where ischildnode(a, '/test') + +xpath2sql element(*, nt:folder) +select [jcr:path], [jcr:score], * from [nt:folder] as a where ischildnode(a, '/') + +xpath2sql //test +select [jcr:path], [jcr:score], * from [nt:base] as a where name(a) = 'test' + +xpath2sql /jcr:root[@foo = 'does-not-exist'] +select [jcr:path], [jcr:score], * from [nt:base] as a where [foo] = 'does-not-exist' and issamenode(a, '/') + +xpath2sql +select [jcr:path], [jcr:score], * from [nt:base] as a where name(a) = 'jcr:root' + +xpath2sql /jcr:root/testroot/*/node11 +select b.[jcr:path] as [jcr:path], b.[jcr:score] as [jcr:score], b.* from [nt:base] as a inner join [nt:base] as b on ischildnode(b, a) where ischildnode(a, '/testroot') and name(b) = 'node11' + +# eq can't currently be supported as there is no equivalent in SQL-2 +# (the behavior is different from = if one of the operands is a multi-valued property) +xpath2sql //testRoot/*[@jcr:primaryType='nt:unstructured' and @text eq 'foo'] +invalid: Query: //testRoot/*[@jcr:primaryType='nt:unstructured' and @text eq(*)'foo']; expected: ] + xpath2sql //testRoot/*[@text = 'foo'] -select b.[jcr:path], b.[jcr:score], b.* from [nt:base] as a inner join [nt:base] as b on ischildnode(b, a) where name(a) = 'testRoot' and b.[text] = 'foo' +select b.[jcr:path] as [jcr:path], b.[jcr:score] as [jcr:score], b.* from [nt:base] as a inner join [nt:base] as b on ischildnode(b, a) where name(a) = 'testRoot' and b.[text] = 'foo' xpath2sql /testRoot/*[@jcr:primaryType='nt:unstructured' and fn:not(@mytext)] -select [jcr:path], [jcr:score], * from [nt:base] as a where [jcr:primaryType] = 'nt:unstructured' and [mytext] is null and ischildnode(a, '/testRoot') +select b.[jcr:path] as [jcr:path], b.[jcr:score] as [jcr:score], b.* from [nt:base] as a inner join [nt:base] as b on ischildnode(b, a) where name(a) = 'testRoot' and issamenode(a, '/') and b.[jcr:primaryType] = 'nt:unstructured' and b.[mytext] is null xpath2sql /jcr:root/testroot/*[jcr:contains(., '"quick brown" -cat')] select [jcr:path], [jcr:score], * from [nt:base] as a where contains(*, '"quick brown" -cat') and ischildnode(a, '/testroot') @@ -63,17 +128,14 @@ xpath2sql /jcr:root/testroot/*[jcr:conta invalid: Query: /jcr:root/testroot/*[jcr:contains(., 'jackrabbit')]/rep:excerpt((*).); expected: xpath2sql /jcr:root/testroot//child/..[@foo1] -invalid: Query: /jcr:root/testroot//child/.(*).[@foo1]; expected: identifier +invalid: Query: /jcr:root/testroot//child/.(*).[@foo1]; expected: jcr:root, /, *, text, element, @, ( xpath2sql //testroot/*[@jcr:primaryType='nt:unstructured' and fn:not(@mytext)] -select b.[jcr:path], b.[jcr:score], b.* from [nt:base] as a inner join [nt:base] as b on ischildnode(b, a) where name(a) = 'testroot' and b.[jcr:primaryType] = 'nt:unstructured' and b.[mytext] is null +select b.[jcr:path] as [jcr:path], b.[jcr:score] as [jcr:score], b.* from [nt:base] as a inner join [nt:base] as b on ischildnode(b, a) where name(a) = 'testroot' and b.[jcr:primaryType] = 'nt:unstructured' and b.[mytext] is null xpath2sql /jcr:root/testroot/people/jcr:deref(@worksfor, '*') invalid: Query: /jcr:root/testroot/people/jcr:deref((*)@worksfor, '*'); expected: -xpath2sql /jcr:root[@foo = 'does-not-exist'] -select [jcr:path], [jcr:score], * from [nt:base] as a where [foo] = 'does-not-exist' and isdescendantnode(a, '/') - xpath2sql //*[@jcr:primaryType='nt:unstructured' and jcr:like(@foo,"%ar'ba%")] select [jcr:path], [jcr:score], * from [nt:base] as a where [jcr:primaryType] = 'nt:unstructured' and [foo] like '%ar''ba%' @@ -104,16 +166,13 @@ select [jcr:path], [jcr:score], * from [ # sling queries xpath2sql //element(*,mix:language)[fn:lower-case(@jcr:language)='en']//element(*,sling:Message)[@sling:message]/(@sling:key|@sling:message) -select b.[jcr:path], b.[jcr:score], b.[sling:key], b.[sling:message] from [mix:language] as a inner join [sling:Message] as b on isdescendantnode(b, a) where lower(a.[jcr:language]) = 'en' and b.[sling:message] is not null +select b.[jcr:path] as [jcr:path], b.[jcr:score] as [jcr:score], b.[sling:key] as [sling:key], b.[sling:message] as [sling:message] from [mix:language] as a inner join [sling:Message] as b on isdescendantnode(b, a) where lower(a.[jcr:language]) = 'en' and b.[sling:message] is not null xpath2sql //element(*,mix:language)[fn:upper-case(@jcr:language)='en']//element(*,sling:Message)[@sling:message]/(@sling:key|@sling:message) -select b.[jcr:path], b.[jcr:score], b.[sling:key], b.[sling:message] from [mix:language] as a inner join [sling:Message] as b on isdescendantnode(b, a) where upper(a.[jcr:language]) = 'en' and b.[sling:message] is not null +select b.[jcr:path] as [jcr:path], b.[jcr:score] as [jcr:score], b.[sling:key] as [sling:key], b.[sling:message] as [sling:message] from [mix:language] as a inner join [sling:Message] as b on isdescendantnode(b, a) where upper(a.[jcr:language]) = 'en' and b.[sling:message] is not null # jboss example queries -xpath2sql //* -select [jcr:path], [jcr:score], * from [nt:base] as a - xpath2sql //element(*,my:type) select [jcr:path], [jcr:score], * from [my:type] as a @@ -129,7 +188,7 @@ xpath2sql /jcr:root/testdata/node[@jcr:p select [jcr:path], [jcr:score], * from [nt:base] as a where [jcr:primaryType] is not null and issamenode(a, '/testdata/node') xpath2sql //testroot/*[@jcr:primaryType='nt:unstructured'] order by @prop2, @prop1 -select b.[jcr:path], b.[jcr:score], b.* from [nt:base] as a inner join [nt:base] as b on ischildnode(b, a) where name(a) = 'testroot' and b.[jcr:primaryType] = 'nt:unstructured' order by b.[prop2], b.[prop1] +select b.[jcr:path] as [jcr:path], b.[jcr:score] as [jcr:score], b.* from [nt:base] as a inner join [nt:base] as b on ischildnode(b, a) where name(a) = 'testroot' and b.[jcr:primaryType] = 'nt:unstructured' order by b.[prop2], b.[prop1] xpath2sql /jcr:root/test//jcr:xmltext select [jcr:path], [jcr:score], * from [nt:base] as a where name(a) = 'jcr:xmltext' and isdescendantnode(a, '/test') @@ -143,9 +202,6 @@ select [jcr:path], [jcr:score], * from [ xpath2sql /jcr:root/test/text() select [jcr:path], [jcr:score], * from [nt:base] as a where issamenode(a, '/test/jcr:xmltext') -xpath2sql /jcr:root -select [jcr:path], [jcr:score], * from [nt:base] as a where isdescendantnode(a, '/') - xpath2sql //*[@name='Hello'] select [jcr:path], [jcr:score], * from [nt:base] as a where [name] = 'Hello' @@ -220,9 +276,6 @@ select [jcr:path], [jcr:score], * from [ # other queries -xpath2sql //* -select [jcr:path], [jcr:score], * from [nt:base] as a - xpath2sql //element(*, my:type) select [jcr:path], [jcr:score], * from [my:type] as a @@ -296,7 +349,7 @@ xpath2sql //element(*, my:type)[jcr:cont select [jcr:path], [jcr:score], * from [my:type] as a where contains([my:title], 'jcr') order by score(a) desc xpath2sql [invalid/query -invalid: Query: /jcr:root/[(*)invalid/query; expected: identifier +invalid: Query: [(*)invalid/query; expected: /, *, text, element, @, ( xpath2sql //element(*, my:type)[@my:value = -'x'] invalid: Query: //element(*, my:type)[@my:value = -'x'(*)]