Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -178,4 +178,5 @@ public interface ExpressionVisitor {

public void visit(NotExpression aThis);

void visit(SubstringExpression substringExpression);
}
Original file line number Diff line number Diff line change
Expand Up @@ -493,4 +493,13 @@ public void visit(DateTimeLiteralExpression literal) {

}

@Override
public void visit(SubstringExpression substringExpression) {
substringExpression.getExpression().accept(this);
substringExpression.getFromExpression().accept(this);
Expression forExpression = substringExpression.getForExpression();
if (forExpression != null) {
forExpression.accept(this);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* #%L
* JSQLParser library
* %%
* Copyright (C) 2004 - 2018 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
* <http://www.gnu.org/licenses/lgpl-2.1.html>.
* #L%
*/
package net.sf.jsqlparser.expression;

import net.sf.jsqlparser.parser.ASTNodeAccessImpl;

public class SubstringExpression extends ASTNodeAccessImpl implements Expression {

private Expression expression;
private Expression fromExpression;
private Expression forExpression;

@Override
public void accept(ExpressionVisitor expressionVisitor) {
expressionVisitor.visit(this);
}

public Expression getExpression() {
return expression;
}

public void setExpression(Expression expression) {
this.expression = expression;
}

public Expression getFromExpression() {
return fromExpression;
}

public void setFromExpression(Expression fromExpression) {
this.fromExpression = fromExpression;
}

public Expression getForExpression() {
return forExpression;
}

public void setForExpression(Expression forExpression) {
this.forExpression = forExpression;
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder("SUBSTRING(");
sb.append(expression);
sb.append(" FROM ");
sb.append(fromExpression);
if (forExpression != null) {
sb.append(" FOR ");
sb.append(forExpression);
}
sb.append(")");
return sb.toString();
}
}
11 changes: 11 additions & 0 deletions src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import net.sf.jsqlparser.expression.RowConstructor;
import net.sf.jsqlparser.expression.SignedExpression;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.SubstringExpression;
import net.sf.jsqlparser.expression.TimeKeyExpression;
import net.sf.jsqlparser.expression.TimeValue;
import net.sf.jsqlparser.expression.TimestampValue;
Expand Down Expand Up @@ -785,4 +786,14 @@ public void visit(Upsert upsert) {
@Override
public void visit(UseStatement use) {
}

@Override
public void visit(SubstringExpression substringExpression) {
substringExpression.getExpression().accept(this);
substringExpression.getFromExpression().accept(this);
Expression forExpression = substringExpression.getForExpression();
if (forExpression != null) {
forExpression.accept(this);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
import net.sf.jsqlparser.expression.RowConstructor;
import net.sf.jsqlparser.expression.SignedExpression;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.SubstringExpression;
import net.sf.jsqlparser.expression.TimeKeyExpression;
import net.sf.jsqlparser.expression.TimeValue;
import net.sf.jsqlparser.expression.TimestampValue;
Expand Down Expand Up @@ -760,4 +761,8 @@ public void visit(DateTimeLiteralExpression literal) {
buffer.append(literal.toString());
}

@Override
public void visit(SubstringExpression substringExpression) {
buffer.append(substringExpression.toString());
}
}
21 changes: 20 additions & 1 deletion src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */
| <K_SKIP: "SKIP">
| <K_SOME:"SOME">
| <K_START:"START">
| <K_SUBSTRING:"SUBSTRING">
| <K_TABLE:"TABLE">
| <K_TABLESPACE : "TABLESPACE">
| <K_THEN:"THEN">
Expand Down Expand Up @@ -960,7 +961,7 @@ String RelObjectNameWithoutValue() :
| tk=<K_OPEN> | tk=<K_OVER> | tk=<K_PERCENT> | tk=<K_PRIOR>
| tk=<K_SEPARATOR> | tk=<K_NO> | tk=<K_ACTION> | tk=<K_CASCADE> | tk=<K_END>
| tk=<K_TABLE> | tk=<K_DATETIMELITERAL> | tk=<K_COMMIT> | tk=<K_PRECISION>
| tk=<K_INSERT> | tk=<K_INDEX> | tk=<K_PRIMARY>
| tk=<K_INSERT> | tk=<K_INDEX> | tk=<K_PRIMARY> | tk=<K_SUBSTRING>
)

{ return tk.image; }
Expand Down Expand Up @@ -2453,6 +2454,8 @@ Expression PrimaryExpression() #PrimaryExpression:

| LOOKAHEAD(JsonExpression()) retval=JsonExpression()

| LOOKAHEAD(SubstringExpression()) retval=SubstringExpression()

| LOOKAHEAD(Function()) retval=Function()

| token=<S_DOUBLE> { retval = new DoubleValue(token.image); }
Expand Down Expand Up @@ -2809,6 +2812,22 @@ Execute Execute(): {
}
}

SubstringExpression SubstringExpression() :
{
SubstringExpression retval = new SubstringExpression();
Expression expr;
Expression fromExpression;
Expression forExpression;
}
{
<K_SUBSTRING> "("
expr=SimpleExpression() { retval.setExpression(expr); }
<K_FROM> fromExpression=AdditiveExpression() { retval.setFromExpression(fromExpression); }
[<K_FOR> forExpression=AdditiveExpression() { retval.setForExpression(forExpression); }]
")"
{ return retval; }
}

Function Function() #Function:
{
Function retval = new Function();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,8 @@
import net.sf.jsqlparser.statement.select.SelectVisitorAdapter;
import org.junit.After;
import org.junit.AfterClass;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
Expand Down Expand Up @@ -217,4 +214,23 @@ public void testAnalyticFunctionWithoutExpression502() throws JSQLParserExceptio
expr.accept(adapter);
}

@Test
public void testSubstringFromFor() throws JSQLParserException {
Expression expr = CCJSqlParserUtil.parseExpression("substring('asdf' from 2 for 3)");
assertThat(expr, instanceOf(SubstringExpression.class));
ExpressionVisitorAdapter adapter = new ExpressionVisitorAdapter();
expr.accept(adapter);
}

@Test
public void testSubstringFrom() throws JSQLParserException {
Expression expr = CCJSqlParserUtil.parseExpression("substring('asdf' from 2)");
assertThat(expr, instanceOf(SubstringExpression.class));
ExpressionVisitorAdapter adapter = new ExpressionVisitorAdapter();
try {
expr.accept(adapter);
} catch (NullPointerException e) {
fail("Tried to visit an optional for expression?");
}
}
}
14 changes: 11 additions & 3 deletions src/test/java/net/sf/jsqlparser/test/select/SelectTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2724,19 +2724,27 @@ public void testIssue554() throws JSQLParserException {
public void testIssue567KeywordPrimary() throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed("SELECT primary, secondary FROM info");
}

public void testIssue572TaskReplacement() throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed("SELECT task_id AS \"Task Id\" FROM testtable");
}

public void testIssue566LargeView() throws IOException, JSQLParserException {
String stmt = IOUtils.toString(SelectTest.class.getResourceAsStream("large-sql-issue-566.txt"));
assertSqlCanBeParsedAndDeparsed(stmt, true);
}

public void testIssue566PostgreSQLEscaped() throws IOException, JSQLParserException {
assertSqlCanBeParsedAndDeparsed("SELECT E'test'");
}

public void testSubstringFrom() throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed("SELECT SUBSTRING(col FROM 3) FROM t");
}

public void testSubstringFromFor() throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed("SELECT SUBSTRING('asdf' FROM 1 FOR 2) FROM dual");
}

public void testIssue563MultiSubJoin() throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed("SELECT c FROM ((SELECT a FROM t) JOIN (SELECT b FROM t2) ON a = B JOIN (SELECT c FROM t3) ON b = c)");
Expand Down
24 changes: 24 additions & 0 deletions src/test/java/net/sf/jsqlparser/util/TablesNamesFinderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import net.sf.jsqlparser.statement.upsert.Upsert;
import net.sf.jsqlparser.test.TestException;
import net.sf.jsqlparser.test.simpleparsing.CCJSqlParserManagerTest;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import org.junit.Test;

Expand Down Expand Up @@ -495,4 +496,27 @@ public void testSelectHavingSubquery() throws Exception {
assertTrue(tableList.contains("TABLE1"));
assertTrue(tableList.contains("TABLE2"));
}

@Test
public void testSubstringFromFor() throws JSQLParserException {
String sql = "select substring((select str from t where id = 1) from (select start_pos from u where id = 1) for (select len from v where id = 1))";
Statement select = CCJSqlParserUtil.parse(sql);
TablesNamesFinder tablesNamesFinder = new TablesNamesFinder();
List<String> tableList = tablesNamesFinder.getTableList(select);
assertThat(tableList, containsInAnyOrder("t", "u", "v"));
}

@Test
public void testSubstringFrom() throws JSQLParserException {
String sql = "select substring((select str from t where id = 1) from (select start_pos from u where id = 1))";
Statement select = null;
try {
select = CCJSqlParserUtil.parse(sql);
} catch (NullPointerException e) {
fail("Tried to visit an optional for expression?");
}
TablesNamesFinder tablesNamesFinder = new TablesNamesFinder();
List<String> tableList = tablesNamesFinder.getTableList(select);
assertThat(tableList, containsInAnyOrder("t", "u"));
}
}