mirror of https://github.com/alibaba/druid.git
Compare commits
5 Commits
5268c1fe1e
...
ff4471b6d6
| Author | SHA1 | Date |
|---|---|---|
|
|
ff4471b6d6 | |
|
|
c8b12f0701 | |
|
|
b1fda6d046 | |
|
|
1121b304c2 | |
|
|
11376b5115 |
|
|
@ -99,6 +99,7 @@ public enum SQLBinaryOperator {
|
||||||
BooleanXor("XOR", 150),
|
BooleanXor("XOR", 150),
|
||||||
BooleanOr("OR", 160),
|
BooleanOr("OR", 160),
|
||||||
Assignment(":=", 169),
|
Assignment(":=", 169),
|
||||||
|
Blank("", 170),
|
||||||
|
|
||||||
PG_And("&&", 140),
|
PG_And("&&", 140),
|
||||||
PG_ST_DISTANCE("<->", 20);
|
PG_ST_DISTANCE("<->", 20);
|
||||||
|
|
|
||||||
|
|
@ -29,16 +29,30 @@ public class SQLVariantRefExpr extends SQLExprImpl {
|
||||||
|
|
||||||
private boolean global;
|
private boolean global;
|
||||||
private boolean session;
|
private boolean session;
|
||||||
|
private boolean templateParameter;
|
||||||
|
private boolean hasPrefixComma;
|
||||||
|
|
||||||
private int index = -1;
|
private int index = -1;
|
||||||
|
|
||||||
public SQLVariantRefExpr(String name) {
|
public SQLVariantRefExpr(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
if (name.startsWith("${") && name.endsWith("}")) {
|
||||||
|
this.templateParameter = true;
|
||||||
|
} else {
|
||||||
|
this.templateParameter = false;
|
||||||
|
}
|
||||||
|
this.hasPrefixComma = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SQLVariantRefExpr(String name, SQLObject parent) {
|
public SQLVariantRefExpr(String name, SQLObject parent) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
|
if (name.startsWith("${") && name.endsWith("}")) {
|
||||||
|
this.templateParameter = true;
|
||||||
|
} else {
|
||||||
|
this.templateParameter = false;
|
||||||
|
}
|
||||||
|
this.hasPrefixComma = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SQLVariantRefExpr(String name, boolean global) {
|
public SQLVariantRefExpr(String name, boolean global) {
|
||||||
|
|
@ -70,6 +84,22 @@ public class SQLVariantRefExpr extends SQLExprImpl {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isTemplateParameter() {
|
||||||
|
return templateParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTemplateParameter(boolean templateParameter) {
|
||||||
|
this.templateParameter = templateParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHasPrefixComma() {
|
||||||
|
return hasPrefixComma;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHasPrefixComma(boolean hasPrefixComma) {
|
||||||
|
this.hasPrefixComma = hasPrefixComma;
|
||||||
|
}
|
||||||
|
|
||||||
public void output(StringBuilder buf) {
|
public void output(StringBuilder buf) {
|
||||||
buf.append(this.name);
|
buf.append(this.name);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,237 @@
|
||||||
|
package com.alibaba.druid.sql.dialect.oracle.ast.expr;
|
||||||
|
|
||||||
|
import com.alibaba.druid.sql.ast.*;
|
||||||
|
import com.alibaba.druid.sql.dialect.oracle.ast.OracleSQLObjectImpl;
|
||||||
|
import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor;
|
||||||
|
import com.alibaba.druid.sql.visitor.SQLASTVisitor;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class OracleJSONTableExpr extends SQLExprImpl implements OracleExpr {
|
||||||
|
private final List<Column> columns = new ArrayList<Column>();
|
||||||
|
private SQLExpr expr;
|
||||||
|
private SQLExpr path;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept0(OracleASTVisitor v) {
|
||||||
|
if (v.visit(this)) {
|
||||||
|
acceptChild(v, expr);
|
||||||
|
acceptChild(v, path);
|
||||||
|
acceptChild(v, columns);
|
||||||
|
}
|
||||||
|
v.endVisit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<SQLObject> getChildren() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SQLExpr getExpr() {
|
||||||
|
return expr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExpr(SQLExpr x) {
|
||||||
|
if (x != null) {
|
||||||
|
x.setParent(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.expr = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SQLExpr getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPath(SQLExpr x) {
|
||||||
|
if (x != null) {
|
||||||
|
x.setParent(this);
|
||||||
|
}
|
||||||
|
this.path = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Column> getColumns() {
|
||||||
|
return columns;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addColumn(Column column) {
|
||||||
|
column.setParent(this);
|
||||||
|
this.columns.add(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Column extends OracleSQLObjectImpl {
|
||||||
|
private final List<Column> nestedColumns = new ArrayList<Column>();
|
||||||
|
private SQLName name;
|
||||||
|
private SQLDataType dataType;
|
||||||
|
private SQLExpr path;
|
||||||
|
private boolean ordinality;
|
||||||
|
private boolean exists;
|
||||||
|
private SQLExpr onError;
|
||||||
|
private SQLExpr onEmpty;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept0(OracleASTVisitor v) {
|
||||||
|
if (v.visit(this)) {
|
||||||
|
acceptChild(v, name);
|
||||||
|
acceptChild(v, dataType);
|
||||||
|
acceptChild(v, path);
|
||||||
|
acceptChild(v, onEmpty);
|
||||||
|
acceptChild(v, onError);
|
||||||
|
}
|
||||||
|
v.endVisit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SQLName getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(SQLName x) {
|
||||||
|
if (x != null) {
|
||||||
|
x.setParent(this);
|
||||||
|
}
|
||||||
|
this.name = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SQLDataType getDataType() {
|
||||||
|
return dataType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDataType(SQLDataType x) {
|
||||||
|
if (x != null) {
|
||||||
|
x.setParent(this);
|
||||||
|
}
|
||||||
|
this.dataType = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SQLExpr getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPath(SQLExpr x) {
|
||||||
|
if (x != null) {
|
||||||
|
x.setParent(this);
|
||||||
|
}
|
||||||
|
this.path = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOrdinality() {
|
||||||
|
return ordinality;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrdinality(boolean ordinality) {
|
||||||
|
this.ordinality = ordinality;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isExists() {
|
||||||
|
return exists;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExists(boolean exists) {
|
||||||
|
this.exists = exists;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SQLExpr getOnError() {
|
||||||
|
return onError;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnError(SQLExpr x) {
|
||||||
|
if (x != null) {
|
||||||
|
x.setParent(this);
|
||||||
|
}
|
||||||
|
this.onError = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SQLExpr getOnEmpty() {
|
||||||
|
return onEmpty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnEmpty(SQLExpr x) {
|
||||||
|
if (x != null) {
|
||||||
|
x.setParent(this);
|
||||||
|
}
|
||||||
|
this.onEmpty = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Column> getNestedColumns() {
|
||||||
|
return nestedColumns;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addNestedColumn(Column column) {
|
||||||
|
column.setParent(this);
|
||||||
|
this.nestedColumns.add(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Column clone() {
|
||||||
|
Column result = new Column();
|
||||||
|
for(Column nestedColumn : nestedColumns) {
|
||||||
|
result.addNestedColumn(nestedColumn.clone());
|
||||||
|
}
|
||||||
|
if(name != null) {
|
||||||
|
result.setName(name.clone());
|
||||||
|
}
|
||||||
|
if(dataType != null) {
|
||||||
|
result.setDataType(dataType.clone());
|
||||||
|
}
|
||||||
|
if(path != null) {
|
||||||
|
result.setPath(path.clone());
|
||||||
|
}
|
||||||
|
result.setOrdinality(ordinality);
|
||||||
|
result.setExists(exists);
|
||||||
|
if(onError != null) {
|
||||||
|
result.setOnEmpty(onError.clone());
|
||||||
|
}
|
||||||
|
if(onEmpty != null) {
|
||||||
|
result.setOnEmpty(onEmpty.clone());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (!(o instanceof Column)) return false;
|
||||||
|
Column column = (Column) o;
|
||||||
|
return ordinality == column.ordinality && exists == column.exists && Objects.equals(nestedColumns, column.nestedColumns) && Objects.equals(name, column.name) && Objects.equals(dataType, column.dataType) && Objects.equals(path, column.path) && Objects.equals(onError, column.onError) && Objects.equals(onEmpty, column.onEmpty);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(nestedColumns, name, dataType, path, ordinality, exists, onError, onEmpty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (!(o instanceof OracleJSONTableExpr)) return false;
|
||||||
|
OracleJSONTableExpr that = (OracleJSONTableExpr) o;
|
||||||
|
return Objects.equals(columns, that.columns) && Objects.equals(expr, that.expr) && Objects.equals(path, that.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(columns, expr, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SQLExpr clone() {
|
||||||
|
OracleJSONTableExpr result = new OracleJSONTableExpr();
|
||||||
|
for(Column column : columns) {
|
||||||
|
result.addColumn(column.clone());
|
||||||
|
}
|
||||||
|
if(expr != null) {
|
||||||
|
result.setExpr(expr.clone());
|
||||||
|
}
|
||||||
|
if(path != null) {
|
||||||
|
result.setPath(path.clone());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void accept0(SQLASTVisitor v) {
|
||||||
|
this.accept0((OracleASTVisitor) v);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -491,11 +491,103 @@ public class OracleExprParser extends SQLExprParser {
|
||||||
sqlExpr = new SQLIdentifierExpr(lexer.stringVal());
|
sqlExpr = new SQLIdentifierExpr(lexer.stringVal());
|
||||||
lexer.nextToken();
|
lexer.nextToken();
|
||||||
return primaryRest(sqlExpr);
|
return primaryRest(sqlExpr);
|
||||||
|
case IDENTIFIER:
|
||||||
|
// if match json_table, then process and return OracleJSONTableExpr object. else, continue invoke primary()
|
||||||
|
if (lexer.identifierEquals("JSON_TABLE")) {
|
||||||
|
lexer.nextToken();
|
||||||
|
accept(Token.LPAREN);
|
||||||
|
OracleJSONTableExpr jsonTable = new OracleJSONTableExpr();
|
||||||
|
jsonTable.setExpr(this.expr());
|
||||||
|
accept(Token.COMMA);
|
||||||
|
jsonTable.setPath(this.expr());
|
||||||
|
acceptIdentifier("COLUMNS");
|
||||||
|
accept(Token.LPAREN);
|
||||||
|
for (; lexer.token() != Token.RPAREN; ) {
|
||||||
|
jsonTable.addColumn(parseJsonTableColumn());
|
||||||
|
if (lexer.token() == Token.COMMA) {
|
||||||
|
lexer.nextToken();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
accept(Token.RPAREN);
|
||||||
|
accept(Token.RPAREN);
|
||||||
|
return jsonTable;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return super.primary();
|
return super.primary();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected OracleJSONTableExpr.Column parseJsonTableColumn() {
|
||||||
|
OracleJSONTableExpr.Column column = new OracleJSONTableExpr.Column();
|
||||||
|
|
||||||
|
SQLName name = this.name();
|
||||||
|
column.setName(name);
|
||||||
|
if (lexer.token() == Token.FOR) {
|
||||||
|
lexer.nextToken();
|
||||||
|
acceptIdentifier("ORDINALITY");
|
||||||
|
column.setOrdinality(true);
|
||||||
|
} else {
|
||||||
|
boolean nested = name instanceof SQLIdentifierExpr
|
||||||
|
&& name.nameHashCode64() == FnvHash.Constants.NESTED;
|
||||||
|
if (!nested) {
|
||||||
|
column.setDataType(
|
||||||
|
this.parseDataType());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lexer.token() == Token.EXISTS) {
|
||||||
|
lexer.nextToken();
|
||||||
|
column.setExists(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lexer.identifierEquals(FnvHash.Constants.PATH)) {
|
||||||
|
lexer.nextToken();
|
||||||
|
column.setPath(
|
||||||
|
this.primary());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name instanceof SQLIdentifierExpr
|
||||||
|
&& name.nameHashCode64() == FnvHash.Constants.NESTED) {
|
||||||
|
acceptIdentifier("COLUMNS");
|
||||||
|
accept(Token.LPAREN);
|
||||||
|
for (; lexer.token() != Token.RPAREN; ) {
|
||||||
|
OracleJSONTableExpr.Column nestedColumn = parseJsonTableColumn();
|
||||||
|
column.addNestedColumn(nestedColumn);
|
||||||
|
|
||||||
|
if (lexer.token() == Token.COMMA) {
|
||||||
|
lexer.nextToken();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
accept(Token.RPAREN);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 2; ++i) {
|
||||||
|
if (lexer.identifierEquals("ERROR")
|
||||||
|
|| lexer.token() == Token.DEFAULT
|
||||||
|
|| lexer.token() == Token.NULL) {
|
||||||
|
if (lexer.token() == Token.DEFAULT) {
|
||||||
|
lexer.nextToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
SQLExpr expr = this.expr();
|
||||||
|
accept(Token.ON);
|
||||||
|
if (lexer.identifierEquals("ERROR")) {
|
||||||
|
lexer.nextToken();
|
||||||
|
column.setOnError(expr);
|
||||||
|
} else {
|
||||||
|
acceptIdentifier("EMPTY");
|
||||||
|
column.setOnEmpty(expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return column;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected SQLExpr methodRest(SQLExpr expr, boolean acceptLPAREN) {
|
protected SQLExpr methodRest(SQLExpr expr, boolean acceptLPAREN) {
|
||||||
if (acceptLPAREN) {
|
if (acceptLPAREN) {
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,22 @@ public interface OracleASTVisitor extends SQLASTVisitor {
|
||||||
default void endVisit(OracleOuterExpr x) {
|
default void endVisit(OracleOuterExpr x) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
default boolean visit(OracleJSONTableExpr x) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
default void endVisit(OracleJSONTableExpr x) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
default boolean visit(OracleJSONTableExpr.Column x) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
default void endVisit(OracleJSONTableExpr.Column x) {
|
||||||
|
}
|
||||||
|
|
||||||
default void endVisit(OracleSelectJoin x) {
|
default void endVisit(OracleSelectJoin x) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2995,4 +2995,77 @@ public class OracleOutputVisitor extends SQLASTOutputVisitor implements OracleAS
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean visit(OracleJSONTableExpr x) {
|
||||||
|
print0(ucase ? "JSON_TABLE(" : "json_table(");
|
||||||
|
x.getExpr().accept(this);
|
||||||
|
print(',');
|
||||||
|
x.getPath().accept(this);
|
||||||
|
incrementIndent();
|
||||||
|
println();
|
||||||
|
print0(ucase ? "COLUMNS (" : "columns (");
|
||||||
|
incrementIndent();
|
||||||
|
println();
|
||||||
|
printlnAndAccept(x.getColumns(), ",");
|
||||||
|
decrementIndent();
|
||||||
|
println();
|
||||||
|
print(')');
|
||||||
|
decrementIndent();
|
||||||
|
println();
|
||||||
|
print(')');
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean visit(OracleJSONTableExpr.Column x) {
|
||||||
|
x.getName().accept(this);
|
||||||
|
|
||||||
|
if (x.isOrdinality()) {
|
||||||
|
print0(ucase ? " FOR ORDINALITY" : " for ordinality");
|
||||||
|
}
|
||||||
|
|
||||||
|
SQLDataType dataType = x.getDataType();
|
||||||
|
if (dataType != null) {
|
||||||
|
print(' ');
|
||||||
|
dataType.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x.isExists()) {
|
||||||
|
print0(ucase ? " EXISTS" : " exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
SQLExpr path = x.getPath();
|
||||||
|
if (path != null) {
|
||||||
|
print0(ucase ? " PATH " : " path ");
|
||||||
|
path.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<OracleJSONTableExpr.Column> nestedColumns = x.getNestedColumns();
|
||||||
|
if (nestedColumns.size() > 0) {
|
||||||
|
print0(ucase ? " COLUMNS (" : " columns (");
|
||||||
|
printAndAccept(nestedColumns, ", ");
|
||||||
|
print(')');
|
||||||
|
}
|
||||||
|
|
||||||
|
SQLExpr onEmpty = x.getOnEmpty();
|
||||||
|
if (onEmpty != null) {
|
||||||
|
print(' ');
|
||||||
|
if (!(onEmpty instanceof SQLNullExpr || onEmpty instanceof SQLIdentifierExpr)) {
|
||||||
|
print0(ucase ? "DEFAULT " : "default ");
|
||||||
|
}
|
||||||
|
onEmpty.accept(this);
|
||||||
|
print0(ucase ? " ON EMPTY" : " on empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
SQLExpr onError = x.getOnError();
|
||||||
|
if (onError != null) {
|
||||||
|
print(' ');
|
||||||
|
if (!(onEmpty instanceof SQLNullExpr || onEmpty instanceof SQLIdentifierExpr)) {
|
||||||
|
print0(ucase ? "DEFAULT " : "default ");
|
||||||
|
}
|
||||||
|
onError.accept(this);
|
||||||
|
print0(ucase ? " ON ERROR" : " on error");
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2278,7 +2278,7 @@ public class Lexer {
|
||||||
if (ch != ':' && ch != '#' && ch != '$' && !(ch == '@' && dialectFeatureEnabled(ScanVariableAt))) {
|
if (ch != ':' && ch != '#' && ch != '$' && !(ch == '@' && dialectFeatureEnabled(ScanVariableAt))) {
|
||||||
throw new ParserException("illegal variable. " + info());
|
throw new ParserException("illegal variable. " + info());
|
||||||
}
|
}
|
||||||
|
boolean templateParameter = false;
|
||||||
mark = pos;
|
mark = pos;
|
||||||
bufPos = 1;
|
bufPos = 1;
|
||||||
char ch;
|
char ch;
|
||||||
|
|
@ -2296,13 +2296,14 @@ public class Lexer {
|
||||||
boolean ident = false;
|
boolean ident = false;
|
||||||
for (; ; ) {
|
for (; ; ) {
|
||||||
ch = charAt(++pos);
|
ch = charAt(++pos);
|
||||||
if (isEOF() || ch == ';' || ch == ';' || ch == '\r' || ch == '\n') {
|
if (isEOF() || (templateParameter && (ch == ';' || ch == ';' || ch == '\r'))) {
|
||||||
pos--;
|
pos--;
|
||||||
bufPos--;
|
bufPos--;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ch == '}' && !ident) {
|
if (ch == '}' && !ident) {
|
||||||
|
templateParameter = false;
|
||||||
if (isIdentifierChar(charAt(pos + 1))) {
|
if (isIdentifierChar(charAt(pos + 1))) {
|
||||||
bufPos++;
|
bufPos++;
|
||||||
ident = true;
|
ident = true;
|
||||||
|
|
@ -2313,6 +2314,7 @@ public class Lexer {
|
||||||
|
|
||||||
if (ident && ch == '$') {
|
if (ident && ch == '$') {
|
||||||
if (charAt(pos + 1) == '{') {
|
if (charAt(pos + 1) == '{') {
|
||||||
|
templateParameter = true;
|
||||||
bufPos++;
|
bufPos++;
|
||||||
ident = false;
|
ident = false;
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -2323,7 +2325,7 @@ public class Lexer {
|
||||||
if (isWhitespace(ch)) {
|
if (isWhitespace(ch)) {
|
||||||
pos--;
|
pos--;
|
||||||
break;
|
break;
|
||||||
} else if (ch == ',' || ch == ')' || ch == '(' || ch == ';') {
|
} else if (ch == ',' || ch == ')' || ch == '(') {
|
||||||
pos--;
|
pos--;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2927,8 +2927,7 @@ public class SQLExprParser extends SQLParser {
|
||||||
SQLSelectOrderByItem item = parseSelectOrderByItem();
|
SQLSelectOrderByItem item = parseSelectOrderByItem();
|
||||||
item.setParent(parent);
|
item.setParent(parent);
|
||||||
items.add(item);
|
items.add(item);
|
||||||
while (lexer.token == Token.COMMA) {
|
while (lexer.nextIf(Token.COMMA) || (item.getExpr() instanceof SQLVariantRefExpr && lexer.token == IDENTIFIER)) {
|
||||||
lexer.nextToken();
|
|
||||||
item = parseSelectOrderByItem();
|
item = parseSelectOrderByItem();
|
||||||
item.setParent(parent);
|
item.setParent(parent);
|
||||||
items.add(item);
|
items.add(item);
|
||||||
|
|
@ -3532,6 +3531,14 @@ public class SQLExprParser extends SQLParser {
|
||||||
SQLBinaryOperator operator = andRestGetAndOperator();
|
SQLBinaryOperator operator = andRestGetAndOperator();
|
||||||
|
|
||||||
expr = new SQLBinaryOpExpr(expr, operator, rightExp, dbType);
|
expr = new SQLBinaryOpExpr(expr, operator, rightExp, dbType);
|
||||||
|
} else if (token == Token.VARIANT) {
|
||||||
|
String value = lexer.stringVal();
|
||||||
|
lexer.nextToken();
|
||||||
|
SQLExpr variantExpr = new SQLVariantRefExpr(value);
|
||||||
|
if (lexer.token == Token.IN) {
|
||||||
|
variantExpr = inRest(variantExpr);
|
||||||
|
}
|
||||||
|
expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Blank, variantExpr, dbType);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -3619,6 +3626,14 @@ public class SQLExprParser extends SQLParser {
|
||||||
SQLBinaryOperator op = orRestGetOrOperator();
|
SQLBinaryOperator op = orRestGetOrOperator();
|
||||||
|
|
||||||
expr = new SQLBinaryOpExpr(expr, op, rightExp, dbType);
|
expr = new SQLBinaryOpExpr(expr, op, rightExp, dbType);
|
||||||
|
} else if (lexer.token == Token.VARIANT) {
|
||||||
|
String value = lexer.stringVal();
|
||||||
|
lexer.nextToken();
|
||||||
|
SQLExpr variantExpr = new SQLVariantRefExpr(value);
|
||||||
|
if (lexer.token == Token.IN) {
|
||||||
|
variantExpr = inRest(variantExpr);
|
||||||
|
}
|
||||||
|
expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Blank, variantExpr, dbType);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -3977,6 +3992,11 @@ public class SQLExprParser extends SQLParser {
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case VARIANT:
|
||||||
|
rightExp = new SQLVariantRefExpr(lexer.stringVal);
|
||||||
|
expr = new SQLBinaryOpExpr(expr, SQLBinaryOperator.Blank, rightExp, dbType);
|
||||||
|
lexer.nextToken();
|
||||||
|
return expr;
|
||||||
default:
|
default:
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
@ -5946,46 +5966,47 @@ public class SQLExprParser extends SQLParser {
|
||||||
|
|
||||||
String alias;
|
String alias;
|
||||||
List<String> aliasList = null;
|
List<String> aliasList = null;
|
||||||
switch (lexer.token) {
|
if (expr instanceof SQLVariantRefExpr && ((SQLVariantRefExpr) expr).isTemplateParameter() && lexer.token != AS) {
|
||||||
case FULL:
|
alias = null;
|
||||||
case TABLESPACE:
|
} else {
|
||||||
alias = lexer.stringVal();
|
switch (lexer.token) {
|
||||||
lexer.nextToken();
|
case FULL:
|
||||||
break;
|
case TABLESPACE:
|
||||||
case AS:
|
alias = lexer.stringVal();
|
||||||
lexer.nextTokenAlias();
|
|
||||||
if (lexer.token == Token.LITERAL_INT) {
|
|
||||||
alias = '"' + lexer.stringVal() + '"';
|
|
||||||
lexer.nextToken();
|
lexer.nextToken();
|
||||||
} else if (lexer.token == Token.LPAREN) {
|
break;
|
||||||
lexer.nextToken();
|
case AS:
|
||||||
aliasList = new ArrayList<String>();
|
lexer.nextTokenAlias();
|
||||||
|
if (lexer.token == Token.LITERAL_INT) {
|
||||||
for (; ; ) {
|
alias = '"' + lexer.stringVal() + '"';
|
||||||
String stringVal = lexer.stringVal();
|
|
||||||
lexer.nextToken();
|
lexer.nextToken();
|
||||||
|
} else if (lexer.token == Token.LPAREN) {
|
||||||
aliasList.add(stringVal);
|
lexer.nextToken();
|
||||||
|
aliasList = new ArrayList<String>();
|
||||||
if (lexer.token() == Token.COMMA) {
|
for (; ; ) {
|
||||||
|
String stringVal = lexer.stringVal();
|
||||||
lexer.nextToken();
|
lexer.nextToken();
|
||||||
continue;
|
aliasList.add(stringVal);
|
||||||
|
if (lexer.token() == Token.COMMA) {
|
||||||
|
lexer.nextToken();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
accept(Token.RPAREN);
|
||||||
}
|
|
||||||
accept(Token.RPAREN);
|
|
||||||
|
|
||||||
|
alias = null;
|
||||||
|
} else {
|
||||||
|
alias = alias();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case EOF:
|
||||||
alias = null;
|
alias = null;
|
||||||
} else {
|
break;
|
||||||
alias = alias();
|
default:
|
||||||
}
|
alias = as();
|
||||||
break;
|
break;
|
||||||
case EOF:
|
}
|
||||||
alias = null;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
alias = as();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (alias == null && isEnabled(SQLParserFeature.SelectItemGenerateAlias)
|
if (alias == null && isEnabled(SQLParserFeature.SelectItemGenerateAlias)
|
||||||
|
|
|
||||||
|
|
@ -756,6 +756,7 @@ public class SQLSelectParser extends SQLParser {
|
||||||
case BANGEQ:
|
case BANGEQ:
|
||||||
case LIKE:
|
case LIKE:
|
||||||
case NOT:
|
case NOT:
|
||||||
|
case VARIANT:
|
||||||
where = this.exprParser.relationalRest(where);
|
where = this.exprParser.relationalRest(where);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
@ -1122,11 +1123,16 @@ public class SQLSelectParser extends SQLParser {
|
||||||
|
|
||||||
protected void parseSelectList(SQLSelectQueryBlock queryBlock) {
|
protected void parseSelectList(SQLSelectQueryBlock queryBlock) {
|
||||||
final List<SQLSelectItem> selectList = queryBlock.getSelectList();
|
final List<SQLSelectItem> selectList = queryBlock.getSelectList();
|
||||||
|
boolean hasComma = true;
|
||||||
for (; ; ) {
|
for (; ; ) {
|
||||||
final SQLSelectItem selectItem = this.exprParser.parseSelectItem();
|
final SQLSelectItem selectItem = this.exprParser.parseSelectItem();
|
||||||
selectList.add(selectItem);
|
selectList.add(selectItem);
|
||||||
selectItem.setParent(queryBlock);
|
selectItem.setParent(queryBlock);
|
||||||
|
if (!hasComma && selectItem.getExpr() instanceof SQLVariantRefExpr) {
|
||||||
|
SQLVariantRefExpr sqlVariantRefExpr = (SQLVariantRefExpr) selectItem.getExpr();
|
||||||
|
sqlVariantRefExpr.setHasPrefixComma(false);
|
||||||
|
hasComma = true;
|
||||||
|
}
|
||||||
//https://github.com/alibaba/druid/issues/5708
|
//https://github.com/alibaba/druid/issues/5708
|
||||||
if (lexer.hasComment()
|
if (lexer.hasComment()
|
||||||
&& lexer.isKeepComments()
|
&& lexer.isKeepComments()
|
||||||
|
|
@ -1136,7 +1142,14 @@ public class SQLSelectParser extends SQLParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lexer.token != Token.COMMA) {
|
if (lexer.token != Token.COMMA) {
|
||||||
break;
|
if (lexer.token == Token.VARIANT) {
|
||||||
|
hasComma = false;
|
||||||
|
continue;
|
||||||
|
} else if (selectItem.getExpr() instanceof SQLVariantRefExpr && ((SQLVariantRefExpr) selectItem.getExpr()).isTemplateParameter() && lexer.token != Token.FROM) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int line = lexer.line;
|
int line = lexer.line;
|
||||||
|
|
|
||||||
|
|
@ -222,11 +222,11 @@ public class SQLStatementParser extends SQLParser {
|
||||||
case SELECT: {
|
case SELECT: {
|
||||||
MySqlHintStatement hintStatement = null;
|
MySqlHintStatement hintStatement = null;
|
||||||
if (i == 1
|
if (i == 1
|
||||||
&& statementList.size() > 0
|
&& !statementList.isEmpty()
|
||||||
&& statementList.get(statementList.size() - i) instanceof MySqlHintStatement) {
|
&& statementList.get(statementList.size() - i) instanceof MySqlHintStatement) {
|
||||||
hintStatement = (MySqlHintStatement) statementList.get(statementList.size() - i);
|
hintStatement = (MySqlHintStatement) statementList.get(statementList.size() - i);
|
||||||
} else if (i > 0 && dialectFeatureEnabled(ParseStatementListSelectUnsupportedSyntax) && !semi
|
} else if (i > 0 && dialectFeatureEnabled(ParseStatementListSelectUnsupportedSyntax) && !semi
|
||||||
&& !(statementList.size() > 0 && statementList.get(statementList.size() - i).isAfterSemi())
|
&& !(!statementList.isEmpty() && statementList.size() - i >= 0 && statementList.get(statementList.size() - i).isAfterSemi())
|
||||||
) {
|
) {
|
||||||
throw new ParserException("syntax error. " + lexer.info());
|
throw new ParserException("syntax error. " + lexer.info());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -586,8 +586,11 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet
|
||||||
lineItemCount = 0;
|
lineItemCount = 0;
|
||||||
println();
|
println();
|
||||||
}
|
}
|
||||||
|
if (selectItem.getExpr() instanceof SQLVariantRefExpr && !((SQLVariantRefExpr) selectItem.getExpr()).isHasPrefixComma()) {
|
||||||
print0(", ");
|
print0(" ");
|
||||||
|
} else {
|
||||||
|
print0(", ");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectItem.getClass() == SQLSelectItem.class) {
|
if (selectItem.getClass() == SQLSelectItem.class) {
|
||||||
|
|
@ -1048,7 +1051,7 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet
|
||||||
&& ((SQLIdentifierExpr) right).getName().equalsIgnoreCase("NOTFOUND")) {
|
&& ((SQLIdentifierExpr) right).getName().equalsIgnoreCase("NOTFOUND")) {
|
||||||
printOpSpace = false;
|
printOpSpace = false;
|
||||||
}
|
}
|
||||||
if (printOpSpace) {
|
if (printOpSpace && operator != SQLBinaryOperator.Blank) {
|
||||||
print(' ');
|
print(' ');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1569,7 +1572,12 @@ public class SQLASTOutputVisitor extends SQLASTVisitorAdapter implements Paramet
|
||||||
if (x.isParenthesized()) {
|
if (x.isParenthesized()) {
|
||||||
print('(');
|
print('(');
|
||||||
}
|
}
|
||||||
printName0(x.getName());
|
String replacedName = x.getName();
|
||||||
|
if (x.getParent() instanceof SQLBinaryOpExpr
|
||||||
|
|| x.getParent() instanceof SQLSelectItem) {
|
||||||
|
replacedName = replaceQuota(x.getName());
|
||||||
|
}
|
||||||
|
printName0(replacedName);
|
||||||
if (x.getCollate() != null) {
|
if (x.getCollate() != null) {
|
||||||
String collate = x.getCollate();
|
String collate = x.getCollate();
|
||||||
print(" COLLATE ");
|
print(" COLLATE ");
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
package com.alibaba.druid.oracle;
|
||||||
|
|
||||||
|
import com.alibaba.druid.sql.ast.SQLStatement;
|
||||||
|
import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleJSONTableExpr;
|
||||||
|
import com.alibaba.druid.sql.dialect.oracle.parser.OracleStatementParser;
|
||||||
|
import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitor;
|
||||||
|
import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitorAdapter;
|
||||||
|
import com.alibaba.druid.sql.parser.SQLStatementParser;
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
import org.junit.Assert;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by pku_liuqiang on 2025/5/7.
|
||||||
|
* 类说明:
|
||||||
|
*/
|
||||||
|
public class OracleJsonTableTest extends TestCase {
|
||||||
|
|
||||||
|
public void testLimit() {
|
||||||
|
String sql = "select a1.* " +
|
||||||
|
"from aaa a1 left join " +
|
||||||
|
"JSON_TABLE(a1.jsondata, '$[*]' COLUMNS ( id NUMBER PATH '$.id', code VARCHAR2(100) path '$.code')) b1 on a1.id=b1.id";
|
||||||
|
SQLStatementParser parser = new OracleStatementParser(sql);
|
||||||
|
List<SQLStatement> stmtList = parser.parseStatementList();
|
||||||
|
OracleASTVisitor visitor = new OracleASTVisitorAdapter() {
|
||||||
|
@Override
|
||||||
|
public boolean visit(OracleJSONTableExpr x) {
|
||||||
|
Assert.assertNotNull(x.getExpr());
|
||||||
|
Assert.assertNotNull(x.getPath());
|
||||||
|
Assert.assertEquals(2, x.getColumns().size());
|
||||||
|
return super.visit(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean visit(OracleJSONTableExpr.Column x) {
|
||||||
|
Assert.assertNotNull(x.getName());
|
||||||
|
Assert.assertNotNull(x.getDataType());
|
||||||
|
Assert.assertNotNull(x.getPath());
|
||||||
|
return super.visit(x);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
stmtList.get(0).accept(visitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,65 @@
|
||||||
|
select c from b where c=1 ${if(len(a)=0,'and 1=1',"and a>0")} ${if(len(a)=0,'and 1=1',"and a>0")}
|
||||||
|
--------------------
|
||||||
|
SELECT c
|
||||||
|
FROM b
|
||||||
|
WHERE c = 1 ${if(len(a)=0,'and 1=1',"and a>0")} ${if(len(a)=0,'and 1=1',"and a>0")}
|
||||||
|
------------------------------------------------------------------------------------------------------------------------
|
||||||
|
select ${if(len(a)=0,' 1=1 ',"c")}, ${if(len(a)=0,' 1=1 ',"c")} from b
|
||||||
|
--------------------
|
||||||
|
SELECT ${if(len(a)=0,' 1=1 ',"c")}, ${if(len(a)=0,' 1=1 ',"c")}
|
||||||
|
FROM b
|
||||||
|
------------------------------------------------------------------------------------------------------------------------
|
||||||
|
select a from test where 1=1 ${if(a=1, "and b","and c")} in d
|
||||||
|
--------------------
|
||||||
|
SELECT a
|
||||||
|
FROM test
|
||||||
|
WHERE 1 = 1 ${if(a=1, "and b","and c")} IN (d)
|
||||||
|
------------------------------------------------------------------------------------------------------------------------
|
||||||
|
select a from test where 1=1
|
||||||
|
and (
|
||||||
|
a.report_item_code in ('${gFinReportItem}') or a.report_item_code ='B002'
|
||||||
|
${if(find('E069',gFinReportItem)>0," or (c.yn_offset_report = 'Y' and a.report_item_code ='E06802') "," ")}
|
||||||
|
)
|
||||||
|
--------------------
|
||||||
|
SELECT a
|
||||||
|
FROM test
|
||||||
|
WHERE 1 = 1
|
||||||
|
AND (a.report_item_code IN ('${gFinReportItem}')
|
||||||
|
OR a.report_item_code = 'B002' ${if(find('E069',gFinReportItem)>0," or (c.yn_offset_report = 'Y' and a.report_item_code ='E06802') "," ")})
|
||||||
|
------------------------------------------------------------------------------------------------------------------------
|
||||||
|
select ${if(len(a)=0,' 1=1 ',"c")}, ${if(len(a)=0,' 1=1 ',"c")} from b
|
||||||
|
--------------------
|
||||||
|
SELECT ${if(len(a)=0,' 1=1 ',"c")}, ${if(len(a)=0,' 1=1 ',"c")}
|
||||||
|
FROM b
|
||||||
|
------------------------------------------------------------------------------------------------------------------------
|
||||||
|
select b.merge_region_code as region_code,b.merge_region_name as region_name,b.govern_region_area_union as region_area_subtotal
|
||||||
|
${if(gRegionAreaLevel='合并营运区',",b.govern_region_area_union as region_area_code,b.govern_region_area_union as region_area_name",",b.region_area_code as region_area_code,b.region_area_name as region_area_name")}
|
||||||
|
-- ${if(len(gQtrMonth)=5,"/*","")}
|
||||||
|
,sum(case when a.stat_date = ${gQtrMonth} then a.period_loc_amt else 0 end) as bq_amt
|
||||||
|
from b
|
||||||
|
--------------------
|
||||||
|
SELECT b.merge_region_code AS region_code, b.merge_region_name AS region_name, b.govern_region_area_union AS region_area_subtotal ${if(gRegionAreaLevel='合并营运区',",b.govern_region_area_union as region_area_code,b.govern_region_area_union as region_area_name",",b.region_area_code as region_area_code,b.region_area_name as region_area_name")} -- ${if(len(gQtrMonth)=5,"/*","")}
|
||||||
|
, sum(CASE
|
||||||
|
WHEN a.stat_date = ${gQtrMonth} THEN a.period_loc_amt
|
||||||
|
ELSE 0
|
||||||
|
END) AS bq_amt
|
||||||
|
FROM b
|
||||||
|
------------------------------------------------------------------------------------------------------------------------
|
||||||
|
select
|
||||||
|
${if(gGyzy_hz=='G'," buyer_name as buyer_name, "," syr as buyer_name, ")}
|
||||||
|
substringUTF8(buyer_name,1,positionUTF8(buyer_name,'(')-1) as tg
|
||||||
|
from a
|
||||||
|
--------------------
|
||||||
|
SELECT ${if(gGyzy_hz=='G'," buyer_name as buyer_name, "," syr as buyer_name, ")}
|
||||||
|
, substringUTF8(buyer_name, 1, positionUTF8(buyer_name, '(') - 1) AS tg
|
||||||
|
FROM a
|
||||||
|
------------------------------------------------------------------------------------------------------------------------
|
||||||
|
select c from b where c=1 ${if(len(a)=0,'and 1=1',"and a>0")} ${if(len(a)=0,'and 1=1',"and a>0")}
|
||||||
|
--------------------
|
||||||
|
SELECT c
|
||||||
|
FROM b
|
||||||
|
WHERE c = 1 ${if(len(a)=0,'and 1=1',"and a>0")} ${if(len(a)=0,'and 1=1',"and a>0")}
|
||||||
|
------------------------------------------------------------------------------------------------------------------------
|
||||||
--test
|
--test
|
||||||
select a from b
|
select a from b
|
||||||
--------------------
|
--------------------
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,22 @@ select * from [test].[test]
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM [test].[test]
|
FROM [test].[test]
|
||||||
------------------------------------------------------------------------------------------------------------------------
|
------------------------------------------------------------------------------------------------------------------------
|
||||||
|
CREATE VIEW [a].[b]
|
||||||
|
AS SELECT
|
||||||
|
*
|
||||||
|
, [c] = DATEDIFF(MILLISECOND, request_time, GETDATE()) / 1000.0
|
||||||
|
FROM
|
||||||
|
tbl
|
||||||
|
WHERE
|
||||||
|
[state] = 'Queued';
|
||||||
|
--------------------
|
||||||
|
CREATE VIEW [a].[b]
|
||||||
|
AS
|
||||||
|
SELECT *
|
||||||
|
, [c] = DATEDIFF(MILLISECOND, request_time, GETDATE()) / 1000.0
|
||||||
|
FROM tbl
|
||||||
|
WHERE [state] = 'Queued';
|
||||||
|
------------------------------------------------------------------------------------------------------------------------
|
||||||
USE AdventureWorks2008R2
|
USE AdventureWorks2008R2
|
||||||
--------------------
|
--------------------
|
||||||
USE AdventureWorks2008R2
|
USE AdventureWorks2008R2
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue