From d51461736cf6b468c4f170414c648619ed297866 Mon Sep 17 00:00:00 2001 From: minleejae Date: Fri, 18 Sep 2026 13:20:10 +0900 Subject: [PATCH 1/2] feat: support typed result columns in PostgreSQL ROWS FROM --- .../sf/jsqlparser/expression/Function.java | 24 ++- .../statement/select/TableFunction.java | 16 +- .../util/deparser/ExpressionDeParser.java | 2 +- .../util/deparser/SelectDeParser.java | 3 +- .../net/sf/jsqlparser/parser/JSqlParserCC.jjt | 39 ++++- .../select/RowsFromColumnDefinitionTest.java | 149 ++++++++++++++++++ .../statement/select/rows-from-invalid.sql | 8 + .../statement/select/rows-from-valid.sql | 12 ++ 8 files changed, 246 insertions(+), 7 deletions(-) create mode 100644 src/test/java/net/sf/jsqlparser/statement/select/RowsFromColumnDefinitionTest.java create mode 100644 src/test/resources/net/sf/jsqlparser/statement/select/rows-from-invalid.sql create mode 100644 src/test/resources/net/sf/jsqlparser/statement/select/rows-from-valid.sql diff --git a/src/main/java/net/sf/jsqlparser/expression/Function.java b/src/main/java/net/sf/jsqlparser/expression/Function.java index 581b13cf50..e411bab60f 100644 --- a/src/main/java/net/sf/jsqlparser/expression/Function.java +++ b/src/main/java/net/sf/jsqlparser/expression/Function.java @@ -17,6 +17,9 @@ import net.sf.jsqlparser.statement.select.Limit; import net.sf.jsqlparser.statement.select.OrderByElement; +import net.sf.jsqlparser.statement.create.table.ColumnDefinition; +import net.sf.jsqlparser.statement.select.PlainSelect; + import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; @@ -46,6 +49,7 @@ public class Function extends ASTNodeAccessImpl implements Expression { private KeepExpression keep = null; private String onOverflowTruncate = null; private String extraKeyword = null; + private List resultColumnDefinitions; /** * Generic keyword arguments captured inside function parentheses, e.g. @@ -55,6 +59,23 @@ public class Function extends ASTNodeAccessImpl implements Expression { */ private List keywordArguments = null; + /** Column definitions supplied for a record-returning function inside ROWS FROM. */ + public List getResultColumnDefinitions() { + return resultColumnDefinitions; + } + + public void setResultColumnDefinitions(List resultColumnDefinitions) { + this.resultColumnDefinitions = resultColumnDefinitions; + } + + public StringBuilder appendResultColumnDefinitionsTo(StringBuilder builder) { + if (resultColumnDefinitions != null) { + builder.append(" AS ") + .append(PlainSelect.getStringList(resultColumnDefinitions, true, true)); + } + return builder; + } + public Function() {} public Function(String name, Expression... parameters) { @@ -446,7 +467,8 @@ public String toString() { ans = "{fn " + ans + "}"; } - return ans; + return resultColumnDefinitions == null ? ans + : appendResultColumnDefinitionsTo(new StringBuilder(ans)).toString(); } public Function withAttribute(Expression attribute) { diff --git a/src/main/java/net/sf/jsqlparser/statement/select/TableFunction.java b/src/main/java/net/sf/jsqlparser/statement/select/TableFunction.java index 359ab7e770..5be3f50751 100644 --- a/src/main/java/net/sf/jsqlparser/statement/select/TableFunction.java +++ b/src/main/java/net/sf/jsqlparser/statement/select/TableFunction.java @@ -10,6 +10,7 @@ package net.sf.jsqlparser.statement.select; import java.util.List; +import java.util.function.Consumer; import net.sf.jsqlparser.expression.Alias; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.Function; @@ -228,13 +229,24 @@ public FromItem setSampleClause(SampleClause sampleClause) { } public StringBuilder appendTo(StringBuilder builder) { + return appendTo(builder, builder::append); + } + + public StringBuilder appendTo(StringBuilder builder, Consumer expressionPrinter) { if (prefix != null) { builder.append(prefix).append(" "); } if (rowsFromFunctions != null) { - builder.append("ROWS FROM ").append(rowsFromFunctions); + builder.append("ROWS FROM ("); + for (int i = 0; i < rowsFromFunctions.size(); i++) { + if (i > 0) { + builder.append(", "); + } + expressionPrinter.accept(rowsFromFunctions.get(i)); + } + builder.append(")"); } else { - builder.append(function); + expressionPrinter.accept(function); } if (withClause != null) { 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 6ffd4cbc29..da3aedc07a 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java @@ -1016,7 +1016,7 @@ public StringBuilder visit(Function function, S context) { if (function.isEscaped()) { builder.append("}"); } - return builder; + return function.appendResultColumnDefinitionsTo(builder); } @Override 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 d5d14aaecf..658a2fe15e 100644 --- a/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java +++ b/src/main/java/net/sf/jsqlparser/util/deparser/SelectDeParser.java @@ -906,7 +906,8 @@ public StringBuilder visit(TableStatement tableStatement, S context) { @Override public StringBuilder visit(TableFunction tableFunction, S context) { - tableFunction.appendTo(builder); + tableFunction.appendTo(builder, + expression -> expression.accept(expressionVisitor, context)); 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 fd6b41f64b..ca7c3d972d 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -13529,10 +13529,10 @@ ParenthesedExpressionList RowsFromFunctionList(): } { "(" - function = Function() { functions.add(function); } + function = RowsFromFunction() { functions.add(function); } ( "," - function = Function() { functions.add(function); } + function = RowsFromFunction() { functions.add(function); } )* ")" { @@ -13540,6 +13540,41 @@ ParenthesedExpressionList RowsFromFunctionList(): } } +/** A function and its optional PostgreSQL record column definition list. */ +Function RowsFromFunction(): +{ + Function function; + List columns = new ArrayList(); + ColumnDefinition column; +} +{ + function=Function() + [ "(" + column=RowsFromColumnDefinition() { columns.add(column); } + ( "," column=RowsFromColumnDefinition() { columns.add(column); } )* + ")" { function.setResultColumnDefinitions(columns); } + ] + { return function; } +} + +ColumnDefinition RowsFromColumnDefinition(): +{ + String name; + String collation; + String part; + ColDataType type; + ColumnDefinition column; +} +{ + name=RelObjectName() type=ColDataType() + { column = new ColumnDefinition(name, type); } + [ collation=RelObjectName() + ( "." part=RelObjectName() { collation += "." + part; } )* + { column.addColumnOptions(ColumnOption.collate(collation)); } + ] + { return column; } +} + List ColumnNamesWithParamsList() : { List colNames = new ArrayList(); String columnName; diff --git a/src/test/java/net/sf/jsqlparser/statement/select/RowsFromColumnDefinitionTest.java b/src/test/java/net/sf/jsqlparser/statement/select/RowsFromColumnDefinitionTest.java new file mode 100644 index 0000000000..fc88ffd5ff --- /dev/null +++ b/src/test/java/net/sf/jsqlparser/statement/select/RowsFromColumnDefinitionTest.java @@ -0,0 +1,149 @@ +/*- + * #%L + * JSQLParser library + * %% + * Copyright (C) 2004 - 2026 JSQLParser + * %% + * Dual licensed under GNU LGPL 2.1 or Apache License 2.0 + * #L% + */ +package net.sf.jsqlparser.statement.select; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Set; +import java.util.stream.Stream; +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.expression.Function; +import net.sf.jsqlparser.expression.LongValue; +import net.sf.jsqlparser.parser.AbstractJSqlParser.Dialect; +import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import net.sf.jsqlparser.statement.create.table.ColDataType; +import net.sf.jsqlparser.statement.create.table.ColumnDefinition; +import net.sf.jsqlparser.util.TablesNamesFinder; +import net.sf.jsqlparser.util.deparser.ExpressionDeParser; +import net.sf.jsqlparser.util.deparser.SelectDeParser; +import net.sf.jsqlparser.util.deparser.StatementDeParser; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +class RowsFromColumnDefinitionTest { + + // Accepted/rejected fixtures were checked against PostgreSQL 18.6. + static Stream validSql() throws IOException { + return fixture("rows-from-valid.sql"); + } + + static Stream invalidSql() throws IOException { + return fixture("rows-from-invalid.sql"); + } + + private static Stream fixture(String name) throws IOException { + try (InputStream input = RowsFromColumnDefinitionTest.class.getResourceAsStream(name)) { + return new String(input.readAllBytes(), StandardCharsets.UTF_8).lines() + .filter(line -> !line.isBlank()); + } + } + + @ParameterizedTest + @MethodSource("validSql") + void preservesDefinitionsAndOuterAliasesThroughBothRenderers(String sql) + throws JSQLParserException { + PlainSelect select = parse(sql); + StringBuilder visitor = new StringBuilder(); + select.accept(new StatementDeParser(visitor)); + for (String rendered : List.of(select.toString(), visitor.toString())) { + PlainSelect reparsed = parse(rendered); + assertEquals(select.toString(), reparsed.toString()); + assertEquals(select.getFromItem().getClass(), reparsed.getFromItem().getClass()); + } + } + + @ParameterizedTest + @MethodSource("invalidSql") + void rejectsTableConstraintsAndMalformedColumnLists(String sql) { + assertThrows(JSQLParserException.class, () -> parse(sql)); + } + + @Test + void eachFunctionOwnsItsDefinitionsIndependentlyOfTheOuterAlias() + throws JSQLParserException { + PlainSelect select = parse("SELECT * FROM ROWS FROM (" + + "json_to_record('{}') AS (amount numeric(10, 2), label text COLLATE \"C\"), " + + "generate_series(1, 2), json_to_record('{}') AS (items integer[])) " + + "WITH ORDINALITY AS r(total, name, n, data, ord)"); + TableFunction table = (TableFunction) select.getFromItem(); + assertTrue(table.isRowsFrom()); + assertEquals(3, table.getRowsFromFunctions().size()); + assertEquals("ORDINALITY", table.getWithClause()); + assertEquals(5, table.getAlias().getAliasColumns().size()); + List first = table.getFunctions().get(0).getResultColumnDefinitions(); + assertEquals("amount", first.get(0).getColumnName()); + assertEquals(10, first.get(0).getColDataType().getPrecision()); + assertEquals(2, first.get(0).getColDataType().getScale()); + assertEquals("\"C\"", first.get(1).getColumnOptions().get(0).getCollation()); + assertNull(table.getFunctions().get(1).getResultColumnDefinitions()); + assertEquals(1, table.getFunctions().get(2).getResultColumnDefinitions().get(0) + .getColDataType().getArrayData().size()); + + first.get(0).setColumnName("changed"); + first.get(0).setColDataType(new ColDataType("bigint")); + TableFunction reparsed = (TableFunction) parse(select.toString()).getFromItem(); + assertEquals("changed", reparsed.getFunctions().get(0).getResultColumnDefinitions() + .get(0).getColumnName()); + assertEquals("bigint", reparsed.getFunctions().get(0).getResultColumnDefinitions() + .get(0).getColDataType().getDataType()); + } + + @Test + void legacyFunctionListEditsKeepDefinitionsAttachedToTheirFunction() + throws JSQLParserException { + TableFunction table = (TableFunction) parse("SELECT * FROM ROWS FROM (" + + "json_to_record('{}') AS (a integer), generate_series(1, 2))").getFromItem(); + Function record = table.getRowsFromFunctions().remove(0); + table.getRowsFromFunctions().add(record); + TableFunction reparsed = (TableFunction) parse("SELECT * FROM " + table).getFromItem(); + assertNull(reparsed.getFunctions().get(0).getResultColumnDefinitions()); + assertNotNull(reparsed.getFunctions().get(1).getResultColumnDefinitions()); + } + + @Test + void tableFunctionRendererVisitsFunctionArguments() throws JSQLParserException { + PlainSelect select = parse("SELECT * FROM ROWS FROM (json_to_record('{}') AS (a integer), " + + "generate_series(1, 2)) WITH ORDINALITY AS r(a, n, ord)"); + String original = select.toString(); + StringBuilder sql = new StringBuilder(); + ExpressionDeParser expressions = new ExpressionDeParser() { + @Override + public StringBuilder visit(LongValue value, S context) { + return getBuilder().append(value.getValue() + 10); + } + }; + select.accept(new StatementDeParser(expressions, new SelectDeParser(), sql)); + assertTrue(sql.toString().contains("generate_series(11, 12)")); + assertTrue(sql.toString().contains("AS (a integer)")); + assertEquals(original, select.toString()); + assertEquals(sql.toString(), parse(sql.toString()).toString()); + } + + @Test + void tableDiscoveryStillVisitsSubqueriesInRecordFunctionArguments() + throws JSQLParserException { + String sql = "SELECT * FROM ROWS FROM (" + + "json_to_record((SELECT payload FROM events)) AS (a integer))"; + assertEquals(Set.of("events"), TablesNamesFinder.findTables(sql)); + } + + private static PlainSelect parse(String sql) throws JSQLParserException { + return (PlainSelect) CCJSqlParserUtil.parse(sql, p -> p.withDialect(Dialect.POSTGRESQL)); + } +} diff --git a/src/test/resources/net/sf/jsqlparser/statement/select/rows-from-invalid.sql b/src/test/resources/net/sf/jsqlparser/statement/select/rows-from-invalid.sql new file mode 100644 index 0000000000..1205ef1a60 --- /dev/null +++ b/src/test/resources/net/sf/jsqlparser/statement/select/rows-from-invalid.sql @@ -0,0 +1,8 @@ +SELECT * FROM ROWS FROM (json_to_record('{}') AS ()) +SELECT * FROM ROWS FROM (json_to_record('{}') AS (a)) +SELECT * FROM ROWS FROM (json_to_record('{}') AS (a integer,)) +SELECT * FROM ROWS FROM (json_to_record('{}') AS (a integer NOT NULL)) +SELECT * FROM ROWS FROM (json_to_record('{}') AS (a integer DEFAULT 1)) +SELECT * FROM ROWS FROM (json_to_record('{}') AS r(a integer)) +SELECT * FROM ROWS FROM (json_to_record('{}') AS (a integer) WITH ORDINALITY) +SELECT * FROM ROWS FROM () diff --git a/src/test/resources/net/sf/jsqlparser/statement/select/rows-from-valid.sql b/src/test/resources/net/sf/jsqlparser/statement/select/rows-from-valid.sql new file mode 100644 index 0000000000..b98f83bb36 --- /dev/null +++ b/src/test/resources/net/sf/jsqlparser/statement/select/rows-from-valid.sql @@ -0,0 +1,12 @@ +SELECT * FROM ROWS FROM (json_to_recordset('[{"a":1}]') AS (a integer)) WITH ORDINALITY +SELECT * FROM ROWS FROM (json_to_recordset('[{"a":1,"b":"x"}]') AS (a integer, b text)) AS r +SELECT * FROM ROWS FROM (json_to_recordset('[{"a":1}]') AS (a integer), generate_series(1, 2)) WITH ORDINALITY AS r(a, n, ord) +SELECT * FROM ROWS FROM (generate_series(1, 2), jsonb_to_recordset('[{"a":1.25}]'::jsonb) AS (a numeric(10, 2))) AS r(n, amount) +SELECT * FROM ROWS FROM (json_to_record('{"a":"x"}') AS (a text COLLATE "C")) +SELECT * FROM ROWS FROM (json_to_record('{"a":"x"}') AS (a text COLLATE pg_catalog."C")) +SELECT * FROM ROWS FROM (json_to_record('{"a":[1,2]}') AS (a integer[])) +SELECT * FROM ROWS FROM (json_to_record('{"a":"2026-01-01T12:00:00Z"}') AS (a timestamp(6) with time zone)) +SELECT * FROM ROWS FROM (json_to_record('{"odd name":1}') AS ("odd name" integer)) AS r("renamed") +SELECT r.* FROM (VALUES ('[{"a":1}]'::json)) AS src(payload) CROSS JOIN LATERAL ROWS FROM (json_to_recordset(src.payload) AS (a integer)) WITH ORDINALITY AS r +SELECT * FROM ROWS FROM (generate_series(1, 2), generate_series(1, 1)) AS r(a, b) +SELECT * FROM ROWS FROM (json_to_record('{"a":1}') AS (a integer), json_to_record('{"b":2}') AS (b integer)) From 322be2c4a8f9cac0d89b2996982ee706d7c50968 Mon Sep 17 00:00:00 2001 From: minleejae Date: Fri, 18 Sep 2026 14:22:50 +0900 Subject: [PATCH 2/2] test: store ROWS FROM fixtures as structured test data --- .../select/RowsFromColumnDefinitionTest.java | 38 +++++-------------- .../statement/select/rows-from-cases.tsv | 20 ++++++++++ .../statement/select/rows-from-invalid.sql | 8 ---- .../statement/select/rows-from-valid.sql | 12 ------ 4 files changed, 29 insertions(+), 49 deletions(-) create mode 100644 src/test/resources/net/sf/jsqlparser/statement/select/rows-from-cases.tsv delete mode 100644 src/test/resources/net/sf/jsqlparser/statement/select/rows-from-invalid.sql delete mode 100644 src/test/resources/net/sf/jsqlparser/statement/select/rows-from-valid.sql diff --git a/src/test/java/net/sf/jsqlparser/statement/select/RowsFromColumnDefinitionTest.java b/src/test/java/net/sf/jsqlparser/statement/select/RowsFromColumnDefinitionTest.java index fc88ffd5ff..46065348d7 100644 --- a/src/test/java/net/sf/jsqlparser/statement/select/RowsFromColumnDefinitionTest.java +++ b/src/test/java/net/sf/jsqlparser/statement/select/RowsFromColumnDefinitionTest.java @@ -15,12 +15,8 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Set; -import java.util.stream.Stream; import net.sf.jsqlparser.JSQLParserException; import net.sf.jsqlparser.expression.Function; import net.sf.jsqlparser.expression.LongValue; @@ -34,30 +30,20 @@ import net.sf.jsqlparser.util.deparser.StatementDeParser; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.CsvFileSource; class RowsFromColumnDefinitionTest { - // Accepted/rejected fixtures were checked against PostgreSQL 18.6. - static Stream validSql() throws IOException { - return fixture("rows-from-valid.sql"); - } - - static Stream invalidSql() throws IOException { - return fixture("rows-from-invalid.sql"); - } - - private static Stream fixture(String name) throws IOException { - try (InputStream input = RowsFromColumnDefinitionTest.class.getResourceAsStream(name)) { - return new String(input.readAllBytes(), StandardCharsets.UTF_8).lines() - .filter(line -> !line.isBlank()); - } - } - + // Accepted/rejected cases are data records, not executable SQL batch scripts. + // Both outcomes were checked against PostgreSQL 18.6. @ParameterizedTest - @MethodSource("validSql") - void preservesDefinitionsAndOuterAliasesThroughBothRenderers(String sql) + @CsvFileSource(resources = "rows-from-cases.tsv", delimiter = '\t') + void preservesValidDefinitionsAndRejectsMalformedLists(boolean valid, String sql) throws JSQLParserException { + if (!valid) { + assertThrows(JSQLParserException.class, () -> parse(sql)); + return; + } PlainSelect select = parse(sql); StringBuilder visitor = new StringBuilder(); select.accept(new StatementDeParser(visitor)); @@ -68,12 +54,6 @@ void preservesDefinitionsAndOuterAliasesThroughBothRenderers(String sql) } } - @ParameterizedTest - @MethodSource("invalidSql") - void rejectsTableConstraintsAndMalformedColumnLists(String sql) { - assertThrows(JSQLParserException.class, () -> parse(sql)); - } - @Test void eachFunctionOwnsItsDefinitionsIndependentlyOfTheOuterAlias() throws JSQLParserException { diff --git a/src/test/resources/net/sf/jsqlparser/statement/select/rows-from-cases.tsv b/src/test/resources/net/sf/jsqlparser/statement/select/rows-from-cases.tsv new file mode 100644 index 0000000000..c7c18216ae --- /dev/null +++ b/src/test/resources/net/sf/jsqlparser/statement/select/rows-from-cases.tsv @@ -0,0 +1,20 @@ +true SELECT * FROM ROWS FROM (json_to_recordset('[{"a":1}]') AS (a integer)) WITH ORDINALITY +true SELECT * FROM ROWS FROM (json_to_recordset('[{"a":1,"b":"x"}]') AS (a integer, b text)) AS r +true SELECT * FROM ROWS FROM (json_to_recordset('[{"a":1}]') AS (a integer), generate_series(1, 2)) WITH ORDINALITY AS r(a, n, ord) +true SELECT * FROM ROWS FROM (generate_series(1, 2), jsonb_to_recordset('[{"a":1.25}]'::jsonb) AS (a numeric(10, 2))) AS r(n, amount) +true SELECT * FROM ROWS FROM (json_to_record('{"a":"x"}') AS (a text COLLATE "C")) +true SELECT * FROM ROWS FROM (json_to_record('{"a":"x"}') AS (a text COLLATE pg_catalog."C")) +true SELECT * FROM ROWS FROM (json_to_record('{"a":[1,2]}') AS (a integer[])) +true SELECT * FROM ROWS FROM (json_to_record('{"a":"2026-01-01T12:00:00Z"}') AS (a timestamp(6) with time zone)) +true SELECT * FROM ROWS FROM (json_to_record('{"odd name":1}') AS ("odd name" integer)) AS r("renamed") +true SELECT r.* FROM (VALUES ('[{"a":1}]'::json)) AS src(payload) CROSS JOIN LATERAL ROWS FROM (json_to_recordset(src.payload) AS (a integer)) WITH ORDINALITY AS r +true SELECT * FROM ROWS FROM (generate_series(1, 2), generate_series(1, 1)) AS r(a, b) +true SELECT * FROM ROWS FROM (json_to_record('{"a":1}') AS (a integer), json_to_record('{"b":2}') AS (b integer)) +false SELECT * FROM ROWS FROM (json_to_record('{}') AS ()) +false SELECT * FROM ROWS FROM (json_to_record('{}') AS (a)) +false SELECT * FROM ROWS FROM (json_to_record('{}') AS (a integer,)) +false SELECT * FROM ROWS FROM (json_to_record('{}') AS (a integer NOT NULL)) +false SELECT * FROM ROWS FROM (json_to_record('{}') AS (a integer DEFAULT 1)) +false SELECT * FROM ROWS FROM (json_to_record('{}') AS r(a integer)) +false SELECT * FROM ROWS FROM (json_to_record('{}') AS (a integer) WITH ORDINALITY) +false SELECT * FROM ROWS FROM () diff --git a/src/test/resources/net/sf/jsqlparser/statement/select/rows-from-invalid.sql b/src/test/resources/net/sf/jsqlparser/statement/select/rows-from-invalid.sql deleted file mode 100644 index 1205ef1a60..0000000000 --- a/src/test/resources/net/sf/jsqlparser/statement/select/rows-from-invalid.sql +++ /dev/null @@ -1,8 +0,0 @@ -SELECT * FROM ROWS FROM (json_to_record('{}') AS ()) -SELECT * FROM ROWS FROM (json_to_record('{}') AS (a)) -SELECT * FROM ROWS FROM (json_to_record('{}') AS (a integer,)) -SELECT * FROM ROWS FROM (json_to_record('{}') AS (a integer NOT NULL)) -SELECT * FROM ROWS FROM (json_to_record('{}') AS (a integer DEFAULT 1)) -SELECT * FROM ROWS FROM (json_to_record('{}') AS r(a integer)) -SELECT * FROM ROWS FROM (json_to_record('{}') AS (a integer) WITH ORDINALITY) -SELECT * FROM ROWS FROM () diff --git a/src/test/resources/net/sf/jsqlparser/statement/select/rows-from-valid.sql b/src/test/resources/net/sf/jsqlparser/statement/select/rows-from-valid.sql deleted file mode 100644 index b98f83bb36..0000000000 --- a/src/test/resources/net/sf/jsqlparser/statement/select/rows-from-valid.sql +++ /dev/null @@ -1,12 +0,0 @@ -SELECT * FROM ROWS FROM (json_to_recordset('[{"a":1}]') AS (a integer)) WITH ORDINALITY -SELECT * FROM ROWS FROM (json_to_recordset('[{"a":1,"b":"x"}]') AS (a integer, b text)) AS r -SELECT * FROM ROWS FROM (json_to_recordset('[{"a":1}]') AS (a integer), generate_series(1, 2)) WITH ORDINALITY AS r(a, n, ord) -SELECT * FROM ROWS FROM (generate_series(1, 2), jsonb_to_recordset('[{"a":1.25}]'::jsonb) AS (a numeric(10, 2))) AS r(n, amount) -SELECT * FROM ROWS FROM (json_to_record('{"a":"x"}') AS (a text COLLATE "C")) -SELECT * FROM ROWS FROM (json_to_record('{"a":"x"}') AS (a text COLLATE pg_catalog."C")) -SELECT * FROM ROWS FROM (json_to_record('{"a":[1,2]}') AS (a integer[])) -SELECT * FROM ROWS FROM (json_to_record('{"a":"2026-01-01T12:00:00Z"}') AS (a timestamp(6) with time zone)) -SELECT * FROM ROWS FROM (json_to_record('{"odd name":1}') AS ("odd name" integer)) AS r("renamed") -SELECT r.* FROM (VALUES ('[{"a":1}]'::json)) AS src(payload) CROSS JOIN LATERAL ROWS FROM (json_to_recordset(src.payload) AS (a integer)) WITH ORDINALITY AS r -SELECT * FROM ROWS FROM (generate_series(1, 2), generate_series(1, 1)) AS r(a, b) -SELECT * FROM ROWS FROM (json_to_record('{"a":1}') AS (a integer), json_to_record('{"b":2}') AS (b integer))