diff --git a/README.md b/README.md index cab86d450..985765efe 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,9 @@ Look here for more information and examples: https://github.com/JSQLParser/JSqlP JSqlParser is dual licensed under **LGPL V2.1** and **Apache Software License, Version 2.0**. +## Discussion + +Please provide feedback on https://github.com/JSQLParser/JSqlParser/issues/677, about removing bracket identifier quotation to support array processing. ## News * Released version **1.3** of JSqlParser @@ -51,6 +54,7 @@ Also I would like to know about needed examples or documentation stuff. ## Extensions in the latest SNAPSHOT version 1.4 +* support of **substring(col from 2), position('test' in col), ..** and more (pull request #702) * support of db2 **VALUES** syntax (issue #561) ## Extensions of JSqlParser releases diff --git a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java index 995c22810..c5730c9a1 100644 --- a/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java +++ b/src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java @@ -346,6 +346,14 @@ public void visit(ExpressionList expressionList) { } } + @Override + public void visit(NamedExpressionList namedExpressionList) { + for (Expression expr : namedExpressionList.getExpressions()) { + expr.accept(this); + } + } + + @Override public void visit(MultiExpressionList multiExprList) { for (ExpressionList list : multiExprList.getExprList()) { diff --git a/src/main/java/net/sf/jsqlparser/expression/Function.java b/src/main/java/net/sf/jsqlparser/expression/Function.java index 53c1ca3d2..9925312bf 100644 --- a/src/main/java/net/sf/jsqlparser/expression/Function.java +++ b/src/main/java/net/sf/jsqlparser/expression/Function.java @@ -22,6 +22,7 @@ package net.sf.jsqlparser.expression; import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.expression.operators.relational.NamedExpressionList; import net.sf.jsqlparser.parser.ASTNodeAccessImpl; /** @@ -31,6 +32,7 @@ public class Function extends ASTNodeAccessImpl implements Expression { private String name; private ExpressionList parameters; + private NamedExpressionList namedParameters; private boolean allColumns = false; private boolean distinct = false; private boolean isEscaped = false; @@ -95,6 +97,20 @@ public void setParameters(ExpressionList list) { parameters = list; } + /** + * the parameters might be named parameters, e.g. substring('foobar' from 2 for 3) + * + * @return the list of named parameters of the function (if any, else null) + */ + + public NamedExpressionList getNamedParameters() { + return namedParameters; + } + + public void setNamedParameters(NamedExpressionList list) { + namedParameters = list; + } + /** * Return true if it's in the form "{fn function_body() }" * @@ -128,12 +144,16 @@ public void setKeep(KeepExpression keep) { public String toString() { String params; - if (parameters != null) { - params = parameters.toString(); - if (isDistinct()) { - params = params.replaceFirst("\\(", "(DISTINCT "); - } else if (isAllColumns()) { - params = params.replaceFirst("\\(", "(ALL "); + if (parameters != null || namedParameters != null) { + if(parameters != null){ + params = parameters.toString(); + if (isDistinct()) { + params = params.replaceFirst("\\(", "(DISTINCT "); + } else if (isAllColumns()) { + params = params.replaceFirst("\\(", "(ALL "); + } + } else{ + params = namedParameters.toString(); } } else if (isAllColumns()) { params = "(*)"; diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/ItemsListVisitor.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/ItemsListVisitor.java index 3c630c21f..44a35c29b 100644 --- a/src/main/java/net/sf/jsqlparser/expression/operators/relational/ItemsListVisitor.java +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/ItemsListVisitor.java @@ -29,5 +29,7 @@ public interface ItemsListVisitor { void visit(ExpressionList expressionList); + void visit(NamedExpressionList namedExpressionList); + void visit(MultiExpressionList multiExprList); } diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/ItemsListVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/ItemsListVisitorAdapter.java index ff2cf1cc7..d821d81db 100644 --- a/src/main/java/net/sf/jsqlparser/expression/operators/relational/ItemsListVisitorAdapter.java +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/ItemsListVisitorAdapter.java @@ -30,6 +30,11 @@ public void visit(SubSelect subSelect) { } + @Override + public void visit(NamedExpressionList namedExpressionList) { + + } + @Override public void visit(ExpressionList expressionList) { diff --git a/src/main/java/net/sf/jsqlparser/expression/operators/relational/NamedExpressionList.java b/src/main/java/net/sf/jsqlparser/expression/operators/relational/NamedExpressionList.java new file mode 100644 index 000000000..3f9e1ca32 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/expression/operators/relational/NamedExpressionList.java @@ -0,0 +1,91 @@ +/* + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2013 JSQLParser + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +package net.sf.jsqlparser.expression.operators.relational; + +import java.util.Arrays; +import java.util.List; + +import net.sf.jsqlparser.expression.Expression; + +/** + * A list of named expressions, as in + * as in select substr('xyzzy' from 2 for 3) + */ +public class NamedExpressionList implements ItemsList { + + private List expressions; + private List names; + + public NamedExpressionList() { + } + + public NamedExpressionList(List expressions) { + this.expressions = expressions; + } + + public NamedExpressionList(Expression ... expressions) { + this.expressions = Arrays.asList(expressions); + } + + public List getExpressions() { + return expressions; + } + + public List getNames() { + return names; + } + + + public void setExpressions(List list) { + expressions = list; + } + + public void setNames(List list) { + names = list; + } + + + @Override + public void accept(ItemsListVisitor itemsListVisitor) { + itemsListVisitor.visit(this); + } + + @Override + public String toString() { + + StringBuilder ret = new StringBuilder(); + ret.append("("); + for(int i=0; i0){ + ret.append(" "); + } + if(! names.get(i).equals("")){ + ret.append(names.get(i)).append(" ").append(expressions.get(i)); + }else{ + ret.append(expressions.get(i)); + } + } + ret.append(")"); + + return ret.toString(); + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java b/src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java index 52790a462..3eb2ab5bd 100644 --- a/src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java +++ b/src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java @@ -22,6 +22,7 @@ package net.sf.jsqlparser.statement; import net.sf.jsqlparser.statement.alter.Alter; +import net.sf.jsqlparser.statement.comment.Comment; import net.sf.jsqlparser.statement.create.index.CreateIndex; import net.sf.jsqlparser.statement.create.table.CreateTable; import net.sf.jsqlparser.statement.create.view.AlterView; @@ -40,6 +41,8 @@ public interface StatementVisitor { + void visit(Comment comment); + void visit(Commit commit); void visit(Delete delete); diff --git a/src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java b/src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java index fcc1188d0..4ddfd4da0 100644 --- a/src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java +++ b/src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java @@ -22,6 +22,7 @@ package net.sf.jsqlparser.statement; import net.sf.jsqlparser.statement.alter.Alter; +import net.sf.jsqlparser.statement.comment.Comment; import net.sf.jsqlparser.statement.create.index.CreateIndex; import net.sf.jsqlparser.statement.create.table.CreateTable; import net.sf.jsqlparser.statement.create.view.AlterView; @@ -40,6 +41,11 @@ public class StatementVisitorAdapter implements StatementVisitor { + @Override + public void visit(Comment comment) { + + } + @Override public void visit(Commit commit) { diff --git a/src/main/java/net/sf/jsqlparser/statement/comment/Comment.java b/src/main/java/net/sf/jsqlparser/statement/comment/Comment.java new file mode 100755 index 000000000..c01856a69 --- /dev/null +++ b/src/main/java/net/sf/jsqlparser/statement/comment/Comment.java @@ -0,0 +1,76 @@ +/* + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2017 JSQLParser + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 2.1 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * . + * #L% + */ +package net.sf.jsqlparser.statement.comment; + +import net.sf.jsqlparser.expression.StringValue; +import net.sf.jsqlparser.schema.Column; +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.StatementVisitor; + +public class Comment implements Statement { + + private Table table; + private Column column; + private StringValue comment; + + @Override + public void accept(StatementVisitor statementVisitor) { + statementVisitor.visit(this); + } + + public Table getTable() { + return table; + } + + public void setTable(Table table) { + this.table = table; + } + + public Column getColumn() { + return column; + } + + public void setColumn(Column column) { + this.column = column; + } + + public StringValue getComment() { + return comment; + } + + public void setComment(StringValue comment) { + this.comment = comment; + } + + @Override + public String toString() { + String sql = "COMMENT ON "; + if (table != null) { + sql += "TABLE " + table + " "; + } else if (column != null) { + sql += "COLUMN " + column + " "; + } + sql += "IS " + comment; + return sql; + } +} diff --git a/src/main/java/net/sf/jsqlparser/statement/create/view/AlterView.java b/src/main/java/net/sf/jsqlparser/statement/create/view/AlterView.java index fb87502ab..f8477a4e2 100644 --- a/src/main/java/net/sf/jsqlparser/statement/create/view/AlterView.java +++ b/src/main/java/net/sf/jsqlparser/statement/create/view/AlterView.java @@ -35,6 +35,7 @@ public class AlterView implements Statement { private Table view; private SelectBody selectBody; + private boolean useReplace = false; private List columnNames = null; @Override @@ -74,9 +75,23 @@ public void setColumnNames(List columnNames) { this.columnNames = columnNames; } + public boolean isUseReplace() { + return useReplace; + } + + public void setUseReplace(boolean useReplace) { + this.useReplace = useReplace; + } + + @Override public String toString() { - StringBuilder sql = new StringBuilder("ALTER "); + StringBuilder sql; + if(useReplace){ + sql = new StringBuilder("REPLACE "); + }else{ + sql = new StringBuilder("ALTER "); + } sql.append("VIEW "); sql.append(view); if (columnNames != null) { diff --git a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java index f3fbd7a1c..267bcaa4f 100644 --- a/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java +++ b/src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java @@ -23,6 +23,7 @@ import java.util.ArrayList; import java.util.List; + import net.sf.jsqlparser.expression.AllComparisonExpression; import net.sf.jsqlparser.expression.AnalyticExpression; import net.sf.jsqlparser.expression.AnyComparisonExpression; @@ -76,6 +77,7 @@ import net.sf.jsqlparser.expression.operators.relational.EqualsTo; import net.sf.jsqlparser.expression.operators.relational.ExistsExpression; import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.expression.operators.relational.NamedExpressionList; import net.sf.jsqlparser.expression.operators.relational.GreaterThan; import net.sf.jsqlparser.expression.operators.relational.GreaterThanEquals; import net.sf.jsqlparser.expression.operators.relational.InExpression; @@ -100,6 +102,7 @@ import net.sf.jsqlparser.statement.Statements; import net.sf.jsqlparser.statement.UseStatement; import net.sf.jsqlparser.statement.alter.Alter; +import net.sf.jsqlparser.statement.comment.Comment; import net.sf.jsqlparser.statement.create.index.CreateIndex; import net.sf.jsqlparser.statement.create.table.CreateTable; import net.sf.jsqlparser.statement.create.view.AlterView; @@ -407,6 +410,13 @@ public void visit(ExpressionList expressionList) { } } + @Override + public void visit(NamedExpressionList namedExpressionList) { + for (Expression expression : namedExpressionList.getExpressions()) { + expression.accept(this); + } + } + @Override public void visit(DateValue dateValue) { } @@ -813,6 +823,19 @@ public void visit(Block block) { } } + @Override + public void visit(Comment comment) { + if (comment.getTable() != null) { + visit(comment.getTable()); + } + if (comment.getColumn() != null) { + Table table = comment.getColumn().getTable(); + if (table != null) { + visit(table); + } + } + } + @Override public void visit(ValuesStatement values) { for (Expression expr : values.getExpressions()) { diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/AlterViewDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/AlterViewDeParser.java index e2c3aa285..4a61d7bb3 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/AlterViewDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/AlterViewDeParser.java @@ -52,7 +52,11 @@ public AlterViewDeParser(StringBuilder buffer, SelectVisitor selectVisitor) { } public void deParse(AlterView alterView) { - buffer.append("ALTER "); + if(alterView.isUseReplace()){ + buffer.append("REPLACE "); + }else{ + buffer.append("ALTER "); + } buffer.append("VIEW ").append(alterView.getView().getFullyQualifiedName()); if (alterView.getColumnNames() != null) { buffer.append(PlainSelect.getStringList(alterView.getColumnNames(), true, true)); diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java index 82f915368..9336bfa0d 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java @@ -77,6 +77,7 @@ import net.sf.jsqlparser.expression.operators.relational.EqualsTo; import net.sf.jsqlparser.expression.operators.relational.ExistsExpression; import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.expression.operators.relational.NamedExpressionList; import net.sf.jsqlparser.expression.operators.relational.GreaterThan; import net.sf.jsqlparser.expression.operators.relational.GreaterThanEquals; import net.sf.jsqlparser.expression.operators.relational.InExpression; @@ -435,7 +436,7 @@ public void visit(Function function) { buffer.append(function.getName()); if (function.isAllColumns() && function.getParameters() == null) { buffer.append("(*)"); - } else if (function.getParameters() == null) { + } else if (function.getParameters() == null && function.getNamedParameters() == null) { buffer.append("()"); } else { boolean oldUseBracketsInExprList = useBracketsInExprList; @@ -446,7 +447,12 @@ public void visit(Function function) { useBracketsInExprList = false; buffer.append("(ALL "); } - visit(function.getParameters()); + if(function.getNamedParameters() != null){ + visit(function.getNamedParameters()); + } + if(function.getParameters() != null){ + visit(function.getParameters()); + } useBracketsInExprList = oldUseBracketsInExprList; if (function.isDistinct() || function.isAllColumns()) { buffer.append(")"); @@ -482,6 +488,29 @@ public void visit(ExpressionList expressionList) { } } + @Override + public void visit(NamedExpressionList namedExpressionList) { + if (useBracketsInExprList) { + buffer.append("("); + } + List names = namedExpressionList.getNames(); + List expressions = namedExpressionList.getExpressions(); + for (int i=0; i0){ + buffer.append(" "); + } + String name = names.get(i); + if(! name.equals("")){ + buffer.append(name); + buffer.append(" "); + } + expressions.get(i).accept(this); + } + if (useBracketsInExprList) { + buffer.append(")"); + } + } + public SelectVisitor getSelectVisitor() { return selectVisitor; } diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/InsertDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/InsertDeParser.java index 59bef5add..26d48334e 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/InsertDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/InsertDeParser.java @@ -26,6 +26,7 @@ import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.ExpressionVisitor; import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.expression.operators.relational.NamedExpressionList; import net.sf.jsqlparser.expression.operators.relational.ItemsListVisitor; import net.sf.jsqlparser.expression.operators.relational.MultiExpressionList; import net.sf.jsqlparser.schema.Column; @@ -174,6 +175,12 @@ public void visit(ExpressionList expressionList) { buffer.append(")"); } +// not used in a top-level insert statement + @Override + public void visit(NamedExpressionList NamedExpressionList) { + + } + @Override public void visit(MultiExpressionList multiExprList) { buffer.append(" VALUES "); diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/ReplaceDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/ReplaceDeParser.java index 7ec489a10..8e0b03ac5 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/ReplaceDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/ReplaceDeParser.java @@ -26,6 +26,7 @@ import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.ExpressionVisitor; import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.expression.operators.relational.NamedExpressionList; import net.sf.jsqlparser.expression.operators.relational.ItemsListVisitor; import net.sf.jsqlparser.expression.operators.relational.MultiExpressionList; import net.sf.jsqlparser.schema.Column; @@ -125,6 +126,11 @@ public void visit(ExpressionList expressionList) { buffer.append(")"); } +// NamedExpressionList not use by top-level Replace + @Override + public void visit(NamedExpressionList namedExpressionList) { + } + @Override public void visit(SubSelect subSelect) { subSelect.getSelectBody().accept(selectVisitor); diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java index ebef94cbc..dcbc56be3 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java @@ -30,6 +30,7 @@ import net.sf.jsqlparser.statement.Statements; import net.sf.jsqlparser.statement.UseStatement; import net.sf.jsqlparser.statement.alter.Alter; +import net.sf.jsqlparser.statement.comment.Comment; import net.sf.jsqlparser.statement.create.index.CreateIndex; import net.sf.jsqlparser.statement.create.table.CreateTable; import net.sf.jsqlparser.statement.create.view.AlterView; @@ -242,6 +243,11 @@ public void visit(Block block) { buffer.append("END"); } + @Override + public void visit(Comment comment) { + buffer.append(comment.toString()); + } + @Override public void visit(ValuesStatement values) { expressionDeParser.setBuffer(buffer); diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/UpsertDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/UpsertDeParser.java index 2394b7705..53e8e9591 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/UpsertDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/UpsertDeParser.java @@ -26,6 +26,7 @@ import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.ExpressionVisitor; import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.expression.operators.relational.NamedExpressionList; import net.sf.jsqlparser.expression.operators.relational.ItemsListVisitor; import net.sf.jsqlparser.expression.operators.relational.MultiExpressionList; import net.sf.jsqlparser.schema.Column; @@ -149,6 +150,11 @@ public void visit(ExpressionList expressionList) { buffer.append(")"); } +// not used by top-level upsert + @Override + public void visit(NamedExpressionList namedExpressionList) { + } + @Override public void visit(MultiExpressionList multiExprList) { buffer.append(" VALUES "); diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 783a5701d..cba75a19b 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -70,6 +70,7 @@ import net.sf.jsqlparser.expression.operators.relational.*; import net.sf.jsqlparser.schema.*; import net.sf.jsqlparser.statement.*; import net.sf.jsqlparser.statement.alter.*; +import net.sf.jsqlparser.statement.comment.*; import net.sf.jsqlparser.statement.create.index.*; import net.sf.jsqlparser.statement.create.table.*; import net.sf.jsqlparser.statement.create.view.*; @@ -149,6 +150,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | @@ -160,6 +162,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | @@ -216,6 +219,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | @@ -245,6 +249,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | @@ -275,6 +280,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */ | | | +| | | | @@ -390,6 +396,7 @@ Statement SingleStatement() : | stm = Delete() | + LOOKAHEAD(2) stm = Replace() | LOOKAHEAD(2) @@ -419,6 +426,8 @@ Statement SingleStatement() : stm = Use() | stm = Commit() + | + stm = Comment() ) { return stm; } } catch (ParseException e) { @@ -610,7 +619,7 @@ Replace Replace(): Expression exp = null; } { - [ { replace.setUseIntoTables(true); }] table=Table() + ( [LOOKAHEAD(2) { replace.setUseIntoTables(true); }] table=Table() ) ( ( @@ -1031,7 +1040,7 @@ String RelObjectNameWithoutValue() : { Token tk = null; } { (tk= | tk= | tk= | - | tk= | tk= | tk= | tk= | tk= + | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= | tk= @@ -1042,6 +1051,7 @@ String RelObjectNameWithoutValue() : | tk= | tk= | tk= | tk= | tk= +/* | tk= | tk= | tk= | tk= */ ) { return tk.image; } @@ -2329,6 +2339,83 @@ ExpressionList SimpleExpressionList(): } } +// trim( [leading|trailing|both] expr from expr) +// The [leading|trailing|both] token has already been consumed +NamedExpressionList NamedExpressionList1(): +{ + NamedExpressionList retval = new NamedExpressionList(); + List expressions = new ArrayList(); + List names = new ArrayList(); + Expression expr1 = null; + Expression expr2 = null; + String name = ""; + Token tk1 = null; + Token tk2 = null; +} +{ + ( + (tk1=|tk1=|tk1=) { names.add(tk1.image); } + expr1=SimpleExpression() + (tk2=|tk2=|tk2=) + expr2=SimpleExpression() + { expressions.add(expr1); names.add(tk2.image); expressions.add(expr2);} + ) + + { + retval.setNames(names); + retval.setExpressions(expressions); + return retval; + } +} + +// substring(expr1 from expr2) +// substring(expr1 from expr2 for expr3) +// trim(expr1 from expr2) +// position(expr1 in expr2) +// overlay(expr1 placing expr2 from expr3) +// overlay(expr1 placing expr2 from expr3 for expr4) +// expr1 has already been consumed +NamedExpressionList NamedExpressionListExprFirst(): +{ + NamedExpressionList retval = new NamedExpressionList(); + List expressions = new ArrayList(); + List names = new ArrayList(); + Expression expr1 = null; + Expression expr2 = null; + Expression expr3 = null; + Expression expr4 = null; + Token tk2 = null; + Token tk3 = null; + Token tk4 = null; +} +{ + expr1=SimpleExpression() + (tk2=|tk2=|tk2=) + { + names.add(""); + expressions.add(expr1); + names.add(tk2.image); + } + ( + expr2=SimpleExpression() { expressions.add(expr2);} + ( + (tk3=|tk3=) + expr3=SimpleExpression() {names.add(tk3.image); expressions.add(expr3);} + ( + (tk4=) + expr4=SimpleExpression() {names.add(tk4.image); expressions.add(expr4);} + )? + )? + ) + + { + retval.setNames(names); + retval.setExpressions(expressions); + return retval; + } +} + + ExpressionList SimpleExpressionListAtLeastTwoItems(): { ExpressionList retval = new ExpressionList(); @@ -2939,9 +3026,14 @@ Function Function() #Function: Function retval = new Function(); String funcName = null; String tmp = null; + List expressions = new ArrayList(); ExpressionList expressionList = null; + NamedExpressionList namedExpressionList = null; KeepExpression keep = null; SubSelect expr = null; + Token tk1 = null; + Token tk2 = null; + Expression expr1 = null; } { ["{fn" { retval.setEscaped(true); } ] @@ -2949,8 +3041,19 @@ Function Function() #Function: funcName=RelObjectNameExt() [ "." tmp=RelObjectNameExt() { funcName+= "." + tmp; } ["." tmp=RelObjectNameExt() { funcName+= "." + tmp; }]] - "(" [ [ { retval.setDistinct(true); } | { retval.setAllColumns(true); }] (LOOKAHEAD(3) expressionList=SimpleExpressionList() | "*" { retval.setAllColumns(true); } - | expr = SubSelect() { expr.setUseBrackets(false); expressionList = new ExpressionList(expr); } ) ] ")" + "(" [ [ { retval.setDistinct(true); } | { retval.setAllColumns(true); }] + ( LOOKAHEAD(4) + namedExpressionList=NamedExpressionList1() + | + LOOKAHEAD(NamedExpressionListExprFirst()) namedExpressionList = NamedExpressionListExprFirst() + | + LOOKAHEAD(3) expressionList=SimpleExpressionList() + | + "*" { retval.setAllColumns(true); } + | + expr = SubSelect() { expr.setUseBrackets(false); expressionList = new ExpressionList(expr); } + )] + ")" [ "." tmp=RelObjectName() { retval.setAttribute(tmp); }] @@ -2959,6 +3062,7 @@ Function Function() #Function: ["}"] { retval.setParameters(expressionList); + retval.setNamedParameters(namedExpressionList); retval.setName(funcName); retval.setKeep(keep); linkAST(retval,jjtThis); @@ -3342,7 +3446,7 @@ AlterView AlterView(): List columnNames = null; } { - + ( ( ) | ( {alterView.setUseReplace(true);}) ) view=Table() { alterView.setView(view); } [ columnNames = ColumnsNamesList() { alterView.setColumnNames(columnNames); } ] @@ -3593,7 +3697,7 @@ AlterExpression AlterExpression(): List columnNames = null; List constraints = null; ForeignKeyIndex fkIndex = null; - NamedConstraint index = null; + Index index = null; Table fkTable = null; AlterExpression.ColumnDataType alterExpressionColumnDataType = null; } @@ -3604,20 +3708,19 @@ AlterExpression AlterExpression(): ( LOOKAHEAD(2) ( columnNames=ColumnsNamesList() { alterExp.setPkColumns(columnNames); } ) constraints=AlterExpressionConstraintState() { alterExp.setConstraints(constraints); } | - LOOKAHEAD(2) ( - tk= - sk3=ColumnsNamesListItem() - { - columnNames = new ArrayList(); - columnNames.add(sk3); - - index = new NamedConstraint(); - index.setType(tk.image); - index.setColumnsNames(columnNames); - alterExp.setIndex(index); - } - constraints=AlterExpressionConstraintState() { alterExp.setConstraints(constraints); } - ) + LOOKAHEAD(2) ( + tk = < K_INDEX > + sk3 = RelObjectName() + columnNames = ColumnsNamesList() + { + index = new Index(); + index.setType(tk.image); + index.setName(sk3); + index.setColumnsNames(columnNames); + alterExp.setIndex(index); + } + constraints=AlterExpressionConstraintState() { alterExp.setConstraints(constraints); } + ) | ( (LOOKAHEAD(2) )? alterExpressionColumnDataType = AlterExpressionColumnDataType() { alterExp.addColDataType(alterExpressionColumnDataType); } @@ -3784,3 +3887,27 @@ Commit Commit(): return commit; } } + +Comment Comment(): +{ + Comment result = new Comment(); + Table table; + Column column; + Token comment; +} +{ + + ( + ( + table = Table() { result.setTable(table); } + ) + | + ( + column = Column() { result.setColumn(column); } + ) + ) + comment= { result.setComment(new StringValue(comment.image)); } + { + return result; + } +} diff --git a/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java b/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java index cf17f70be..2438de1ac 100644 --- a/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/alter/AlterTest.java @@ -12,7 +12,6 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import org.junit.Ignore; import org.junit.Test; public class AlterTest { @@ -313,8 +312,7 @@ public void testIssue633() throws JSQLParserException { } @Test - @Ignore public void testIssue679() throws JSQLParserException { - assertSqlCanBeParsedAndDeparsed("ALTER TABLE tb_session_status ADD INDEX idx_user_id(user_id)"); + assertSqlCanBeParsedAndDeparsed("ALTER TABLE tb_session_status ADD INDEX idx_user_id_name (user_id, user_name(10)), ADD INDEX idx_user_name (user_name)"); } } diff --git a/src/test/java/net/sf/jsqlparser/statement/comment/CommentTest.java b/src/test/java/net/sf/jsqlparser/statement/comment/CommentTest.java new file mode 100755 index 000000000..e2c22ac27 --- /dev/null +++ b/src/test/java/net/sf/jsqlparser/statement/comment/CommentTest.java @@ -0,0 +1,67 @@ +package net.sf.jsqlparser.statement.comment; + +import java.io.StringReader; +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import net.sf.jsqlparser.schema.Column; +import net.sf.jsqlparser.schema.Table; +import static net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed; +import static org.junit.Assert.assertEquals; +import org.junit.Test; + +public class CommentTest { + + @Test + public void testCommentTable() throws JSQLParserException { + String statement = "COMMENT ON TABLE table1 IS 'comment1'"; + Comment comment = (Comment) CCJSqlParserUtil.parse(new StringReader(statement)); + Table table = comment.getTable(); + assertEquals("table1", table.getName()); + assertEquals("comment1", comment.getComment().getValue()); + assertEquals(statement, "" + comment); + } + + @Test + public void testCommentTable2() throws JSQLParserException { + String statement = "COMMENT ON TABLE schema1.table1 IS 'comment1'"; + Comment comment = (Comment) CCJSqlParserUtil.parse(new StringReader(statement)); + Table table = comment.getTable(); + assertEquals("schema1", table.getSchemaName()); + assertEquals("table1", table.getName()); + assertEquals("comment1", comment.getComment().getValue()); + assertEquals(statement, "" + comment); + } + + @Test + public void testCommentTableDeparse() throws JSQLParserException { + String statement = "COMMENT ON TABLE table1 IS 'comment1'"; + assertSqlCanBeParsedAndDeparsed(statement); + } + + @Test + public void testCommentColumn() throws JSQLParserException { + String statement = "COMMENT ON COLUMN table1.column1 IS 'comment1'"; + Comment comment = (Comment) CCJSqlParserUtil.parse(new StringReader(statement)); + Column column = comment.getColumn(); + assertEquals("table1", column.getTable().getName()); + assertEquals("column1", column.getColumnName()); + assertEquals("comment1", comment.getComment().getValue()); + assertEquals(statement, "" + comment); + } + + @Test + public void testCommentColumnDeparse() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("COMMENT ON COLUMN table1.column1 IS 'comment1'"); + } + + @Test + public void testToString() { + Comment comment = new Comment(); + assertEquals("COMMENT ON IS null", comment.toString()); + } + + @Test + public void testCommentColumnDeparseIssue696() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("COMMENT ON COLUMN hotels.hotelid IS 'Primary key of the table'"); + } +} diff --git a/src/test/java/net/sf/jsqlparser/statement/create/AlterViewTest.java b/src/test/java/net/sf/jsqlparser/statement/create/AlterViewTest.java index 162d2bfc6..165975fb5 100644 --- a/src/test/java/net/sf/jsqlparser/statement/create/AlterViewTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/create/AlterViewTest.java @@ -11,4 +11,10 @@ public void testAlterView() throws JSQLParserException { String stmt = "ALTER VIEW myview AS SELECT * FROM mytab"; assertSqlCanBeParsedAndDeparsed(stmt); } + + @Test + public void testReplaceView() throws JSQLParserException { + String stmt = "REPLACE VIEW myview AS SELECT * FROM mytab"; + assertSqlCanBeParsedAndDeparsed(stmt); + } } diff --git a/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java b/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java index 4aa46564a..bdadf515a 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java @@ -975,6 +975,10 @@ public void testFunctions() throws JSQLParserException { getName()); assertStatementCanBeDeparsedAs(select, statement); + statement = "SELECT substring(id, 2, 3), substring(id from 2 for 3), substring(id from 2), trim(BOTH ' ' from 'foo bar '), trim(LEADING ' ' from 'foo bar '), trim(TRAILING ' ' from 'foo bar '), trim(' ' from 'foo bar '), position('foo' in 'bar'), overlay('foo' placing 'bar' from 1), overlay('foo' placing 'bar' from 1 for 2) FROM my table"; + select = (Select) parserManager.parse(new StringReader(statement)); + assertStatementCanBeDeparsedAs(select, statement); + statement = "SELECT MAX(id), AVG(pro) AS myavg FROM mytable WHERE mytable.col = 9 GROUP BY pro"; select = (Select) parserManager.parse(new StringReader(statement)); plainSelect = (PlainSelect) select.getSelectBody(); @@ -1031,6 +1035,17 @@ public void testEscapedFunctionsIssue647() throws JSQLParserException { assertSqlCanBeParsedAndDeparsed("SELECT {fn concat(a, b)} AS COL"); } + @Test + public void testNamedParametersPR702() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT substring(id, 2, 3), substring(id from 2 for 3), substring(id from 2), trim(BOTH ' ' from 'foo bar '), trim(LEADING ' ' from 'foo bar '), trim(TRAILING ' ' from 'foo bar '), trim(' ' from 'foo bar '), position('foo' in 'bar'), overlay('foo' placing 'bar' from 1), overlay('foo' placing 'bar' from 1 for 2) FROM my table"); + } + + @Test + public void testNamedParametersPR702_2() throws JSQLParserException { + assertSqlCanBeParsedAndDeparsed("SELECT substring(id, 2, 3) FROM mytable"); + assertSqlCanBeParsedAndDeparsed("SELECT substring(id from 2 for 3) FROM mytable"); + } + @Test public void testWhere() throws JSQLParserException { diff --git a/src/test/java/net/sf/jsqlparser/statement/select/SpecialOracleTest.java b/src/test/java/net/sf/jsqlparser/statement/select/SpecialOracleTest.java index b66969411..8b69bac38 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/SpecialOracleTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/SpecialOracleTest.java @@ -18,20 +18,18 @@ */ package net.sf.jsqlparser.statement.select; -import net.sf.jsqlparser.JSQLParserException; -import net.sf.jsqlparser.parser.CCJSqlParserUtil; -import net.sf.jsqlparser.statement.Statement; -import org.apache.commons.io.FileUtils; -import org.junit.ComparisonFailure; -import org.junit.Test; - import java.io.File; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; - +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import net.sf.jsqlparser.statement.Statement; import static net.sf.jsqlparser.test.TestUtils.assertSqlCanBeParsedAndDeparsed; +import org.apache.commons.io.FileUtils; import static org.junit.Assert.assertTrue; +import org.junit.ComparisonFailure; +import org.junit.Test; /** * Tries to parse and deparse all statments in net.sf.jsqlparser.test.oracle-tests. @@ -77,7 +75,7 @@ public void testAllSqlsParseDeparse() throws IOException { LOG. log(Level.INFO, "tested {0} files. got {1} correct parse results", new Object[]{count, success}); - assertTrue(success >= 149); + assertTrue(success >= 150); } @Test diff --git a/src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java b/src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java index 18bf366a1..21fb9b531 100644 --- a/src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java +++ b/src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java @@ -11,7 +11,9 @@ import net.sf.jsqlparser.parser.CCJSqlParserManager; import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.comment.Comment; import net.sf.jsqlparser.statement.create.table.CreateTable; import net.sf.jsqlparser.statement.delete.Delete; import net.sf.jsqlparser.statement.insert.Insert; @@ -45,8 +47,8 @@ public void testComplexMergeExamples() throws Exception { } private void runTestOnResource(String resPath) throws Exception { - BufferedReader in = new BufferedReader(new InputStreamReader(TablesNamesFinderTest.class. - getResourceAsStream(resPath))); + BufferedReader in = new BufferedReader( + new InputStreamReader(TablesNamesFinderTest.class.getResourceAsStream(resPath))); TablesNamesFinder tablesNamesFinder = new TablesNamesFinder(); try { @@ -114,8 +116,10 @@ public void testGetTableList() throws Exception { + " WHERE ID = (SELECT MAX(ID) FROM MY_TABLE5) AND ID2 IN (SELECT * FROM MY_TABLE6)"; net.sf.jsqlparser.statement.Statement statement = pm.parse(new StringReader(sql)); - // now you should use a class that implements StatementVisitor to decide what to do - // based on the kind of the statement, that is SELECT or INSERT etc. but here we are only + // now you should use a class that implements StatementVisitor to decide what to + // do + // based on the kind of the statement, that is SELECT or INSERT etc. but here we + // are only // interested in SELECTS if (statement instanceof Select) { Select selectStatement = (Select) statement; @@ -192,7 +196,7 @@ public void testGetTableListFromDelete2() throws Exception { assertEquals(1, tableList.size()); assertTrue(tableList.contains("MY_TABLE1")); } - + @Test public void testGetTableListFromDeleteWithJoin() throws Exception { String sql = "DELETE t1, t2 FROM MY_TABLE1 t1 JOIN MY_TABLE2 t2 ON t1.id = t2.id"; @@ -391,8 +395,8 @@ public void testGetTableListIssue284() throws Exception { @Test public void testUpdateGetTableListIssue295() throws JSQLParserException { - Update statement = (Update) CCJSqlParserUtil. - parse("UPDATE component SET col = 0 WHERE (component_id,ver_num) IN (SELECT component_id,ver_num FROM component_temp)"); + Update statement = (Update) CCJSqlParserUtil.parse( + "UPDATE component SET col = 0 WHERE (component_id,ver_num) IN (SELECT component_id,ver_num FROM component_temp)"); TablesNamesFinder tablesNamesFinder = new TablesNamesFinder(); List tableList = tablesNamesFinder.getTableList(statement); assertEquals(2, tableList.size()); @@ -475,7 +479,7 @@ public void testCaseWhenSubSelect3() throws JSQLParserException { assertEquals(1, tableList.size()); assertTrue(tableList.contains("mytable2")); } - + @Test public void testExpressionIssue515() throws JSQLParserException { TablesNamesFinder finder = new TablesNamesFinder(); @@ -483,7 +487,7 @@ public void testExpressionIssue515() throws JSQLParserException { assertEquals(1, tableList.size()); assertTrue(tableList.contains("SOME_TABLE")); } - + @Test public void testSelectHavingSubquery() throws Exception { String sql = "SELECT * FROM TABLE1 GROUP BY COL1 HAVING SUM(COL2) > (SELECT COUNT(*) FROM TABLE2)"; @@ -496,7 +500,7 @@ public void testSelectHavingSubquery() throws Exception { assertTrue(tableList.contains("TABLE1")); assertTrue(tableList.contains("TABLE2")); } - + @Test public void testMySQLValueListExpression() throws JSQLParserException { String sql = "SELECT * FROM TABLE1 WHERE (a, b) = (c, d)"; @@ -505,7 +509,7 @@ public void testMySQLValueListExpression() throws JSQLParserException { assertEquals(1, tableList.size()); assertTrue(tableList.contains("TABLE1")); } - + @Test public void testSkippedSchemaIssue600() throws JSQLParserException { String sql = "delete from schema.table where id = 1"; @@ -514,5 +518,31 @@ public void testSkippedSchemaIssue600() throws JSQLParserException { assertEquals(1, tableList.size()); assertTrue(tableList.contains("schema.table")); } - + + @Test + public void testCommentTable() throws JSQLParserException { + String sql = "comment on table schema.table is 'comment1'"; + TablesNamesFinder finder = new TablesNamesFinder(); + List tableList = finder.getTableList(CCJSqlParserUtil.parse(sql)); + assertEquals(1, tableList.size()); + assertTrue(tableList.contains("schema.table")); + } + + @Test + public void testCommentColumn() throws JSQLParserException { + String sql = "comment on column schema.table.column1 is 'comment1'"; + TablesNamesFinder finder = new TablesNamesFinder(); + List tableList = finder.getTableList(CCJSqlParserUtil.parse(sql)); + assertEquals(1, tableList.size()); + assertTrue(tableList.contains("schema.table")); + } + + @Test + public void testCommentColumn2() throws JSQLParserException { + Comment comment = new Comment(); + comment.setColumn(new Column()); + TablesNamesFinder finder = new TablesNamesFinder(); + List tableList = finder.getTableList(comment); + assertEquals(0, tableList.size()); + } }