diff --git a/src/main/java/net/sf/jsqlparser/statement/piped/CallPipeOperator.java b/src/main/java/net/sf/jsqlparser/statement/piped/CallPipeOperator.java index 982b715c1..d72c45ec1 100644 --- a/src/main/java/net/sf/jsqlparser/statement/piped/CallPipeOperator.java +++ b/src/main/java/net/sf/jsqlparser/statement/piped/CallPipeOperator.java @@ -1,6 +1,44 @@ package net.sf.jsqlparser.statement.piped; +import net.sf.jsqlparser.expression.Alias; +import net.sf.jsqlparser.statement.select.TableFunction; + public class CallPipeOperator extends PipeOperator { + private TableFunction tableFunction; + private Alias alias; + + public CallPipeOperator(TableFunction tableFunction, Alias alias) { + this.tableFunction = tableFunction; + this.alias = alias; + } + + public TableFunction getTableFunction() { + return tableFunction; + } + + public CallPipeOperator setTableFunction(TableFunction tableFunction) { + this.tableFunction = tableFunction; + return this; + } + + public Alias getAlias() { + return alias; + } + + public CallPipeOperator setAlias(Alias alias) { + this.alias = alias; + return this; + } + + @Override + public StringBuilder appendTo(StringBuilder builder) { + builder.append("|> CALL ").append(tableFunction); + if (alias != null) { + builder.append(" ").append(alias); + } + + return builder; + } @Override public T accept(PipeOperatorVisitor visitor, S context) { diff --git a/src/main/java/net/sf/jsqlparser/statement/piped/PivotPipeOperator.java b/src/main/java/net/sf/jsqlparser/statement/piped/PivotPipeOperator.java index 70d337189..fcfa13472 100644 --- a/src/main/java/net/sf/jsqlparser/statement/piped/PivotPipeOperator.java +++ b/src/main/java/net/sf/jsqlparser/statement/piped/PivotPipeOperator.java @@ -1,6 +1,78 @@ package net.sf.jsqlparser.statement.piped; +import net.sf.jsqlparser.expression.Alias; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.schema.Column; + public class PivotPipeOperator extends PipeOperator { + private Expression aggregateExpression; + private Column inputColumn; + private ExpressionList pivotColumns; + private Alias alias = null; + + public PivotPipeOperator(Expression aggregateExpression, Column inputColumn, + ExpressionList pivotColumns, Alias alias) { + this.aggregateExpression = aggregateExpression; + this.inputColumn = inputColumn; + this.pivotColumns = pivotColumns; + this.alias = alias; + } + + public Expression getAggregateExpression() { + return aggregateExpression; + } + + public PivotPipeOperator setAggregateExpression(Expression aggregateExpression) { + this.aggregateExpression = aggregateExpression; + return this; + } + + public Column getInputColumn() { + return inputColumn; + } + + public PivotPipeOperator setInputColumn(Column inputColumn) { + this.inputColumn = inputColumn; + return this; + } + + public ExpressionList getPivotColumns() { + return pivotColumns; + } + + public PivotPipeOperator setPivotColumns(ExpressionList pivotColumns) { + this.pivotColumns = pivotColumns; + return this; + } + + public Alias getAlias() { + return alias; + } + + public PivotPipeOperator setAlias(Alias alias) { + this.alias = alias; + return this; + } + + @Override + public StringBuilder appendTo(StringBuilder builder) { + builder + .append("|> ") + .append("PIVOT( ") + .append(aggregateExpression) + .append(" FOR ") + .append(inputColumn) + .append(" IN (") + .append(pivotColumns) + .append("))"); + if (alias != null) { + builder.append(" ").append(alias); + } + builder.append("\n"); + return builder; + } + @Override public T accept(PipeOperatorVisitor visitor, S context) { return visitor.visit(this, context); diff --git a/src/main/java/net/sf/jsqlparser/statement/piped/TableSamplePipeOperator.java b/src/main/java/net/sf/jsqlparser/statement/piped/TableSamplePipeOperator.java index cf209b966..e56b540ad 100644 --- a/src/main/java/net/sf/jsqlparser/statement/piped/TableSamplePipeOperator.java +++ b/src/main/java/net/sf/jsqlparser/statement/piped/TableSamplePipeOperator.java @@ -1,6 +1,31 @@ package net.sf.jsqlparser.statement.piped; public class TableSamplePipeOperator extends PipeOperator { + double percent; + + public TableSamplePipeOperator(double percent) { + this.percent = percent; + } + + public TableSamplePipeOperator(String percentStr) { + this.percent = Double.parseDouble(percentStr); + } + + public double getPercent() { + return percent; + } + + public TableSamplePipeOperator setPercent(double percent) { + this.percent = percent; + return this; + } + + @Override + public StringBuilder appendTo(StringBuilder builder) { + builder.append("|> ").append("TABLESAMPLE SYSTEM (").append(percent).append(" PERCENT)"); + return builder; + } + @Override public T accept(PipeOperatorVisitor visitor, S context) { return visitor.visit(this, context); diff --git a/src/main/java/net/sf/jsqlparser/statement/piped/UnPivotPipeOperator.java b/src/main/java/net/sf/jsqlparser/statement/piped/UnPivotPipeOperator.java index 5f4b7e328..80cf609c8 100644 --- a/src/main/java/net/sf/jsqlparser/statement/piped/UnPivotPipeOperator.java +++ b/src/main/java/net/sf/jsqlparser/statement/piped/UnPivotPipeOperator.java @@ -1,6 +1,77 @@ package net.sf.jsqlparser.statement.piped; +import net.sf.jsqlparser.expression.Alias; +import net.sf.jsqlparser.expression.operators.relational.ExpressionList; +import net.sf.jsqlparser.schema.Column; + public class UnPivotPipeOperator extends PipeOperator { + private Column valuesColumn; + private Column nameColumn; + private ExpressionList pivotColumns; + private Alias alias = null; + + public UnPivotPipeOperator(Column valuesColumn, Column nameColumn, + ExpressionList pivotColumns, Alias alias) { + this.valuesColumn = valuesColumn; + this.nameColumn = nameColumn; + this.pivotColumns = pivotColumns; + this.alias = alias; + } + + public Column getValuesColumn() { + return valuesColumn; + } + + public UnPivotPipeOperator setValuesColumn(Column valuesColumn) { + this.valuesColumn = valuesColumn; + return this; + } + + public Column getNameColumn() { + return nameColumn; + } + + public UnPivotPipeOperator setNameColumn(Column nameColumn) { + this.nameColumn = nameColumn; + return this; + } + + public ExpressionList getPivotColumns() { + return pivotColumns; + } + + public UnPivotPipeOperator setPivotColumns(ExpressionList pivotColumns) { + this.pivotColumns = pivotColumns; + return this; + } + + public Alias getAlias() { + return alias; + } + + public UnPivotPipeOperator setAlias(Alias alias) { + this.alias = alias; + return this; + } + + @Override + public StringBuilder appendTo(StringBuilder builder) { + builder + .append("|> ") + .append("UNPIVOT( ") + .append(valuesColumn) + .append(" FOR ") + .append(nameColumn) + .append(" IN (") + .append(pivotColumns) + .append("))"); + if (alias != null) { + builder.append(" ").append(alias); + } + builder.append("\n"); + return builder; + } + @Override public T accept(PipeOperatorVisitor visitor, S context) { return visitor.visit(this, context); diff --git a/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java b/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java index c17db5582..eecda1cf1 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java @@ -897,6 +897,12 @@ public StringBuilder visit(AsPipeOperator as, S context) { @Override public StringBuilder visit(CallPipeOperator call, S context) { + builder.append("|> CALL "); + call.getTableFunction().accept(this); + if (call.getAlias() != null) { + builder.append(" ").append(call.getAlias()); + } + return builder; } @@ -951,6 +957,19 @@ public StringBuilder visit(OrderByPipeOperator orderBy, S context) { @Override public StringBuilder visit(PivotPipeOperator pivot, S context) { + builder + .append("|> ") + .append("PIVOT( ") + .append(pivot.getAggregateExpression()) + .append(" FOR ") + .append(pivot.getInputColumn()) + .append(" IN (") + .append(pivot.getPivotColumns()) + .append("))"); + if (pivot.getAlias() != null) { + builder.append(" ").append(pivot.getAlias()); + } + builder.append("\n"); return builder; } @@ -983,6 +1002,8 @@ public StringBuilder visit(SetPipeOperator set, S context) { @Override public StringBuilder visit(TableSamplePipeOperator tableSample, S context) { + builder.append("|> ").append("TABLESAMPLE SYSTEM (").append(tableSample.getPercent()) + .append(" PERCENT)"); return builder; } @@ -1006,6 +1027,19 @@ public StringBuilder visit(SetOperationPipeOperator setOperationPipeOperator @Override public StringBuilder visit(UnPivotPipeOperator unPivot, S context) { + builder + .append("|> ") + .append("UNPIVOT( ") + .append(unPivot.getValuesColumn()) + .append(" FOR ") + .append(unPivot.getNameColumn()) + .append(" IN (") + .append(unPivot.getPivotColumns()) + .append("))"); + if (unPivot.getAlias() != null) { + builder.append(" ").append(unPivot.getAlias()); + } + builder.append("\n"); return builder; } diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index 2a29888b3..276983701 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -2377,6 +2377,14 @@ PipeOperator PipeOperator() #PipeOperator: operator = SetOperationPipeOperator() | operator = JoinPipeOperator() + | + operator = CallPipeOperator() + | + operator = TableSamplePipeOperator() + | + operator = PivotPipeOperator() + | + operator = UnPivotPipeOperator() ) { return operator; @@ -2562,6 +2570,67 @@ SetOperationPipeOperator SetOperationPipeOperator(): } } +CallPipeOperator CallPipeOperator(): +{ + TableFunction tableFunction; + Alias alias=null; +} +{ + tableFunction = TableFunction() [ LOOKAHEAD(2) alias = Alias() ] + + { + return new CallPipeOperator(tableFunction, alias); + } +} + + +TableSamplePipeOperator TableSamplePipeOperator(): +{ + Token token; +} +{ + "(" ( token= | token= ) ")" + { + return new TableSamplePipeOperator( token.image ); + } +} + +PivotPipeOperator PivotPipeOperator(): +{ + Expression aggregateExpression; + Column inputColumn; + ExpressionList pivotColumns; + Alias alias = null; +} +{ + "(" aggregateExpression=Expression() + inputColumn=Column() + "(" pivotColumns = ColumnList() ")" + ")" + [ LOOKAHEAD(2) alias = Alias() ] + { + return new PivotPipeOperator(aggregateExpression, inputColumn, pivotColumns, alias); + } +} + +UnPivotPipeOperator UnPivotPipeOperator(): +{ + Column valuesColumn; + Column nameColumn; + ExpressionList pivotColumns; + Alias alias = null; +} +{ + "(" valuesColumn=Column() + nameColumn=Column() + "(" pivotColumns = ColumnList() ")" + ")" + [ LOOKAHEAD(2) alias = Alias() ] + { + return new UnPivotPipeOperator(valuesColumn, nameColumn, pivotColumns, alias); + } +} + TableStatement TableStatement(): { Table table = null; @@ -2574,7 +2643,7 @@ TableStatement TableStatement(): table = Table() { tableStatement.setTable(table); } [ LOOKAHEAD( ) orderByElements = OrderByElements() { tableStatement.setOrderByElements(orderByElements); } ] - [ LOOKAHEAD() limit=LimitWithOffset() { tableStatement.setLimit(limit);} ] + [ LOOKAHEAD() limit = LimitWithOffset() { tableStatement.setLimit(limit);} ] [ LOOKAHEAD() offset = Offset() { tableStatement.setOffset(offset);} ] { return tableStatement; } /* Support operationList */ diff --git a/src/test/java/net/sf/jsqlparser/statement/piped/CallPipeOperatorTest.java b/src/test/java/net/sf/jsqlparser/statement/piped/CallPipeOperatorTest.java new file mode 100644 index 000000000..99e7dd1c3 --- /dev/null +++ b/src/test/java/net/sf/jsqlparser/statement/piped/CallPipeOperatorTest.java @@ -0,0 +1,16 @@ +package net.sf.jsqlparser.statement.piped; + +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.test.TestUtils; +import org.junit.jupiter.api.Test; + + +class CallPipeOperatorTest { + @Test + void testParseAndDeparse() throws JSQLParserException { + String sqlStr = "FROM input_table\n" + + "|> CALL tvf1(arg1)\n" + + "|> CALL tvf2(arg2, arg3);"; + TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true); + } +} diff --git a/src/test/java/net/sf/jsqlparser/statement/piped/PivotPipeOperatorTest.java b/src/test/java/net/sf/jsqlparser/statement/piped/PivotPipeOperatorTest.java new file mode 100644 index 000000000..7fb2fd1a7 --- /dev/null +++ b/src/test/java/net/sf/jsqlparser/statement/piped/PivotPipeOperatorTest.java @@ -0,0 +1,25 @@ +package net.sf.jsqlparser.statement.piped; + +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.test.TestUtils; +import org.junit.jupiter.api.Test; + + +class PivotPipeOperatorTest { + @Test + void testParseAndDeparse() throws JSQLParserException { + String sqlStr = "(\n" + + " SELECT \"kale\" AS product, 51 AS sales, \"Q1\" AS quarter\n" + + " UNION ALL\n" + + " SELECT \"kale\" AS product, 4 AS sales, \"Q1\" AS quarter\n" + + " UNION ALL\n" + + " SELECT \"kale\" AS product, 45 AS sales, \"Q2\" AS quarter\n" + + " UNION ALL\n" + + " SELECT \"apple\" AS product, 8 AS sales, \"Q1\" AS quarter\n" + + " UNION ALL\n" + + " SELECT \"apple\" AS product, 10 AS sales, \"Q2\" AS quarter\n" + + ")\n" + + "|> PIVOT(SUM(sales) FOR quarter IN (\"Q1\", \"Q2\"));"; + TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true); + } +} diff --git a/src/test/java/net/sf/jsqlparser/statement/piped/TableSamplePipeOperatorTest.java b/src/test/java/net/sf/jsqlparser/statement/piped/TableSamplePipeOperatorTest.java new file mode 100644 index 000000000..35dff43f6 --- /dev/null +++ b/src/test/java/net/sf/jsqlparser/statement/piped/TableSamplePipeOperatorTest.java @@ -0,0 +1,15 @@ +package net.sf.jsqlparser.statement.piped; + +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.test.TestUtils; +import org.junit.jupiter.api.Test; + + +class TableSamplePipeOperatorTest { + @Test + void testParseAndDeparse() throws JSQLParserException { + String sqlStr = "FROM LargeTable\n" + + "|> TABLESAMPLE SYSTEM (1.0 PERCENT);\n"; + TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true); + } +} diff --git a/src/test/java/net/sf/jsqlparser/statement/piped/UnPivotPipeOperatorTest.java b/src/test/java/net/sf/jsqlparser/statement/piped/UnPivotPipeOperatorTest.java new file mode 100644 index 000000000..f6216102f --- /dev/null +++ b/src/test/java/net/sf/jsqlparser/statement/piped/UnPivotPipeOperatorTest.java @@ -0,0 +1,20 @@ +package net.sf.jsqlparser.statement.piped; + +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.test.TestUtils; +import org.junit.jupiter.api.Test; + +class UnPivotPipeOperatorTest { + + @Test + void testParseAndDeparse() throws JSQLParserException { + String sqlStr = "(\n" + + " SELECT 'kale' as product, 55 AS Q1, 45 AS Q2\n" + + " UNION ALL\n" + + " SELECT 'apple', 8, 10\n" + + ")\n" + + "|> UNPIVOT(sales FOR quarter IN (Q1, Q2));"; + TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true); + } + +}