Skip to content

Commit

Permalink
Fixes #1267 Cast into RowConstructor (#1274)
Browse files Browse the repository at this point in the history
* Fixes #1267 Cast into RowConstructor

* Improve Test Coverage

* Improve Test Coverage

* Improve Test Coverage

Co-authored-by: Tobias <t.warneke@gmx.net>
  • Loading branch information
manticore-projects and wumpz authored Jul 26, 2021
1 parent c074a21 commit c89cf21
Show file tree
Hide file tree
Showing 11 changed files with 164 additions and 21 deletions.
20 changes: 19 additions & 1 deletion src/main/java/net/sf/jsqlparser/expression/CastExpression.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,30 @@ public class CastExpression extends ASTNodeAccessImpl implements Expression {

private Expression leftExpression;
private ColDataType type;
private RowConstructor rowConstructor;
private boolean useCastKeyword = true;

public RowConstructor getRowConstructor() {
return rowConstructor;
}

public void setRowConstructor(RowConstructor rowConstructor) {
this.rowConstructor = rowConstructor;
this.type = null;
}

public CastExpression withRowConstructor(RowConstructor rowConstructor) {
setRowConstructor(rowConstructor);
return this;
}

public ColDataType getType() {
return type;
}

public void setType(ColDataType type) {
this.type = type;
this.rowConstructor = null;
}

public Expression getLeftExpression() {
Expand All @@ -50,7 +66,9 @@ public void setUseCastKeyword(boolean useCastKeyword) {
@Override
public String toString() {
if (useCastKeyword) {
return "CAST(" + leftExpression + " AS " + type.toString() + ")";
return rowConstructor!=null
? "CAST(" + leftExpression + " AS " + rowConstructor.toString() + ")"
: "CAST(" + leftExpression + " AS " + type.toString() + ")";
} else {
return leftExpression + "::" + type.toString();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import net.sf.jsqlparser.expression.operators.conditional.XorExpression;
import net.sf.jsqlparser.expression.operators.relational.*;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.create.table.ColumnDefinition;
import net.sf.jsqlparser.statement.select.AllColumns;
import net.sf.jsqlparser.statement.select.AllTableColumns;
import net.sf.jsqlparser.statement.select.ExpressionListItem;
Expand Down Expand Up @@ -503,8 +504,14 @@ public void visit(SelectExpressionItem selectExpressionItem) {

@Override
public void visit(RowConstructor rowConstructor) {
for (Expression expr : rowConstructor.getExprList().getExpressions()) {
expr.accept(this);
if (rowConstructor.getColumnDefinitions().isEmpty()) {
for (Expression expression: rowConstructor.getExprList().getExpressions()) {
expression.accept(this);
}
} else {
for (ColumnDefinition columnDefinition : rowConstructor.getColumnDefinitions()) {
columnDefinition.accept(this);
}
}
}

Expand Down Expand Up @@ -605,4 +612,8 @@ public void visit(JsonFunction expression) {
expr.getExpression().accept(this);
}
}

public void visit(ColumnDefinition columnDefinition) {
columnDefinition.accept(this);
}
}
23 changes: 22 additions & 1 deletion src/main/java/net/sf/jsqlparser/expression/RowConstructor.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,26 @@
*/
package net.sf.jsqlparser.expression;

import java.util.ArrayList;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.parser.ASTNodeAccessImpl;
import net.sf.jsqlparser.statement.create.table.ColumnDefinition;

public class RowConstructor extends ASTNodeAccessImpl implements Expression {

private ExpressionList exprList;
private ArrayList<ColumnDefinition> columnDefinitions = new ArrayList<>();
private String name = null;

public RowConstructor() {
}

public ArrayList<ColumnDefinition> getColumnDefinitions() {
return columnDefinitions;
}

public boolean addColumnDefinition(ColumnDefinition columnDefinition) {
return columnDefinitions.add(columnDefinition);
}

public ExpressionList getExprList() {
return exprList;
Expand All @@ -43,6 +53,17 @@ public void accept(ExpressionVisitor expressionVisitor) {

@Override
public String toString() {
if (columnDefinitions.size()>0) {
StringBuilder builder = new StringBuilder(name != null ? name : "");
builder.append("(");
int i = 0;
for (ColumnDefinition columnDefinition:columnDefinitions) {
builder.append(i>0 ? ", " : "").append(columnDefinition.toString());
i++;
}
builder.append(")");
return builder.toString();
}
return (name != null ? name : "") + exprList.toString();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import net.sf.jsqlparser.expression.ExpressionVisitorAdapter;
import net.sf.jsqlparser.statement.select.PlainSelect;

/**
Expand Down Expand Up @@ -99,4 +100,8 @@ public ColumnDefinition addColumnSpecs(Collection<String> columnSpecs) {
collection.addAll(columnSpecs);
return this.withColumnSpecs(collection);
}

public void accept(ExpressionVisitorAdapter expressionVisitor) {
expressionVisitor.visit(this);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
import net.sf.jsqlparser.expression.operators.relational.SupportsOldOracleJoinSyntax;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.create.table.ColumnDefinition;
import net.sf.jsqlparser.statement.select.OrderByElement;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.SelectVisitor;
Expand Down Expand Up @@ -668,7 +669,7 @@ public void visit(CastExpression cast) {
buffer.append("CAST(");
cast.getLeftExpression().accept(this);
buffer.append(" AS ");
buffer.append(cast.getType());
buffer.append( cast.getRowConstructor()!=null ? cast.getRowConstructor() : cast.getType() );
buffer.append(")");
} else {
cast.getLeftExpression().accept(this);
Expand Down Expand Up @@ -872,14 +873,25 @@ public void visit(RowConstructor rowConstructor) {
buffer.append(rowConstructor.getName());
}
buffer.append("(");
boolean first = true;
for (Expression expr : rowConstructor.getExprList().getExpressions()) {
if (first) {
first = false;
} else {
buffer.append(", ");

if (rowConstructor.getColumnDefinitions().size()>0) {
buffer.append("(");
int i = 0;
for (ColumnDefinition columnDefinition:rowConstructor.getColumnDefinitions()) {
buffer.append(i>0 ? ", " : "").append(columnDefinition.toString());
i++;
}
buffer.append(")");
} else {
boolean first = true;
for (Expression expr : rowConstructor.getExprList().getExpressions()) {
if (first) {
first = false;
} else {
buffer.append(", ");
}
expr.accept(this);
}
expr.accept(this);
}
buffer.append(")");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
import net.sf.jsqlparser.expression.operators.relational.SupportsOldOracleJoinSyntax;
import net.sf.jsqlparser.parser.feature.Feature;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.create.table.ColumnDefinition;
import net.sf.jsqlparser.statement.select.SubSelect;
import net.sf.jsqlparser.util.validation.ValidationCapability;
import net.sf.jsqlparser.util.validation.metadata.NamedObject;
Expand Down Expand Up @@ -499,7 +500,13 @@ public void visit(ValueListExpression valueList) {

@Override
public void visit(RowConstructor rowConstructor) {
validateOptionalExpressionList(rowConstructor.getExprList());
if (rowConstructor.getColumnDefinitions().isEmpty()) {
validateOptionalExpressionList(rowConstructor.getExprList());
} else {
for (ColumnDefinition columnDefinition: rowConstructor.getColumnDefinitions()) {
validateName(NamedObject.column, columnDefinition.getColumnName());
}
}
}

@Override
Expand Down
31 changes: 23 additions & 8 deletions src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -3169,7 +3169,8 @@ ExpressionList SimpleExpressionList(boolean outerBrackets) #ExpressionList:
Expression expr = null;
}
{
expr=SimpleExpression() { expressions.add(expr); } ("," expr=SimpleExpression() { expressions.add(expr); })*
expr=SimpleExpression() { expressions.add(expr); }
( LOOKAHEAD(2) "," expr=SimpleExpression() { expressions.add(expr); } )*
{
retval.setExpressions(expressions);
return retval;
Expand Down Expand Up @@ -3552,6 +3553,8 @@ Expression PrimaryExpression() #PrimaryExpression:

| LOOKAHEAD(2) retval=CastExpression()

//| LOOKAHEAD(2) retval=RowConstructor()

// support timestamp expressions
| (token=<K_TIME_KEY_EXPR> | token=<K_CURRENT>) { retval = new TimeKeyExpression(token.image); }

Expand Down Expand Up @@ -4096,15 +4099,23 @@ CastExpression CastExpression():
{
CastExpression retval = new CastExpression();
ColDataType type = null;
RowConstructor rowConstructor = null;
Expression expression = null;
boolean useCastKeyword;
}
{
<K_CAST> "(" expression=SimpleExpression() <K_AS> type=ColDataType() ")" { retval.setUseCastKeyword(true); }
<K_CAST>
"("
expression=SimpleExpression()
<K_AS> { retval.setUseCastKeyword(true); }
(
LOOKAHEAD(3) rowConstructor = RowConstructor() { retval.setRowConstructor(rowConstructor); }
| type=ColDataType() { retval.setType(type); }
)
")"

{
retval.setLeftExpression(expression);
retval.setType(type);
return retval;
}
}
Expand Down Expand Up @@ -4168,16 +4179,20 @@ WhenClause WhenThenSearchCondition():
}*/

RowConstructor RowConstructor(): {
ExpressionList list = null;
RowConstructor rowConstructor = new RowConstructor();
ColumnDefinition columnDefinition = null;
} {
[ <K_ROW> { rowConstructor.setName("ROW");} ]
"("
list = SimpleExpressionList(true)
")"
columnDefinition = ColumnDefinition() { rowConstructor.addColumnDefinition(columnDefinition); }
(
","
columnDefinition = ColumnDefinition() { rowConstructor.addColumnDefinition(columnDefinition); }
)*
")"


{
rowConstructor.setExprList(list);
return rowConstructor;
}
}
Expand Down Expand Up @@ -4541,7 +4556,7 @@ ColumnDefinition ColumnDefinition(): {

colDataType = ColDataType()

( parameter=CreateParameter() { columnSpecs.addAll(parameter); } )*
( LOOKAHEAD(2) parameter=CreateParameter() { columnSpecs.addAll(parameter); } )*

{
coldef = new ColumnDefinition();
Expand Down
27 changes: 27 additions & 0 deletions src/test/java/net/sf/jsqlparser/expression/CastExpressionTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*-
* #%L
* JSQLParser library
* %%
* Copyright (C) 2004 - 2021 JSQLParser
* %%
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
* #L%
*/

package net.sf.jsqlparser.expression;

import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.test.TestUtils;
import org.junit.Test;

/**
*
* @author <a href="mailto:andreas@manticore-projects.com">Andreas Reichel</a>
*/
public class CastExpressionTest {
@Test
public void testCastToRowConstructorIssue1267() throws JSQLParserException {
TestUtils.assertExpressionCanBeParsedAndDeparsed("CAST(ROW(dataid, value, calcMark) AS ROW(datapointid CHAR, value CHAR, calcMark CHAR))", true);
TestUtils.assertExpressionCanBeParsedAndDeparsed("CAST(ROW(dataid, value, calcMark) AS testcol)", true);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -256,4 +256,12 @@ public void testJsonAggregateFunction() throws JSQLParserException {
.parseExpression("JSON_ARRAYAGG( a FORMAT JSON ABSENT ON NULL ) FILTER( WHERE name = 'Raj' ) OVER( PARTITION BY name )")
.accept(adapter);
}

@Test
public void testRowConstructor() throws JSQLParserException {
ExpressionVisitorAdapter adapter = new ExpressionVisitorAdapter();
CCJSqlParserUtil
.parseExpression("CAST(ROW(dataid, value, calcMark) AS ROW(datapointid CHAR, value CHAR, calcMark CHAR))")
.accept(adapter);
}
}
11 changes: 11 additions & 0 deletions src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -4666,6 +4666,17 @@ public void testKeywordFilterIssue1255() throws JSQLParserException {
}

@Test
public void testUnionLimitOrderByIssue1268() throws JSQLParserException {
String sqlStr = "(SELECT __time FROM traffic_protocol_stat_log LIMIT 1) UNION ALL (SELECT __time FROM traffic_protocol_stat_log ORDER BY __time LIMIT 1)";
assertSqlCanBeParsedAndDeparsed(sqlStr, true);
}

@Test
public void testCastToRowConstructorIssue1267() throws JSQLParserException {
String sqlStr = "SELECT CAST(ROW(dataid, value, calcMark) AS ROW(datapointid CHAR, value CHAR, calcMark CHAR)) AS datapoints";
assertSqlCanBeParsedAndDeparsed(sqlStr, true);
}

public void testCollisionWithSpecialStringFunctionsIssue1284() throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed(
"SELECT test( a in (1) AND 2=2) ", true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,14 @@ public void testFeatureSetName() {
assertEquals("UPDATE + SELECT + feature set",
FeaturesAllowed.UPDATE.copy().add(new FeaturesAllowed(Feature.commit)).getName());
}

@Test
public void testRowConstructorValidation() throws JSQLParserException {
String stmt = "SELECT CAST(ROW(dataid, value, calcMark) AS ROW(datapointid CHAR, value CHAR, calcMark CHAR))";
List<ValidationError> errors = Validation.validate(
Arrays.asList(DatabaseType.ANSI_SQL, FeaturesAllowed.SELECT), stmt);
assertErrorsSize(errors, 0);
}


}

0 comments on commit c89cf21

Please sign in to comment.