Skip to content

Commit

Permalink
TEIID-5541
Browse files Browse the repository at this point in the history
adding an option to disable relative xpaths for xmltable
a light refactoring to further isolate saxon
  • Loading branch information
shawkins committed Nov 27, 2018
1 parent 01748b9 commit 1633056
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ protected void initialSort(boolean onePass, boolean lowLatency, int rowLimit) th
if (!nonBlocking) {
//obey the timeslice
CommandContext cc = CommandContext.getThreadLocalContext();
if (cc != null) {
if (cc != null && cc.getWorkItem() != null) {
end = System.nanoTime() + (cc.getTimeSliceEnd()-System.currentTimeMillis())*1000000;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ private List<?> processRow() throws ExpressionEvaluationException, BlockedExcept
tuple.add((int)rowCount);
} else {
try {
XPathExpression path = proColumn.getPathExpression();
XPathExpression path = table.getXQueryExpression().getXPathExpression(proColumn.getName());
XPathDynamicContext dynamicContext = path.createDynamicContext(item);
final SequenceIterator pathIter = path.iterate(dynamicContext);
Item colItem = pathIter.next();
Expand Down
15 changes: 1 addition & 14 deletions engine/src/main/java/org/teiid/query/sql/lang/XMLTable.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
import java.util.ArrayList;
import java.util.List;

import net.sf.saxon.sxpath.XPathExpression;

import org.teiid.core.TeiidProcessingException;
import org.teiid.core.types.DataTypeManager;
import org.teiid.core.util.EquivalenceUtil;
Expand All @@ -21,8 +19,6 @@ public static class XMLColumn extends ProjectedColumn {
private String path;
private Expression defaultExpression;

private XPathExpression pathExpression;

public XMLColumn(String name) {
super(name, DataTypeManager.DefaultDataTypes.INTEGER);
this.ordinal = true;
Expand Down Expand Up @@ -62,14 +58,6 @@ public void setOrdinal(boolean ordinal) {
this.ordinal = ordinal;
}

public void setPathExpression(XPathExpression pathExpression) {
this.pathExpression = pathExpression;
}

public XPathExpression getPathExpression() {
return pathExpression;
}

@Override
public boolean equals(Object obj) {
if (obj == this) {
Expand All @@ -93,7 +81,6 @@ public XMLColumn clone() {
if (this.defaultExpression != null) {
clone.defaultExpression = (Expression)this.defaultExpression.clone();
}
clone.pathExpression = this.pathExpression;
return clone;
}
}
Expand All @@ -111,7 +98,7 @@ public List<DerivedColumn> getPassing() {
}

public void compileXqueryExpression() throws TeiidProcessingException {
this.xqueryExpression = new SaxonXQueryExpression(xquery, namespaces, passing, this.columns);
this.xqueryExpression = SaxonXQueryExpression.compile(xquery, namespaces, passing, this.columns);
}

public SaxonXQueryExpression getXQueryExpression() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public List<DerivedColumn> getPassing() {

//TODO: display the analysis record info
public void compileXqueryExpression() throws QueryResolverException {
this.xqueryExpression = new SaxonXQueryExpression(xquery, namespaces, passing, null);
this.xqueryExpression = SaxonXQueryExpression.compile(xquery, namespaces, passing, null);
this.xqueryExpression.useDocumentProjection(null, new AnalysisRecord(false, false));
}

Expand Down
16 changes: 16 additions & 0 deletions engine/src/main/java/org/teiid/query/util/Options.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public class Options {
private long maxSessionBufferSizeEstimate = Long.MAX_VALUE;
private boolean tracingWithActiveSpanOnly = true;
private boolean enforceSingleMaxBufferSizeEstimate = true;
private boolean relativeXPath = true;

public Properties getProperties() {
return properties;
Expand Down Expand Up @@ -222,4 +223,19 @@ public Options enforceSingleMaxBufferSizeEstimate(
this.enforceSingleMaxBufferSizeEstimate = b;
return this;
}

public boolean isRelativeXPath() {
return relativeXPath;
}

public void setRelativeXPath(boolean relativeXPath) {
this.relativeXPath = relativeXPath;
}

public Options relativeXPath(
boolean b) {
this.relativeXPath = b;
return this;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,7 @@
import net.sf.saxon.value.EmptySequence;
import net.sf.saxon.value.SequenceType;

@SuppressWarnings("serial")
public class SaxonXQueryExpression {
public class SaxonXQueryExpression implements Cloneable {

private static final String XQUERY_PLANNING = "XQuery Planning"; //$NON-NLS-1$
private static final String EMPTY_STRING = ""; //$NON-NLS-1$
Expand Down Expand Up @@ -185,14 +184,22 @@ public void fatalError(TransformerException arg0) throws TransformerException {
Configuration config = new Configuration();
PathMapRoot contextRoot;
String streamingPath;
Map<String, XPathExpression> columnMap;

boolean relativePaths = true;

public SaxonXQueryExpression(String xQueryString, XMLNamespaces namespaces, List<DerivedColumn> passing, List<XMLTable.XMLColumn> columns)
public static SaxonXQueryExpression compile(String xQueryString, XMLNamespaces namespaces, List<DerivedColumn> passing, List<XMLTable.XMLColumn> columns)
throws QueryResolverException {
config.setErrorListener(ERROR_LISTENER);
this.xQueryString = xQueryString;
StaticQueryContext context = config.newStaticQueryContext();
IndependentContext ic = new IndependentContext(config);
namespaceMap.put(EMPTY_STRING, EMPTY_STRING);
SaxonXQueryExpression saxonXQueryExpression = new SaxonXQueryExpression();
CommandContext cc = CommandContext.getThreadLocalContext();
if (cc != null) {
saxonXQueryExpression.relativePaths = cc.getOptions().isRelativeXPath();
}
saxonXQueryExpression.config.setErrorListener(ERROR_LISTENER);
saxonXQueryExpression.xQueryString = xQueryString;
StaticQueryContext context = saxonXQueryExpression.config.newStaticQueryContext();
IndependentContext ic = new IndependentContext(saxonXQueryExpression.config);
saxonXQueryExpression.namespaceMap.put(EMPTY_STRING, EMPTY_STRING);
if (namespaces != null) {
for (NamespaceItem item : namespaces.getNamespaceItems()) {
if (item.getPrefix() == null) {
Expand All @@ -202,12 +209,12 @@ public SaxonXQueryExpression(String xQueryString, XMLNamespaces namespaces, List
} else {
context.setDefaultElementNamespace(item.getUri());
ic.setDefaultElementNamespace(item.getUri());
namespaceMap.put(EMPTY_STRING, item.getUri());
saxonXQueryExpression.namespaceMap.put(EMPTY_STRING, item.getUri());
}
} else {
context.declareNamespace(item.getPrefix(), item.getUri());
ic.declareNamespace(item.getPrefix(), item.getUri());
namespaceMap.put(item.getPrefix(), item.getUri());
saxonXQueryExpression.namespaceMap.put(item.getPrefix(), item.getUri());
}
}
}
Expand All @@ -223,28 +230,28 @@ public SaxonXQueryExpression(String xQueryString, XMLNamespaces namespaces, List
}
}

processColumns(columns, ic);
saxonXQueryExpression.processColumns(columns, ic);

try {
this.xQuery = context.compileQuery(xQueryString);
saxonXQueryExpression.xQuery = context.compileQuery(xQueryString);
} catch (XPathException e) {
throw new QueryResolverException(QueryPlugin.Event.TEIID30154, e, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30154, xQueryString));
}

return saxonXQueryExpression;
}

private SaxonXQueryExpression() {

}

@Override
public SaxonXQueryExpression clone() {
SaxonXQueryExpression clone = new SaxonXQueryExpression();
clone.xQuery = xQuery;
clone.xQueryString = xQueryString;
clone.config = config;
clone.contextRoot = contextRoot;
clone.namespaceMap = namespaceMap;
clone.streamingPath = streamingPath;
return clone;
try {
return (SaxonXQueryExpression) super.clone();
} catch (CloneNotSupportedException e) {
throw new TeiidRuntimeException(e);
}
}

public boolean usesContextItem() {
Expand Down Expand Up @@ -340,7 +347,7 @@ private PathMapRoot projectColumns(PathMapRoot parentRoot, List<XMLTable.XMLColu
if (xmlColumn.isOrdinal()) {
continue;
}
Expression internalExpression = xmlColumn.getPathExpression().getInternalExpression();
Expression internalExpression = getXPathExpression(xmlColumn.getName()).getInternalExpression();
if (containsRootFunction(internalExpression)) {
if (record.recordAnnotations()) {
record.addAnnotation(XQUERY_PLANNING, "Root function used in column path " + xmlColumn.getPath(), "Document projection will not be used", Priority.MEDIUM); //$NON-NLS-1$ //$NON-NLS-2$
Expand Down Expand Up @@ -478,6 +485,7 @@ private void processColumns(List<XMLTable.XMLColumn> columns, IndependentContext
if (columns == null) {
return;
}
this.columnMap = new HashMap<>();
XPathEvaluator eval = new XPathEvaluator(config);
eval.setStaticContext(ic);
for (XMLColumn xmlColumn : columns) {
Expand All @@ -489,7 +497,7 @@ private void processColumns(List<XMLTable.XMLColumn> columns, IndependentContext
path = xmlColumn.getName();
}
path = path.trim();
if (path.startsWith("/")) { //$NON-NLS-1$
if (relativePaths && path.startsWith("/")) { //$NON-NLS-1$
if (path.startsWith("//")) { //$NON-NLS-1$
path = '.' + path;
} else {
Expand All @@ -502,7 +510,7 @@ private void processColumns(List<XMLTable.XMLColumn> columns, IndependentContext
} catch (XPathException e) {
throw new QueryResolverException(QueryPlugin.Event.TEIID30155, e, QueryPlugin.Util.gs(QueryPlugin.Event.TEIID30155, xmlColumn.getName(), xmlColumn.getPath()));
}
xmlColumn.setPathExpression(exp);
this.columnMap.put(xmlColumn.getName(), exp);
}
}

Expand Down Expand Up @@ -575,4 +583,11 @@ public boolean isStreaming() {
return streamingPath != null && contextRoot != null;
}

public XPathExpression getXPathExpression(String name) {
if (columnMap == null) {
return null;
}
return columnMap.get(name);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ public class TestSQLXMLProcessing {
}

@Test public void testXmlTable() throws Exception {
String sql = "select * from xmltable('/a/b' passing convert('<a><b>first</b><b x=\"attr\">second</b></a>', xml) columns x string path '@x', val string path '/.') as x"; //$NON-NLS-1$
String sql = "select * from xmltable('/a/b' passing convert('<a><b>first</b><b x=\"attr\">second</b></a>', xml) columns x string path '@x', val string path '.') as x"; //$NON-NLS-1$

List<?>[] expected = new List<?>[] {
Arrays.asList(null, "first"),
Expand All @@ -317,7 +317,7 @@ public class TestSQLXMLProcessing {
}

@Test public void testXmlTableWithPeriodAlias() throws Exception {
String sql = "select \"x.b\".* from xmltable('/a/b' passing convert('<a><b>first</b><b x=\"attr\">second</b></a>', xml) columns x string path '@x', val string path '/.') as \"x.b\""; //$NON-NLS-1$
String sql = "select \"x.b\".* from xmltable('/a/b' passing convert('<a><b>first</b><b x=\"attr\">second</b></a>', xml) columns x string path '@x', val string path '.') as \"x.b\""; //$NON-NLS-1$

List<?>[] expected = new List<?>[] {
Arrays.asList(null, "first"),
Expand Down Expand Up @@ -383,7 +383,7 @@ public class TestSQLXMLProcessing {
}

@Test public void testXmlTableBinary() throws Exception {
String sql = "select * from xmltable('/a/b' passing convert('<a xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><b xsi:type=\"xs:hexBinary\">0FAB</b><b>1F1C</b></a>', xml) columns val varbinary path '/.') as x"; //$NON-NLS-1$
String sql = "select * from xmltable('/a/b' passing convert('<a xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><b xsi:type=\"xs:hexBinary\">0FAB</b><b>1F1C</b></a>', xml) columns val varbinary path '.') as x"; //$NON-NLS-1$

List<?>[] expected = new List<?>[] {
Arrays.asList(new BinaryType(new byte[] {0xf, (byte)0xab})),
Expand Down Expand Up @@ -456,7 +456,7 @@ public class TestSQLXMLProcessing {
}

@Test public void testXmlTablePassingSubquery() throws Exception {
String sql = "select * from xmltable('/a/b' passing (SELECT xmlelement(name a, xmlAgg(xmlelement(name b, e1))) from pm1.g1) columns val string path '/.') as x"; //$NON-NLS-1$
String sql = "select * from xmltable('/a/b' passing (SELECT xmlelement(name a, xmlAgg(xmlelement(name b, e1))) from pm1.g1) columns val string path '.') as x"; //$NON-NLS-1$

List<?>[] expected = new List<?>[] {
Arrays.asList("a"),
Expand All @@ -482,7 +482,7 @@ public class TestSQLXMLProcessing {
// Create models
Schema vm1 = RealMetadataFactory.createVirtualModel("vm1", metadataStore); //$NON-NLS-1$

QueryNode vm1g1n1 = new QueryNode("select * from xmltable('/a/b' passing convert('<a><b>first</b><b x=\"attr\">second</b></a>', xml) columns x string path '@x', val string path '/.') as x"); //$NON-NLS-1$ //$NON-NLS-2$
QueryNode vm1g1n1 = new QueryNode("select * from xmltable('/a/b' passing convert('<a><b>first</b><b x=\"attr\">second</b></a>', xml) columns x string path '@x', val string path '.') as x"); //$NON-NLS-1$ //$NON-NLS-2$
Table vm1g1 = RealMetadataFactory.createVirtualGroup("g1", vm1, vm1g1n1); //$NON-NLS-1$

RealMetadataFactory.createElements(vm1g1,
Expand Down Expand Up @@ -563,7 +563,7 @@ public class TestSQLXMLProcessing {
Arrays.asList("1 2 3 4 5"),
};

process(sql, expected);
process(sql, expected, true);
}

@Test public void testXmlQuery() throws Exception {
Expand Down Expand Up @@ -836,7 +836,7 @@ public void run() {
}

@Test public void testXmlTableSubquery() throws Exception {
String sql = "select * from xmltable('/a/b' passing convert('<a><b>first</b><b x=\"attr\">c</b></a>', xml) columns x string path '@x', val string path '/.') as x where val = (select max(e1) from pm1.g1 as x)";
String sql = "select * from xmltable('/a/b' passing convert('<a><b>first</b><b x=\"attr\">c</b></a>', xml) columns x string path '@x', val string path '.') as x where val = (select max(e1) from pm1.g1 as x)";

List[] expected = new List[] {
Arrays.asList("attr", "c"),
Expand All @@ -856,19 +856,29 @@ public void run() {
TimestampWithTimezone.resetCalendar(null); //$NON-NLS-1$
}

private ProcessorPlan process(String sql, List<?>[] expected) throws Exception {
CommandContext cc = createCommandContext();
ProcessorPlan plan = helpGetPlan(helpParse(sql), RealMetadataFactory.example1Cached(), new DefaultCapabilitiesFinder(), cc);
helpProcess(plan, cc, dataManager, expected);
return plan;
private ProcessorPlan process(String sql, List<?>[] expected) throws Exception {
return process(sql, expected, false);
}

private ProcessorPlan process(String sql, List<?>[] expected, boolean relativeXPath) throws Exception {
CommandContext cc = createCommandContext();
cc.getOptions().relativeXPath(relativeXPath);
CommandContext.pushThreadLocalContext(cc);
try {
ProcessorPlan plan = helpGetPlan(helpParse(sql), RealMetadataFactory.example1Cached(), new DefaultCapabilitiesFinder(), cc);
helpProcess(plan, cc, dataManager, expected);
return plan;
} finally {
CommandContext.popThreadLocalContext();
}
}

public static BlobType blobFromFile(final String file) {
return new BlobType(new BlobImpl(new InputStreamFactory.FileInputStreamFactory(UnitTestUtil.getTestDataFile(file))));
}

@Test public void testXmlTableWithDefault() throws Exception {
String sql = "select * from xmltable(XMLNAMESPACES(default 'http://x.y.com'), '/a/b' passing convert('<a xmlns=\"http://x.y.com\"><b>first</b><b x=\"attr\">second</b></a>', xml) columns x string path '@x', val string path '/.') as x"; //$NON-NLS-1$
String sql = "select * from xmltable(XMLNAMESPACES(default 'http://x.y.com'), '/a/b' passing convert('<a xmlns=\"http://x.y.com\"><b>first</b><b x=\"attr\">second</b></a>', xml) columns x string path '@x', val string path '.') as x"; //$NON-NLS-1$

List<?>[] expected = new List<?>[] {
Arrays.asList(null, "first"),
Expand Down Expand Up @@ -1089,4 +1099,20 @@ public static BlobType blobFromFile(final String file) {
process(sql, expected);
}

@Test public void testRootPath() throws Exception {
String sql = "Select * From XmlTable (\n" +
" '/root'\n" +
" Passing convert('<root><def><test1>10</test1><test1>20</test1></def><abc>22</abc></root>', xml)\n" +
" Columns\n" +
" b integer Path '//root',\n" +
" c string Path '/root'\n" +
" )xx;";

List<?>[] expected = new List<?>[] {
Arrays.asList(102022, "102022"),
};

process(sql, expected);
}

}

0 comments on commit 1633056

Please sign in to comment.