From 58c0072866e20c7d92931e072172f96deb5670e4 Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Mon, 23 Mar 2020 15:23:09 +0100 Subject: [PATCH] [jOOQ/jOOQ#9925] Add support for XMLTABLE() --- .../jooq/XMLTableColumnForOrdinalityStep.java | 57 ++++ .../java/org/jooq/XMLTableColumnPathStep.java | 57 ++++ .../org/jooq/XMLTableColumnsFirstStep.java | 92 ++++++ .../java/org/jooq/XMLTableColumnsStep.java | 47 +++ .../java/org/jooq/XMLTablePassingStep.java | 92 ++++++ jOOQ/src/main/java/org/jooq/impl/DSL.java | 17 + .../src/main/java/org/jooq/impl/Keywords.java | 1 + jOOQ/src/main/java/org/jooq/impl/Names.java | 1 + .../main/java/org/jooq/impl/ParserImpl.java | 66 +++- .../main/java/org/jooq/impl/XMLExists.java | 34 +- .../org/jooq/impl/XMLPassingMechanism.java | 2 +- .../src/main/java/org/jooq/impl/XMLQuery.java | 31 +- .../src/main/java/org/jooq/impl/XMLTable.java | 300 ++++++++++++++++++ 13 files changed, 731 insertions(+), 66 deletions(-) create mode 100644 jOOQ/src/main/java/org/jooq/XMLTableColumnForOrdinalityStep.java create mode 100644 jOOQ/src/main/java/org/jooq/XMLTableColumnPathStep.java create mode 100644 jOOQ/src/main/java/org/jooq/XMLTableColumnsFirstStep.java create mode 100644 jOOQ/src/main/java/org/jooq/XMLTableColumnsStep.java create mode 100644 jOOQ/src/main/java/org/jooq/XMLTablePassingStep.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/XMLTable.java diff --git a/jOOQ/src/main/java/org/jooq/XMLTableColumnForOrdinalityStep.java b/jOOQ/src/main/java/org/jooq/XMLTableColumnForOrdinalityStep.java new file mode 100644 index 0000000000..c6dda1f3ff --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/XMLTableColumnForOrdinalityStep.java @@ -0,0 +1,57 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Other licenses: + * ----------------------------------------------------------------------------- + * Commercial licenses for this work are available. These replace the above + * ASL 2.0 and offer limited warranties, support, maintenance, and commercial + * database integrations. + * + * For more information, please visit: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq; + +// ... +import static org.jooq.SQLDialect.POSTGRES; + +/** + * A step in the construction of an XMLTABLE expression. + * + * @author Lukas Eder + */ +public interface XMLTableColumnForOrdinalityStep { + + /** + * Specify the FOR ORDINALITY clause on a column in the + * COLUMNS clause of the XMLTABLE predicate. + */ + @Support({ POSTGRES }) + XMLTableColumnsStep forOrdinality(); + +} diff --git a/jOOQ/src/main/java/org/jooq/XMLTableColumnPathStep.java b/jOOQ/src/main/java/org/jooq/XMLTableColumnPathStep.java new file mode 100644 index 0000000000..a47dbc663a --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/XMLTableColumnPathStep.java @@ -0,0 +1,57 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Other licenses: + * ----------------------------------------------------------------------------- + * Commercial licenses for this work are available. These replace the above + * ASL 2.0 and offer limited warranties, support, maintenance, and commercial + * database integrations. + * + * For more information, please visit: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq; + +// ... +import static org.jooq.SQLDialect.POSTGRES; + +/** + * A step in the construction of an XMLTABLE expression. + * + * @author Lukas Eder + */ +public interface XMLTableColumnPathStep extends XMLTableColumnForOrdinalityStep, XMLTableColumnsStep { + + /** + * Specify the PATH of a column in the COLUMNS + * clause of the XMLTABLE predicate. + */ + @Support({ POSTGRES }) + XMLTableColumnsStep path(String path); + +} diff --git a/jOOQ/src/main/java/org/jooq/XMLTableColumnsFirstStep.java b/jOOQ/src/main/java/org/jooq/XMLTableColumnsFirstStep.java new file mode 100644 index 0000000000..d045b14bda --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/XMLTableColumnsFirstStep.java @@ -0,0 +1,92 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Other licenses: + * ----------------------------------------------------------------------------- + * Commercial licenses for this work are available. These replace the above + * ASL 2.0 and offer limited warranties, support, maintenance, and commercial + * database integrations. + * + * For more information, please visit: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq; + +// ... +import static org.jooq.SQLDialect.POSTGRES; + +/** + * A step in the construction of an XMLTABLE expression. + * + * @author Lukas Eder + */ +public interface XMLTableColumnsFirstStep { + + /** + * Add a column to the COLUMNS clause of the + * XMLTABLE expression. + */ + @Support({ POSTGRES }) + XMLTableColumnForOrdinalityStep column(String name); + + /** + * Add a column to the COLUMNS clause of the + * XMLTABLE expression. + */ + @Support({ POSTGRES }) + XMLTableColumnForOrdinalityStep column(Name name); + + /** + * Add a column to the COLUMNS clause of the + * XMLTABLE expression. + */ + @Support({ POSTGRES }) + XMLTableColumnPathStep column(Field name); + + /** + * Add a column to the COLUMNS clause of the + * XMLTABLE expression. + */ + @Support({ POSTGRES }) + XMLTableColumnPathStep column(String name, DataType type); + + /** + * Add a column to the COLUMNS clause of the + * XMLTABLE expression. + */ + @Support({ POSTGRES }) + XMLTableColumnPathStep column(Name name, DataType type); + + /** + * Add a column to the COLUMNS clause of the + * XMLTABLE expression. + */ + @Support({ POSTGRES }) + XMLTableColumnPathStep column(Field name, DataType type); + +} diff --git a/jOOQ/src/main/java/org/jooq/XMLTableColumnsStep.java b/jOOQ/src/main/java/org/jooq/XMLTableColumnsStep.java new file mode 100644 index 0000000000..d774f120fc --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/XMLTableColumnsStep.java @@ -0,0 +1,47 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Other licenses: + * ----------------------------------------------------------------------------- + * Commercial licenses for this work are available. These replace the above + * ASL 2.0 and offer limited warranties, support, maintenance, and commercial + * database integrations. + * + * For more information, please visit: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq; + +/** + * A step in the construction of an XMLTABLE expression. + * + * @author Lukas Eder + */ +public interface XMLTableColumnsStep extends XMLTableColumnsFirstStep, Table { + +} diff --git a/jOOQ/src/main/java/org/jooq/XMLTablePassingStep.java b/jOOQ/src/main/java/org/jooq/XMLTablePassingStep.java new file mode 100644 index 0000000000..e42f9a9992 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/XMLTablePassingStep.java @@ -0,0 +1,92 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Other licenses: + * ----------------------------------------------------------------------------- + * Commercial licenses for this work are available. These replace the above + * ASL 2.0 and offer limited warranties, support, maintenance, and commercial + * database integrations. + * + * For more information, please visit: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq; + +// ... +import static org.jooq.SQLDialect.POSTGRES; + +/** + * A step in the construction of an XMLTABLE expression. + * + * @author Lukas Eder + */ +public interface XMLTablePassingStep extends XMLTableColumnsFirstStep { + + /** + * Add the PASSING clause to the XMLEXISTS + * predicate. + */ + @Support({ POSTGRES }) + XMLTableColumnsFirstStep passing(XML xml); + + /** + * Add the PASSING clause to the XMLEXISTS + * predicate. + */ + @Support({ POSTGRES }) + XMLTableColumnsFirstStep passing(Field xml); + + /** + * Add the PASSING BY REF clause to the XMLEXISTS + * predicate. + */ + @Support({ POSTGRES }) + XMLTableColumnsFirstStep passingByRef(XML xml); + + /** + * Add the PASSING BY REF clause to the XMLEXISTS + * predicate. + */ + @Support({ POSTGRES }) + XMLTableColumnsFirstStep passingByRef(Field xml); + + /** + * Add the PASSING BY VALUE clause to the XMLEXISTS + * predicate. + */ + @Support({ POSTGRES }) + XMLTableColumnsFirstStep passingByValue(XML xml); + + /** + * Add the PASSING BY VALUE clause to the XMLEXISTS + * predicate. + */ + @Support({ POSTGRES }) + XMLTableColumnsFirstStep passingByValue(Field xml); + +} diff --git a/jOOQ/src/main/java/org/jooq/impl/DSL.java b/jOOQ/src/main/java/org/jooq/impl/DSL.java index becd47e334..acb050499d 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DSL.java +++ b/jOOQ/src/main/java/org/jooq/impl/DSL.java @@ -394,6 +394,7 @@ import org.jooq.XMLAggOrderByStep; import org.jooq.XMLAttributes; import org.jooq.XMLExistsPassingStep; import org.jooq.XMLQueryPassingStep; +import org.jooq.XMLTablePassingStep; import org.jooq.conf.Settings; import org.jooq.exception.SQLDialectNotSupportedException; import org.jooq.impl.XMLParse.DocumentOrContent; @@ -18343,6 +18344,22 @@ public class DSL { return new XMLExists(xpath); } + /** + * The XML table function. + */ + @Support({ POSTGRES }) + public static XMLTablePassingStep xmltable(String xpath) { + return xmltable(Tools.field(xpath)); + } + + /** + * The XML table function. + */ + @Support({ POSTGRES }) + public static XMLTablePassingStep xmltable(Field xpath) { + return new XMLTable(xpath); + } + // ------------------------------------------------------------------------- // XXX JSON functions // ------------------------------------------------------------------------- diff --git a/jOOQ/src/main/java/org/jooq/impl/Keywords.java b/jOOQ/src/main/java/org/jooq/impl/Keywords.java index 4332a8e612..c954d6f834 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Keywords.java +++ b/jOOQ/src/main/java/org/jooq/impl/Keywords.java @@ -273,6 +273,7 @@ final class Keywords { static final Keyword K_OR = keyword("or"); static final Keyword K_ORDER = keyword("order"); static final Keyword K_ORDER_BY = keyword("order by"); + static final Keyword K_ORDINALITY = keyword("ordinality"); static final Keyword K_OUTPUT = keyword("output"); static final Keyword K_OVER = keyword("over"); static final Keyword K_OVERLAPS = keyword("overlaps"); diff --git a/jOOQ/src/main/java/org/jooq/impl/Names.java b/jOOQ/src/main/java/org/jooq/impl/Names.java index 2285f0650f..fe78b50b71 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Names.java +++ b/jOOQ/src/main/java/org/jooq/impl/Names.java @@ -165,6 +165,7 @@ final class Names { static final Name N_XMLPI = DSL.unquotedName("xmlpi"); static final Name N_XMLQUERY = DSL.unquotedName("xmlquery"); static final Name N_XMLROOT = DSL.unquotedName("xmlroot"); + static final Name N_XMLTABLE = DSL.unquotedName("xmltable"); static final Name N_XPATH = DSL.unquotedName("xpath"); } diff --git a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java index 2846dde529..10e391f789 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java @@ -303,6 +303,7 @@ import static org.jooq.impl.DSL.xmlparseContent; import static org.jooq.impl.DSL.xmlparseDocument; import static org.jooq.impl.DSL.xmlpi; import static org.jooq.impl.DSL.xmlquery; +import static org.jooq.impl.DSL.xmltable; import static org.jooq.impl.DSL.year; import static org.jooq.impl.DSL.zero; import static org.jooq.impl.JSONNullClause.ABSENT_ON_NULL; @@ -519,6 +520,9 @@ import org.jooq.WindowSpecificationRowsStep; import org.jooq.XML; import org.jooq.XMLAggOrderByStep; import org.jooq.XMLAttributes; +import org.jooq.XMLTableColumnPathStep; +import org.jooq.XMLTableColumnsStep; +import org.jooq.XMLTablePassingStep; import org.jooq.conf.ParseSearchSchema; import org.jooq.conf.ParseUnknownFunctions; import org.jooq.conf.ParseUnsupportedSyntax; @@ -5034,6 +5038,41 @@ final class ParserImpl implements Parser { ? generateSeries(from, to) : generateSeries(from, to, step); } + else if (parseFunctionNameIf(ctx, "XMLTABLE")) { + parse(ctx, '('); + + XMLTablePassingStep s1 = xmltable((Field) toField(ctx, parseConcat(ctx, Type.S))); + XMLPassingMechanism m = parseXMLPassingMechanismIf(ctx); + Field passing = m == null ? null : (Field) parseField(ctx); + + XMLTableColumnsStep s2 = (XMLTableColumnsStep) ( + m == BY_REF + ? s1.passingByRef(passing) + : m == BY_VALUE + ? s1.passingByValue(passing) + : m == XMLPassingMechanism.DEFAULT + ? s1.passing(passing) + : s1 + ); + + parseKeyword(ctx, "COLUMNS"); + + do { + Name fieldName = parseIdentifier(ctx); + + if (parseKeywordIf(ctx, "FOR ORDINALITY")) { + s2 = s2.column(fieldName).forOrdinality(); + } + else { + XMLTableColumnPathStep s3 = s2.column(fieldName, parseDataType(ctx)); + s2 = parseKeywordIf(ctx, "PATH") ? s3.path(parseStringLiteral(ctx)) : s3; + } + } + while (parseIf(ctx, ',')); + + parse(ctx, ')'); + result = s2; + } else if (parseIf(ctx, '(')) { // A table factor parenthesis can mark the beginning of any of: @@ -6867,18 +6906,25 @@ final class ParserImpl implements Parser { } private static final XMLPassingMechanism parseXMLPassingMechanism(ParserContext ctx) { - parseKeyword(ctx, "PASSING"); + XMLPassingMechanism result = parseXMLPassingMechanismIf(ctx); - if (parseKeywordIf(ctx, "BY")) { - if (parseKeywordIf(ctx, "REF")) - return BY_REF; - else if (parseKeywordIf(ctx, "VALUE")) - return BY_VALUE; - else - throw ctx.expected("REF", "VALUE"); - } + if (result == null) + throw ctx.expected("PASSING"); - return null; + return result; + } + + private static final XMLPassingMechanism parseXMLPassingMechanismIf(ParserContext ctx) { + if (!parseKeywordIf(ctx, "PASSING")) + return null; + else if (!parseKeywordIf(ctx, "BY")) + return XMLPassingMechanism.DEFAULT; + else if (parseKeywordIf(ctx, "REF")) + return BY_REF; + else if (parseKeywordIf(ctx, "VALUE")) + return BY_VALUE; + else + throw ctx.expected("REF", "VALUE"); } private static final List> parseAliasedXMLContent(ParserContext ctx) { diff --git a/jOOQ/src/main/java/org/jooq/impl/XMLExists.java b/jOOQ/src/main/java/org/jooq/impl/XMLExists.java index 8173ab58d1..8485370322 100644 --- a/jOOQ/src/main/java/org/jooq/impl/XMLExists.java +++ b/jOOQ/src/main/java/org/jooq/impl/XMLExists.java @@ -37,22 +37,17 @@ */ package org.jooq.impl; -// ... -import static org.jooq.conf.ParamType.INLINED; -import static org.jooq.impl.Keywords.K_BY; -import static org.jooq.impl.Keywords.K_PASSING; -import static org.jooq.impl.Keywords.K_REF; -import static org.jooq.impl.Keywords.K_VALUE; import static org.jooq.impl.Keywords.K_XMLEXISTS; import static org.jooq.impl.XMLPassingMechanism.BY_REF; import static org.jooq.impl.XMLPassingMechanism.BY_VALUE; +import static org.jooq.impl.XMLTable.acceptPassing; +import static org.jooq.impl.XMLTable.acceptXPath; import org.jooq.Condition; import org.jooq.Context; import org.jooq.Field; import org.jooq.XML; import org.jooq.XMLExistsPassingStep; -import org.jooq.conf.ParamType; /** * @author Lukas Eder @@ -120,29 +115,10 @@ final class XMLExists extends AbstractCondition implements XMLExistsPassingStep .formatIndentStart() .formatNewLine(); + acceptXPath(ctx, xpath); + acceptPassing(ctx, passing, passingMechanism); - - - - - - - - - - - ctx.visit(xpath); - - ctx.formatSeparator() - .visit(K_PASSING); - - if (passingMechanism == BY_REF) - ctx.sql(' ').visit(K_BY).sql(' ').visit(K_REF); - else if (passingMechanism == BY_VALUE) - ctx.sql(' ').visit(K_BY).sql(' ').visit(K_VALUE); - - ctx.sql(' ').visit(passing) - .formatIndentEnd() + ctx.formatIndentEnd() .formatNewLine() .sql(')'); } diff --git a/jOOQ/src/main/java/org/jooq/impl/XMLPassingMechanism.java b/jOOQ/src/main/java/org/jooq/impl/XMLPassingMechanism.java index cd02afaf9b..e0bdc4877d 100644 --- a/jOOQ/src/main/java/org/jooq/impl/XMLPassingMechanism.java +++ b/jOOQ/src/main/java/org/jooq/impl/XMLPassingMechanism.java @@ -37,4 +37,4 @@ */ package org.jooq.impl; -enum XMLPassingMechanism { BY_REF, BY_VALUE } \ No newline at end of file +enum XMLPassingMechanism { BY_REF, BY_VALUE, DEFAULT } \ No newline at end of file diff --git a/jOOQ/src/main/java/org/jooq/impl/XMLQuery.java b/jOOQ/src/main/java/org/jooq/impl/XMLQuery.java index 414527d647..4057aa3b60 100644 --- a/jOOQ/src/main/java/org/jooq/impl/XMLQuery.java +++ b/jOOQ/src/main/java/org/jooq/impl/XMLQuery.java @@ -37,23 +37,19 @@ */ package org.jooq.impl; -// ... -import static org.jooq.conf.ParamType.INLINED; import static org.jooq.impl.DSL.select; import static org.jooq.impl.DSL.unnest; import static org.jooq.impl.DSL.xmlagg; -import static org.jooq.impl.Keywords.K_BY; -import static org.jooq.impl.Keywords.K_PASSING; -import static org.jooq.impl.Keywords.K_REF; import static org.jooq.impl.Names.N_XMLQUERY; import static org.jooq.impl.SQLDataType.XML; import static org.jooq.impl.XMLPassingMechanism.BY_REF; +import static org.jooq.impl.XMLTable.acceptPassing; +import static org.jooq.impl.XMLTable.acceptXPath; import org.jooq.Context; import org.jooq.Field; import org.jooq.XML; import org.jooq.XMLQueryPassingStep; -import org.jooq.conf.ParamType; /** * @author Lukas Eder @@ -127,27 +123,10 @@ final class XMLQuery extends AbstractField implements XMLQueryPassingStep { .formatIndentStart() .formatNewLine(); + acceptXPath(ctx, xpath); + acceptPassing(ctx, passing, passingMechanism); - - - - - - - - - - - ctx.visit(xpath); - - ctx.formatSeparator() - .visit(K_PASSING); - - if (passingMechanism == BY_REF) - ctx.sql(' ').visit(K_BY).sql(' ').visit(K_REF); - - ctx.sql(' ').visit(passing) - .formatIndentEnd() + ctx.formatIndentEnd() .formatNewLine() .sql(')'); diff --git a/jOOQ/src/main/java/org/jooq/impl/XMLTable.java b/jOOQ/src/main/java/org/jooq/impl/XMLTable.java new file mode 100644 index 0000000000..e7b42ecff7 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/XMLTable.java @@ -0,0 +1,300 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Other licenses: + * ----------------------------------------------------------------------------- + * Commercial licenses for this work are available. These replace the above + * ASL 2.0 and offer limited warranties, support, maintenance, and commercial + * database integrations. + * + * For more information, please visit: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq.impl; + +// ... +import static org.jooq.conf.ParamType.INLINED; +import static org.jooq.impl.DSL.inline; +import static org.jooq.impl.Keywords.K_BY; +import static org.jooq.impl.Keywords.K_COLUMNS; +import static org.jooq.impl.Keywords.K_FOR; +import static org.jooq.impl.Keywords.K_ORDINALITY; +import static org.jooq.impl.Keywords.K_PASSING; +import static org.jooq.impl.Keywords.K_PATH; +import static org.jooq.impl.Keywords.K_REF; +import static org.jooq.impl.Keywords.K_VALUE; +import static org.jooq.impl.Keywords.K_XMLTABLE; +import static org.jooq.impl.Names.N_XMLTABLE; +import static org.jooq.impl.XMLPassingMechanism.BY_REF; +import static org.jooq.impl.XMLPassingMechanism.BY_VALUE; + +import java.util.ArrayList; +import java.util.List; + +import org.jooq.Context; +import org.jooq.DataType; +import org.jooq.Field; +import org.jooq.Name; +import org.jooq.Record; +import org.jooq.TableOptions; +import org.jooq.XML; +import org.jooq.XMLTableColumnPathStep; +import org.jooq.XMLTablePassingStep; +import org.jooq.conf.ParamType; + +/** + * @author Lukas Eder + */ +final class XMLTable +extends AbstractTable +implements + XMLTablePassingStep, + XMLTableColumnPathStep { + + /** + * Generated UID + */ + private static final long serialVersionUID = -4881363881968319258L; + private final Field xpath; + private final Field passing; + private final XMLPassingMechanism passingMechanism; + private final QueryPartList columns; + private transient Fields fields; + + XMLTable(Field xpath) { + this(xpath, null, null, null); + } + + private XMLTable( + Field xpath, + Field passing, + XMLPassingMechanism passingMechanism, + QueryPartList columns + ) { + super(TableOptions.expression(), N_XMLTABLE); + + this.xpath = xpath; + this.passing = passing; + this.passingMechanism = passingMechanism; + this.columns = columns == null ? new QueryPartList<>() : columns; + } + + // ------------------------------------------------------------------------- + // XXX: DSL API + // ------------------------------------------------------------------------- + + @Override + public final XMLTable passing(XML xml) { + return passing(Tools.field(xml)); + } + + @Override + public final XMLTable passing(Field xml) { + return new XMLTable(xpath, xml, null, columns); + } + + @Override + public final XMLTable passingByRef(XML xml) { + return passingByRef(Tools.field(xml)); + } + + @Override + public final XMLTable passingByRef(Field xml) { + return new XMLTable(xpath, xml, BY_REF, columns); + } + + @Override + public final XMLTable passingByValue(XML xml) { + return passingByRef(Tools.field(xml)); + } + + @Override + public final XMLTable passingByValue(Field xml) { + return new XMLTable(xpath, xml, BY_VALUE, columns); + } + + @Override + public final XMLTable column(String name) { + return column(DSL.field(name)); + } + + @Override + public final XMLTable column(Name name) { + return column(DSL.field(name)); + } + + @Override + public final XMLTable column(Field name) { + return column(name, name.getDataType()); + } + + @Override + public final XMLTable column(String name, DataType type) { + return column(DSL.field(name), type); + } + + @Override + public final XMLTable column(Name name, DataType type) { + return column(DSL.field(name), type); + } + + @Override + public final XMLTable column(Field name, DataType type) { + QueryPartList c = new QueryPartList<>(columns); + c.add(new XMLTableColumn(name, type, false, null)); + return new XMLTable(xpath, passing, passingMechanism, c); + } + + @Override + public final XMLTable forOrdinality() { + return path0(true, null); + } + + @Override + public final XMLTable path(String path) { + return path0(false, path); + } + + private final XMLTable path0(boolean forOrdinality, String path) { + QueryPartList c = new QueryPartList<>(columns); + int i = c.size() - 1; + XMLTableColumn last = c.get(i); + c.set(i, new XMLTableColumn(last.field, last.type, forOrdinality, path)); + return new XMLTable(xpath, passing, passingMechanism, c); + } + + // ------------------------------------------------------------------------- + // XXX: Table API + // ------------------------------------------------------------------------- + + @Override + public final Class getRecordType() { + return RecordImplN.class; + } + + @Override + final Fields fields0() { + if (fields == null) { + List> f = new ArrayList<>(); + + for (XMLTableColumn c : columns) + f.add(c.field.getDataType() == c.type ? c.field : field(c.field.getQualifiedName(), c.type)); + + fields = new Fields<>(f); + } + + return fields; + } + + // ------------------------------------------------------------------------- + // XXX: QueryPart API + // ------------------------------------------------------------------------- + + @Override + public final void accept(Context ctx) { + ctx.visit(K_XMLTABLE).sql('(') + .formatIndentStart() + .formatNewLine(); + + acceptXPath(ctx, xpath); + if (passing != null) + acceptPassing(ctx, passing, passingMechanism); + + ctx.formatSeparator() + .visit(K_COLUMNS).sql(' ').visit(columns); + + ctx.formatIndentEnd() + .formatNewLine() + .sql(')'); + } + + static final void acceptXPath(Context ctx, Field xpath) { + + + + + + + + + + + + ctx.visit(xpath); + } + + static final void acceptPassing(Context ctx, Field passing, XMLPassingMechanism passingMechanism) { + ctx.formatSeparator() + .visit(K_PASSING); + + if (passingMechanism == BY_REF) + ctx.sql(' ').visit(K_BY).sql(' ').visit(K_REF); + else if (passingMechanism == BY_VALUE) + ctx.sql(' ').visit(K_BY).sql(' ').visit(K_VALUE); + + ctx.sql(' ').visit(passing); + } + + private static class XMLTableColumn extends AbstractQueryPart { + + /** + * Generated UID + */ + private static final long serialVersionUID = 783627375014050176L; + + final Field field; + final DataType type; + final boolean forOrdinality; + final String path; + + XMLTableColumn(Field field, DataType type, boolean forOrdinality, String path) { + this.field = field; + this.type = type; + this.forOrdinality = forOrdinality; + this.path = path; + } + + @Override + public final void accept(Context ctx) { + boolean previous = ctx.qualify(); + + ctx.qualify(false) + .visit(field) + .qualify(previous) + .sql(' '); + + if (forOrdinality) + ctx.visit(K_FOR).sql(' ').visit(K_ORDINALITY); + else + Tools.toSQLDDLTypeDeclaration(ctx, type); + + if (path != null) + ctx.sql(' ').visit(K_PATH).sql(' ').visit(inline(path)); + } + } +}