Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 34 additions & 2 deletions src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,38 @@ public class CCJSqlParser extends AbstractJSqlParser<CCJSqlParser> {
}
}

/**
* XML constructor functions take a bare leading keyword before their first argument,
* e.g. XMLPARSE(CONTENT expr), XMLPARSE(DOCUMENT expr), XMLELEMENT(NAME ident, ...).
* Guarded by the function name so that CONTENT/DOCUMENT/NAME stay plain identifiers
* everywhere else, e.g. f(content).
*/
private void consumeXmlLeadingKeyword(Function function) {
String name = function.getName();
if (name == null) {
return;
}
name = name.toUpperCase(java.util.Locale.ROOT);
if (!"XMLPARSE".equals(name) && !"XMLELEMENT".equals(name) && !"XMLFOREST".equals(name)) {
return;
}
Token token = getToken(1);
if (token.kind != S_IDENTIFIER && token.kind != K_NAME) {
return;
}
String keyword = token.image.toUpperCase(java.util.Locale.ROOT);
if (!"CONTENT".equals(keyword) && !"DOCUMENT".equals(keyword) && !"NAME".equals(keyword)) {
return;
}
// only a bare leading keyword: an expression must follow
Token next = getToken(2);
if (next.kind == EOF || ")".equals(next.image) || ",".equals(next.image)
|| ".".equals(next.image) || "(".equals(next.image)) {
return;
}
function.setExtraKeyword(getNextToken().image);
}

private boolean isKeywordArgumentAhead() {
Token t = getToken(1);
if (t.kind == EOF || t.image.equals(")")) return false;
Expand Down Expand Up @@ -10154,9 +10186,9 @@ Function InternalFunction(boolean escaped):
}
{
[ LOOKAHEAD(2) prefixToken = <K_APPROXIMATE> ]
funcName = RelObjectNames() { if (prefixToken!=null) funcName.getNames().add(0, prefixToken.image ); }
funcName = RelObjectNames() { if (prefixToken!=null) funcName.getNames().add(0, prefixToken.image ); retval.setName(funcName.getNames()); }

"("
"(" { consumeXmlLeadingKeyword(retval); }
[
LOOKAHEAD(2) [
LOOKAHEAD(2) (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*-
* #%L
* JSQLParser library
* %%
* Copyright (C) 2004 - 2026 JSQLParser
* %%
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
* #L%
*/
package net.sf.jsqlparser.statement.select;

import net.sf.jsqlparser.JSQLParserException;
import org.junit.jupiter.api.Test;

import static net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed;

/**
* XML constructor functions taking a bare leading keyword before their first argument, e.g.
* {@code XMLPARSE(CONTENT expr)}.
*/
public class SelectXMLParseTest {

@Test
public void testXmlParseContent() throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed("SELECT xmlparse(content a) FROM mytable");
}

@Test
public void testXmlParseDocument() throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed("SELECT xmlparse(document a) FROM mytable");
}

@Test
public void testXmlParseUpperCaseKeyword() throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed("SELECT xmlparse(CONTENT a) FROM mytable");
}

@Test
public void testXmlAggWithXmlParse() throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed(
"SELECT xmlagg(xmlparse(content sn.CODE || ',') ORDER BY sn.CODE).getclobval() AS SN FROM GV_SYS_CODEINFO sn");
}

@Test
public void testXmlElementName() throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed("SELECT xmlelement(name a, b) FROM mytable");
}

@Test
public void testXmlForestName() throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed("SELECT xmlforest(name a) FROM mytable");
}

/**
* CONTENT, DOCUMENT and NAME must stay plain identifiers everywhere else.
*/
@Test
public void testKeywordsRemainIdentifiers() throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed("SELECT content, document, name FROM mytable");
assertSqlCanBeParsedAndDeparsed("SELECT content FROM content c WHERE c.document = 1");
assertSqlCanBeParsedAndDeparsed("SELECT t.name AS document FROM mytable t");
assertSqlCanBeParsedAndDeparsed("SELECT f(content) FROM mytable");
assertSqlCanBeParsedAndDeparsed("SELECT xmlparse(content) FROM mytable");
}
}
Loading