From cdda855b6dbd16ffad8faed00574d8c680af8ba8 Mon Sep 17 00:00:00 2001 From: gs-jp1 <80327721+gs-jp1@users.noreply.github.com> Date: Tue, 10 Oct 2023 09:29:38 +0100 Subject: [PATCH] Legend Engine - Assortment of changes (#2349) 1. refactor tests for simplified reading 2. autogenerate schema protocol models 3. grammar composer support tablesubquery --- .../legend-engine-xt-sql-compiler/pom.xml | 16 + .../sql/grammar/to/SQLGrammarComposer.java | 2 +- .../test/roundtrip/TestSQLRoundTrip.java | 6 + .../legend-engine-xt-sql-protocol/pom.xml | 16 + ...l_query_sql_schema_metamodel.protocol.json | 6 + .../schema_metamodel.pure | 41 + .../binding/fromPure/fromPure.pure | 4 +- .../binding/fromPure/tests/testTranspile.pure | 1166 ++++++++--------- .../binding/model.pure | 100 -- .../binding/schema.pure | 58 + .../legend-engine-xt-sql-query/pom.xml | 4 - .../engine/query/sql/api/SQLExecutor.java | 293 +++++ .../query/sql/api/execute/SqlExecute.java | 287 +--- .../query/sql/api/execute/SqlExecuteTest.java | 103 +- 14 files changed, 1113 insertions(+), 989 deletions(-) create mode 100644 legend-engine-xts-sql/legend-engine-xt-sql-protocol/src/main/resources/core_external_query_sql_schema_metamodel.protocol.json create mode 100644 legend-engine-xts-sql/legend-engine-xt-sql-pure-metamodel/src/main/resources/core_external_query_sql_metamodel/schema_metamodel.pure delete mode 100644 legend-engine-xts-sql/legend-engine-xt-sql-pure/src/main/resources/core_external_query_sql/binding/model.pure create mode 100644 legend-engine-xts-sql/legend-engine-xt-sql-pure/src/main/resources/core_external_query_sql/binding/schema.pure create mode 100644 legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/SQLExecutor.java diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-compiler/pom.xml b/legend-engine-xts-sql/legend-engine-xt-sql-compiler/pom.xml index 60b55bdd533..5beacacb8f2 100644 --- a/legend-engine-xts-sql/legend-engine-xt-sql-compiler/pom.xml +++ b/legend-engine-xts-sql/legend-engine-xt-sql-compiler/pom.xml @@ -52,6 +52,22 @@ test + + Generate translator for protocol + generate-sources + + java + + + true + org.finos.legend.engine.protocol.generation.GenerateMetamodelToProtocolTranslator + + core_external_query_sql_schema_metamodel.protocol.json + ${project.build.directory}/generated-sources/ + + test + + diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-grammar/src/main/java/org/finos/legend/engine/language/sql/grammar/to/SQLGrammarComposer.java b/legend-engine-xts-sql/legend-engine-xt-sql-grammar/src/main/java/org/finos/legend/engine/language/sql/grammar/to/SQLGrammarComposer.java index 1e6b49eb802..57aaa1af936 100644 --- a/legend-engine-xts-sql/legend-engine-xt-sql-grammar/src/main/java/org/finos/legend/engine/language/sql/grammar/to/SQLGrammarComposer.java +++ b/legend-engine-xts-sql/legend-engine-xt-sql-grammar/src/main/java/org/finos/legend/engine/language/sql/grammar/to/SQLGrammarComposer.java @@ -527,7 +527,7 @@ public String visit(TableFunction val) @Override public String visit(TableSubquery val) { - return null; + return "(" + visit(val.query) + ")"; } @Override diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-grammar/src/test/java/org/finos/legend/engine/language/sql/grammar/test/roundtrip/TestSQLRoundTrip.java b/legend-engine-xts-sql/legend-engine-xt-sql-grammar/src/test/java/org/finos/legend/engine/language/sql/grammar/test/roundtrip/TestSQLRoundTrip.java index 4c9901b5bb0..befce651ac1 100644 --- a/legend-engine-xts-sql/legend-engine-xt-sql-grammar/src/test/java/org/finos/legend/engine/language/sql/grammar/test/roundtrip/TestSQLRoundTrip.java +++ b/legend-engine-xts-sql/legend-engine-xt-sql-grammar/src/test/java/org/finos/legend/engine/language/sql/grammar/test/roundtrip/TestSQLRoundTrip.java @@ -288,6 +288,12 @@ public void testWithinGroup() check("SELECT percentile_cont(0.1) WITHIN GROUP (ORDER BY a ASC) FROM myTable"); } + @Test + public void testNested() + { + check("SELECT * from (select col from myTable)"); + } + private void fail(String sql, int start, int end, String message) { try diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-protocol/pom.xml b/legend-engine-xts-sql/legend-engine-xt-sql-protocol/pom.xml index cd9d62c0365..e9bebe9d248 100644 --- a/legend-engine-xts-sql/legend-engine-xt-sql-protocol/pom.xml +++ b/legend-engine-xts-sql/legend-engine-xt-sql-protocol/pom.xml @@ -52,6 +52,22 @@ test + + generate schema metamodel + generate-sources + + java + + + true + org.finos.legend.engine.protocol.generation.GenerateMetaClasses + + core_external_query_sql_schema_metamodel.protocol.json + ${project.build.directory}/generated-sources/ + + test + + diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-protocol/src/main/resources/core_external_query_sql_schema_metamodel.protocol.json b/legend-engine-xts-sql/legend-engine-xt-sql-protocol/src/main/resources/core_external_query_sql_schema_metamodel.protocol.json new file mode 100644 index 00000000000..e973ac4b716 --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-protocol/src/main/resources/core_external_query_sql_schema_metamodel.protocol.json @@ -0,0 +1,6 @@ +{ + "purePackage": "meta::external::query::sql::schema::metamodel", + "javaPackage": "org.finos.legend.engine.protocol.sql.schema.metamodel", + "elementsToBeExcluded": [ + ] +} \ No newline at end of file diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-pure-metamodel/src/main/resources/core_external_query_sql_metamodel/schema_metamodel.pure b/legend-engine-xts-sql/legend-engine-xt-sql-pure-metamodel/src/main/resources/core_external_query_sql_metamodel/schema_metamodel.pure new file mode 100644 index 00000000000..495a29cf632 --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-pure-metamodel/src/main/resources/core_external_query_sql_metamodel/schema_metamodel.pure @@ -0,0 +1,41 @@ +Class meta::external::query::sql::schema::metamodel::SchemaColumn +{ + <> name: String[1]; +} + +Class meta::external::query::sql::schema::metamodel::PrimitiveSchemaColumn extends meta::external::query::sql::schema::metamodel::SchemaColumn +{ + <> type: meta::external::query::sql::schema::metamodel::PrimitiveType[1]; +} + +Class meta::external::query::sql::schema::metamodel::EnumSchemaColumn extends meta::external::query::sql::schema::metamodel::SchemaColumn +{ + <> type: String[1]; +} + + +Class meta::external::query::sql::schema::metamodel::Schema +{ + <> columns: meta::external::query::sql::schema::metamodel::SchemaColumn[*]; + <> enums: meta::external::query::sql::schema::metamodel::Enum[*]; +} + +Enum meta::external::query::sql::schema::metamodel::PrimitiveType +{ + Boolean, + StrictDate, + Number, + String, + LatestDate, + Float, + DateTime, + Date, + Integer, + Decimal +} + +Class meta::external::query::sql::schema::metamodel::Enum +{ + <> type: String[1]; + <> values: String[*]; +} \ No newline at end of file diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-pure/src/main/resources/core_external_query_sql/binding/fromPure/fromPure.pure b/legend-engine-xts-sql/legend-engine-xt-sql-pure/src/main/resources/core_external_query_sql/binding/fromPure/fromPure.pure index 90bb05666b6..f451545280f 100644 --- a/legend-engine-xts-sql/legend-engine-xt-sql-pure/src/main/resources/core_external_query_sql/binding/fromPure/fromPure.pure +++ b/legend-engine-xts-sql/legend-engine-xt-sql-pure/src/main/resources/core_external_query_sql/binding/fromPure/fromPure.pure @@ -155,13 +155,13 @@ function meta::external::query::sql::transformation::queryToPure::getSchemaFromS sources: SQLSource[*], query: meta::external::query::sql::metamodel::Node[1], extensions: meta::pure::extension::Extension[*] - ): meta::external::query::sql::Schema[1] + ): meta::external::query::sql::schema::metamodel::Schema[1] { let sqlTransformContext = processRootQuery($sources, $query, $extensions); getSchema($sqlTransformContext); } -function meta::external::query::sql::transformation::queryToPure::getSchema(context:SqlTransformContext[1]): meta::external::query::sql::Schema[1] +function meta::external::query::sql::transformation::queryToPure::getSchema(context:SqlTransformContext[1]): meta::external::query::sql::schema::metamodel::Schema[1] { $context.columns()->meta::external::query::sql::tdsColsToSchema(); } diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-pure/src/main/resources/core_external_query_sql/binding/fromPure/tests/testTranspile.pure b/legend-engine-xts-sql/legend-engine-xt-sql-pure/src/main/resources/core_external_query_sql/binding/fromPure/tests/testTranspile.pure index 6ce75ddc42d..1d745b94da8 100644 --- a/legend-engine-xts-sql/legend-engine-xt-sql-pure/src/main/resources/core_external_query_sql/binding/fromPure/tests/testTranspile.pure +++ b/legend-engine-xts-sql/legend-engine-xt-sql-pure/src/main/resources/core_external_query_sql/binding/fromPure/tests/testTranspile.pure @@ -19,6 +19,7 @@ import meta::external::query::sql::transformation::queryToPure::*; import meta::external::query::sql::metamodel::*; import meta::external::query::sql::transformation::queryToPure::tests::*; import meta::external::query::sql::*; +import meta::external::query::sql::schema::metamodel::*; import meta::legend::service::metamodel::*; import meta::pure::functions::meta::*; import meta::pure::mapping::*; @@ -27,34 +28,36 @@ import meta::pure::metamodel::serialization::grammar::*; //SELECT function <> meta::external::query::sql::transformation::queryToPure::tests::testSelectStar():Boolean[1] { - let sqlString = 'SELECT * FROM service."/service/service1"'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT * FROM service."/service/service1"', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + } + ) } function <> meta::external::query::sql::transformation::queryToPure::tests::testSelectNull():Boolean[1] { - let sqlString = 'SELECT NULL FROM service."/service/service1"'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT NULL FROM service."/service/service1"', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ])->project([ col(row:TDSRow[1] | []->cast(@String), 'NULL') ]) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + } + ); } function <> meta::external::query::sql::transformation::queryToPure::tests::testSelectStarFromJoinTable():Boolean[1] { - let sqlString = 'SELECT table1.* FROM service."/service/service1" "table1" LEFT OUTER JOIN (SELECT * FROM service."/service/service2") AS "table2" ON "table1"."Integer" = "table2"."Integer"'; + test( + 'SELECT table1.* FROM service."/service/service1" "table1" LEFT OUTER JOIN (SELECT * FROM service."/service/service2") AS "table2" ON "table1"."Integer" = "table2"."Integer"', - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String']) ->renameColumns([ @@ -83,16 +86,16 @@ function <> meta::external::query::sql::transformation::queryToPure:: pair('DateTime_table1', 'DateTime'), pair('String_table1', 'String') ])->restrict(['Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String']) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + } + ) } function <> meta::external::query::sql::transformation::queryToPure::tests::testSelectStarAndColumnsFromJoinTable():Boolean[1] { - let sqlString = 'SELECT table1.*, table2.id, sin(table2.int) FROM service."/service/service1" "table1" LEFT OUTER JOIN (SELECT ID as "id", Integer AS "int" FROM service."/service/service2") AS "table2" ON "table1"."Integer" = "table2"."int"'; + test( + 'SELECT table1.*, table2.id, sin(table2.int) FROM service."/service/service1" "table1" LEFT OUTER JOIN (SELECT ID as "id", Integer AS "int" FROM service."/service/service2") AS "table2" ON "table1"."Integer" = "table2"."int"', - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String'])->renameColumns([ pair('Boolean', 'Boolean_table1'), @@ -124,359 +127,358 @@ function <> meta::external::query::sql::transformation::queryToPure:: col(row:TDSRow[1] | $row.getInteger('id_table2'), 'id'), col(row:TDSRow[1] | sin($row.getInteger('int_table2')), 'sin(int)') ]) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + } + ) } function <> meta::external::query::sql::transformation::queryToPure::tests::testSelectColumns():Boolean[1] { - let sqlString = 'SELECT Boolean, Integer FROM service."/service/service1"'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT Boolean, Integer FROM service."/service/service1"', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->restrict(['Boolean', 'Integer']) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } function <> meta::external::query::sql::transformation::queryToPure::tests::testSelectWithAliases():Boolean[1] { - let sqlString = 'SELECT Boolean, Integer AS int FROM service."/service/service1"'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT Boolean, Integer AS int FROM service."/service/service1"', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->restrict(['Boolean', 'Integer']) ->renameColumns(pair('Integer', 'int')) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } function <> meta::external::query::sql::transformation::queryToPure::tests::testSelectColumnMultiTimes():Boolean[1] { - let sqlString = 'SELECT String, String as "str" FROM service."/service/service1"'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT String, String as "str" FROM service."/service/service1"', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ])->project([ col(row:TDSRow[1] | $row.getString('String'), 'String'), col(row:TDSRow[1] | $row.getString('String'), 'str') ]) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } function <> meta::external::query::sql::transformation::queryToPure::tests::testSelectColumnMultiTimesRealiasToExisting():Boolean[1] { - let sqlString = 'SELECT String as "str", String as "String" FROM service."/service/service1"'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT String as "str", String as "String" FROM service."/service/service1"', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ])->project([ col(row:TDSRow[1] | $row.getString('String'), 'str'), col(row:TDSRow[1] | $row.getString('String'), 'String') ]) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } function <> meta::external::query::sql::transformation::queryToPure::tests::testSelectColumnAliasedAsUnusedTableColumnName():Boolean[1] { - let sqlString = 'SELECT Integer as "String" FROM service."/service/service1"'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT Integer as "String" FROM service."/service/service1"', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->restrict('Integer')->renameColumns(pair('Integer', 'String')) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } function <> meta::external::query::sql::transformation::queryToPure::tests::testSelectColumnMultiTimesGroupBy():Boolean[1] { - let sqlString = 'SELECT String, String as "str" FROM service."/service/service1" group by 1'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT String, String as "str" FROM service."/service/service1" group by 1', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ])->project([ col(row:TDSRow[1] | $row.getString('String'), 'String'), col(row:TDSRow[1] | $row.getString('String'), 'str') ])->restrict(['String', 'str'])->distinct() - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } function <> meta::external::query::sql::transformation::queryToPure::tests::testSelectQualified():Boolean[1] { - let sqlString = 'SELECT Boolean, service."/service/service1".Integer FROM service."/service/service1"'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT Boolean, service."/service/service1".Integer FROM service."/service/service1"', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->restrict(['Boolean', 'Integer']) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } function <> meta::external::query::sql::transformation::queryToPure::tests::testSelectQualifiedWithAlias():Boolean[1] { - let sqlString = 'SELECT Boolean, table1.Integer FROM service."/service/service1" AS table1'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT Boolean, table1.Integer FROM service."/service/service1" AS table1', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->restrict(['Boolean', 'Integer']) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } function <> meta::external::query::sql::transformation::queryToPure::tests::testTableFunc():Boolean[1] { - let sqlString = 'SELECT Boolean, table1.Integer FROM service(\'/service/service1\') AS table1'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT Boolean, table1.Integer FROM service(\'/service/service1\') AS table1', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->restrict(['Boolean', 'Integer']) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } function <> meta::external::query::sql::transformation::queryToPure::tests::testSelectQuotedSingleColumn():Boolean[1] { - let sqlString = 'SELECT "Boolean", "Integer" FROM service."/service/service1"'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT "Boolean", "Integer" FROM service."/service/service1"', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->restrict(['Boolean', 'Integer']) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } function <> meta::external::query::sql::transformation::queryToPure::tests::testSelectUnquotedSingleColumn():Boolean[1] { - let sqlString = 'SELECT Boolean, Integer FROM service."/service/service1"'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT Boolean, Integer FROM service."/service/service1"', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->restrict(['Boolean', 'Integer']) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } function <> meta::external::query::sql::transformation::queryToPure::tests::testSimplificationOfNullablesComplex():Boolean[1] { - let sqlString = 'SELECT (date_trunc(\'day\', CAST(\'1900-01-01\' AS DATE) + NULL * INTERVAL \'1 DAY\') + 1 * INTERVAL \'1 YEAR\') AS "NULL1", ' + - 'CAST( FLOOR(EXTRACT(EPOCH FROM CAST((CAST(\'1900-01-01 00:00:00\' AS TIMESTAMP) + NULL * INTERVAL \'1 DAY\') AS TIMESTAMP)) / 86400) - FLOOR(EXTRACT(EPOCH FROM CAST((CAST(\'1900-01-01 00:00:00\' AS TIMESTAMP) + NULL * INTERVAL \'1 DAY\') AS TIMESTAMP)) / 86400) AS BIGINT) AS "NULL2" FROM service."/service/service1"'; - let sqlTransformContext = $sqlString->processQuery(); + test( + 'SELECT (date_trunc(\'day\', CAST(\'1900-01-01\' AS DATE) + NULL * INTERVAL \'1 DAY\') + 1 * INTERVAL \'1 YEAR\') AS "NULL1", ' + + 'CAST( FLOOR(EXTRACT(EPOCH FROM CAST((CAST(\'1900-01-01 00:00:00\' AS TIMESTAMP) + NULL * INTERVAL \'1 DAY\') AS TIMESTAMP)) / 86400) - ' + + 'FLOOR(EXTRACT(EPOCH FROM CAST((CAST(\'1900-01-01 00:00:00\' AS TIMESTAMP) + NULL * INTERVAL \'1 DAY\') AS TIMESTAMP)) / 86400) AS BIGINT) AS "NULL2" FROM service."/service/service1"', - let expected = {| FlatInput.all()->project( + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ])->project([ col(row:TDSRow[1] | []->cast(@Date), 'NULL1'), col(row:TDSRow[1] | []->cast(@Integer), 'NULL2') ]) - }; - - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } //DISTINCT function <> meta::external::query::sql::transformation::queryToPure::tests::testDistinctAllColumns():Boolean[1] { - let sqlString = 'SELECT DISTINCT * FROM service."/service/service1"'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT DISTINCT * FROM service."/service/service1"', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->distinct() - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } function <> meta::external::query::sql::transformation::queryToPure::tests::testDistinctSingleColumn():Boolean[1] { - let sqlString = 'SELECT DISTINCT Boolean FROM service."/service/service1"'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT DISTINCT Boolean FROM service."/service/service1"', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->restrict(['Boolean']) ->distinct() - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } //LIMIT-OFFSET function <> meta::external::query::sql::transformation::queryToPure::tests::testLimit():Boolean[1] { - let sqlString = 'SELECT * FROM service."/service/service1" LIMIT 2'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT * FROM service."/service/service1" LIMIT 2', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->limit(2) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } function <> meta::external::query::sql::transformation::queryToPure::tests::testLimitAll():Boolean[1] { - let sqlString = 'SELECT * FROM service."/service/service1" LIMIT ALL'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT * FROM service."/service/service1" LIMIT ALL', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } function <> meta::external::query::sql::transformation::queryToPure::tests::testOffset():Boolean[1] { - let sqlString = 'SELECT * FROM service."/service/service1" OFFSET 1'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT * FROM service."/service/service1" OFFSET 1', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->drop(1) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } function <> meta::external::query::sql::transformation::queryToPure::tests::testLimitOffset():Boolean[1] { - let sqlString = 'SELECT * FROM service."/service/service1" OFFSET 2 LIMIT 1'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT * FROM service."/service/service1" OFFSET 2 LIMIT 1', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->slice(2, 2 + 1) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } //ORDER BY function <> meta::external::query::sql::transformation::queryToPure::tests::testOrderBy():Boolean[1] { - let sqlString = 'SELECT * FROM service."/service/service1" ORDER BY Integer DESC, Boolean ASC'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT * FROM service."/service/service1" ORDER BY Integer DESC, Boolean ASC', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->sort([desc('Integer'), asc('Boolean')]) - }; - - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } function <> meta::external::query::sql::transformation::queryToPure::tests::testOrderByAlias():Boolean[1] { - let sqlString = 'SELECT * FROM service."/service/service1" AS table1 ORDER BY table1.Integer DESC, Boolean'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT * FROM service."/service/service1" AS table1 ORDER BY table1.Integer DESC, Boolean', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->sort([desc('Integer'), asc('Boolean')]) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } function <> meta::external::query::sql::transformation::queryToPure::tests::testOrderByQualified():Boolean[1] { - let sqlString = 'SELECT * FROM service."/service/service1" ORDER BY service."/service/service1".Integer DESC, Boolean ASC'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT * FROM service."/service/service1" ORDER BY service."/service/service1".Integer DESC, Boolean ASC', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->sort([desc('Integer'), asc('Boolean')]) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } function <> meta::external::query::sql::transformation::queryToPure::tests::testOrderByIndex():Boolean[1] { - let sqlString = 'SELECT Integer, String FROM service."/service/service1" ORDER BY 1 DESC, 2 ASC'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT Integer, String FROM service."/service/service1" ORDER BY 1 DESC, 2 ASC', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->restrict(['Integer', 'String']) ->sort([desc('Integer'), asc('String')]) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } //WHERE function <> meta::external::query::sql::transformation::queryToPure::tests::testWhere():Boolean[1] { - let sqlString = 'SELECT * FROM service."/service/service1" WHERE Integer != 2 AND Integer <> 3'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT * FROM service."/service/service1" WHERE Integer != 2 AND Integer <> 3', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->filter(row | ($row.getInteger('Integer') != 2) && ($row.getInteger('Integer') != 3)) - }; - assertLambdaEquals($expected, $sqlTransformContext.lambda()); + }, false) } function <> meta::external::query::sql::transformation::queryToPure::tests::testCompositeWhere():Boolean[1] { - let sqlString = 'SELECT * FROM service."/service/service1" WHERE (Integer = 2 AND String = \'abc\') OR (String = \'def\' AND Integer = 1)'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT * FROM service."/service/service1" WHERE (Integer = 2 AND String = \'abc\') OR (String = \'def\' AND Integer = 1)', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->filter(row | ($row.getInteger('Integer') == 2 && $row.getString('String') == 'abc') || ($row.getString('String') == 'def' && $row.getInteger('Integer') == 1)) - }; - assertLambdaEquals($expected, $sqlTransformContext.lambda()); + }, false) } function <> meta::external::query::sql::transformation::queryToPure::tests::testWhereAliases():Boolean[1] { - let sqlString = 'SELECT * FROM service."/service/service1" AS table1 WHERE Integer = 2 OR table1.Integer = 3'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT * FROM service."/service/service1" AS table1 WHERE Integer = 2 OR table1.Integer = 3', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->filter(row | $row.getInteger('Integer') == 2 || $row.getInteger('Integer') == 3) - }; - assertLambdaEquals($expected, $sqlTransformContext.lambda()); + }, false) } function <> meta::external::query::sql::transformation::queryToPure::tests::testWhereQualified():Boolean[1] { - let sqlString = 'SELECT * FROM service."/service/service1" WHERE service."/service/service1".Integer = 2 OR Integer = 3'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT * FROM service."/service/service1" WHERE service."/service/service1".Integer = 2 OR Integer = 3', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->filter(row | $row.getInteger('Integer') == 2 || $row.getInteger('Integer') == 3) - }; - assertLambdaEquals($expected, $sqlTransformContext.lambda()); + }, false) } function <> meta::external::query::sql::transformation::queryToPure::tests::testInAndNullOperators():Boolean[1] { - let sqlString = 'SELECT * FROM service."/service/service1" WHERE Integer IN (1,2,3) OR Integer IS NULL OR Integer IS NOT NULL'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT * FROM service."/service/service1" WHERE Integer IN (1,2,3) OR Integer IS NULL OR Integer IS NOT NULL', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->filter(row | $row.getInteger('Integer')->in([1,2,3]) || $row.getInteger('Integer')->isEmpty() || $row.getInteger('Integer')->isNotEmpty()) - }; - assertLambdaEquals($expected, $sqlTransformContext.lambda()); + }, false) } function <> meta::external::query::sql::transformation::queryToPure::tests::testComparisonOperatorsNumber():Boolean[1] { - let sqlString = 'SELECT * FROM service."/service/service1" WHERE Integer > 1 OR Integer < 1 OR Integer >= 1 OR Integer <= 1 OR Integer BETWEEN 0 AND 1'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT * FROM service."/service/service1" WHERE Integer > 1 OR Integer < 1 OR Integer >= 1 OR Integer <= 1 OR Integer BETWEEN 0 AND 1', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->filter(row | ($row.getInteger('Integer') > 1) @@ -484,52 +486,52 @@ function <> meta::external::query::sql::transformation::queryToPure:: || ($row.getInteger('Integer') >= 1) || ($row.getInteger('Integer') <= 1) || (($row.getInteger('Integer') >= 0) && ($row.getInteger('Integer') <= 1))) - }; - assertLambdaEquals($expected, $sqlTransformContext.lambda()); + }, false) } function <> meta::external::query::sql::transformation::queryToPure::tests::testComparisonOperatorsString():Boolean[1] { - let sqlString = 'SELECT * FROM service."/service/service1" WHERE String > \'one\' OR String < \'one\' OR String >= \'one\' OR String <= \'one\''; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT * FROM service."/service/service1" WHERE String > \'one\' OR String < \'one\' OR String >= \'one\' OR String <= \'one\'', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->filter(row | ($row.getString('String') > 'one') || ($row.getString('String') < 'one') || ($row.getString('String') >= 'one') || ($row.getString('String') <= 'one')) - }; - assertLambdaEquals($expected, $sqlTransformContext.lambda()); + }, false) } function <> meta::external::query::sql::transformation::queryToPure::tests::testComparisonOperatorsBoolean():Boolean[1] { - let sqlString = 'SELECT * FROM service."/service/service1" WHERE Boolean > true OR Boolean < true OR Boolean >= false OR Boolean <= false'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT * FROM service."/service/service1" WHERE Boolean > true OR Boolean < true OR Boolean >= false OR Boolean <= false', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->filter(row | ($row.getBoolean('Boolean') > true) || ($row.getBoolean('Boolean') < true) || ($row.getBoolean('Boolean') >= false) || ($row.getBoolean('Boolean') <= false)) - }; - assertLambdaEquals($expected, $sqlTransformContext.lambda()); + }, false) } function <> meta::external::query::sql::transformation::queryToPure::tests::testComparisonOperatorsDate():Boolean[1] { - let sqlString = 'SELECT * FROM service."/service/service1" WHERE StrictDate > TIMESTAMP \'2023-01-01\' OR StrictDate < CURRENT_DATE OR StrictDate >= TIMESTAMP \'2023-01-02\' OR StrictDate <= CURRENT_DATE OR StrictDate > \'2023-01-03\''; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT * FROM service."/service/service1" WHERE StrictDate > TIMESTAMP \'2023-01-01\' OR StrictDate < CURRENT_DATE OR StrictDate >= TIMESTAMP \'2023-01-02\' OR StrictDate <= CURRENT_DATE OR StrictDate > \'2023-01-03\'', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->filter(row | ($row.getStrictDate('StrictDate') > parseDate('2023-01-01')) || ($row.getStrictDate('StrictDate') < today()) || ($row.getStrictDate('StrictDate') >= parseDate('2023-01-02')) || ($row.getStrictDate('StrictDate') <= today()) || ($row.getStrictDate('StrictDate') > parseDate('2023-01-03'))) - }; - assertLambdaEquals($expected, $sqlTransformContext.lambda()); + }, false) } function <> meta::external::query::sql::transformation::queryToPure::tests::testComparisonOperatorsEnum():Boolean[1] { - let sqlString = 'SELECT * FROM service."/service/service3" WHERE "The Enum Value" > \'Value2\' or "The Enum Value" < \'Value_3\' or "The Enum Value" >= \'Value2\' or "The Enum Value" <= \'Value_3\' or "The Enum Value" = \'Value1\' or "The Enum Value" != \'Value1\''; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| + test( + 'SELECT * FROM service."/service/service3" WHERE "The Enum Value" > \'Value2\' or "The Enum Value" < \'Value_3\' or "The Enum Value" >= \'Value2\' or "The Enum Value" <= \'Value_3\' or "The Enum Value" = \'Value1\' or "The Enum Value" != \'Value1\'', + + {| let const = 123; FlatInput.all()->project( [ @@ -545,15 +547,15 @@ function <> meta::external::query::sql::transformation::queryToPure:: || $row.getEnum('The Enum Value') == MyEnum.Value1 || $row.getEnum('The Enum Value') != MyEnum.Value1 ) - ;}; - assertLambdaEquals($expected, $sqlTransformContext.lambda()); + ;}, false) } function <> meta::external::query::sql::transformation::queryToPure::tests::testDistinctFromOperator():Boolean[1] { - let sqlString = 'SELECT * FROM service."/service/service1" WHERE String IS DISTINCT FROM \'ABC\' OR String IS NOT DISTINCT FROM \'DEF\''; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT * FROM service."/service/service1" WHERE String IS DISTINCT FROM \'ABC\' OR String IS NOT DISTINCT FROM \'DEF\'', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->filter(row | @@ -561,100 +563,99 @@ function <> meta::external::query::sql::transformation::queryToPure:: || (($row.getString('String')->isEmpty() && 'DEF'->isEmpty()) || ($row.getString('String') == 'DEF')) ) - }; - assertLambdaEquals($expected, $sqlTransformContext.lambda()); + }, false) } function <> meta::external::query::sql::transformation::queryToPure::tests::testWhereCombined():Boolean[1] { - let sqlString = 'SELECT String, sum(Float) AS "float" FROM service."/service/service1" WHERE Integer = 1 GROUP BY 1'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT String, sum(Float) AS "float" FROM service."/service/service1" WHERE Integer = 1 GROUP BY 1', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->filter(row | ($row.getInteger('Integer') == 1)) ->groupBy('String', agg('float', row | $row.getFloat('Float'), y | $y->sum())) - }; - assertLambdaEquals($expected, $sqlTransformContext.lambda()); + }, false) } //GROUP BY function <> meta::external::query::sql::transformation::queryToPure::tests::testGroupBy():Boolean[1] { - let sqlString = 'SELECT count(Integer) AS "count" FROM service."/service/service1" GROUP BY String'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT count(Integer) AS "count" FROM service."/service/service1" GROUP BY String', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ] )->groupBy('String', agg('count', row | $row.getInteger('Integer'), y | $y->count())) ->restrict('count') - }; - assertLambdaEquals($expected, $sqlTransformContext.lambda()); + }, false) } function <> meta::external::query::sql::transformation::queryToPure::tests::testGroupByNoAggregates():Boolean[1] { - let sqlString = 'SELECT String FROM service."/service/service1" GROUP BY 1'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT String FROM service."/service/service1" GROUP BY 1', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ] )->restrict('String')->distinct() - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } function <> meta::external::query::sql::transformation::queryToPure::tests::testGroupByIndex():Boolean[1] { - let sqlString = 'SELECT String, sum(Integer) AS "sum" FROM service."/service/service1" GROUP BY 1'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT String, sum(Integer) AS "sum" FROM service."/service/service1" GROUP BY 1', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ] )->groupBy(['String'], agg('sum', row | $row.getInteger('Integer'), y | $y->sum())) - }; - assertLambdaEquals($expected, $sqlTransformContext.lambda()); + }, false) } function <> meta::external::query::sql::transformation::queryToPure::tests::testGroupByIndexMultipleWithAlias():Boolean[1] { - let sqlString = 'SELECT String, sum(Integer) AS "sum", Boolean AS "bool" FROM service."/service/service1" GROUP BY 1, 3'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT String, sum(Integer) AS "sum", Boolean AS "bool" FROM service."/service/service1" GROUP BY 1, 3', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ] )->renameColumns(pair('Boolean', 'bool')) ->groupBy(['String', 'bool'], agg('sum', row | $row.getInteger('Integer'), y | $y->sum())) ->restrict(['String', 'sum', 'bool']) - }; - assertLambdaEquals($expected, $sqlTransformContext.lambda()); + }, false) } function <> meta::external::query::sql::transformation::queryToPure::tests::testGroupByWithAlias():Boolean[1] { - let sqlString = 'SELECT String, sum(Integer) AS "sum", Boolean AS "bool" FROM service."/service/service1" GROUP BY String, "bool"'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT String, sum(Integer) AS "sum", Boolean AS "bool" FROM service."/service/service1" GROUP BY String, "bool"', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ] )->renameColumns(pair('Boolean', 'bool')) ->groupBy(['String', 'bool'], agg('sum', row | $row.getInteger('Integer'), y | $y->sum())) ->restrict(['String', 'sum', 'bool']) - }; - assertLambdaEquals($expected, $sqlTransformContext.lambda()); + }, false) } function <> meta::external::query::sql::transformation::queryToPure::tests::testGroupByFunctions():Boolean[1] { - let sqlString = 'SELECT count(Integer) AS "count", count(DISTINCT Integer) AS "distinctCount", SUM(Integer) AS "sum", avg(Integer) AS "avg", ' + + test('SELECT count(Integer) AS "count", count(DISTINCT Integer) AS "distinctCount", SUM(Integer) AS "sum", avg(Integer) AS "avg", ' + 'stddev_pop(Integer) AS "stdDevPop", stddev_samp(Integer) AS "stdDevSamp", stddev(Integer) AS "stdDev", ' + 'var_pop(Integer) AS "variancePop", var_samp(Integer) AS "varianceSamp", variance(Integer) AS "variance", ' + 'min(Integer) AS "minInt", min(StrictDate) AS "minDate", min(String) AS "minString", ' + 'max(Integer) AS "maxInt", max(StrictDate) AS "maxDate", max(String) AS "maxString", ' + - 'string_agg(String) AS "stringAgg", string_agg(cast(Integer AS VARCHAR), \' \') AS "stringAgg2" FROM service."/service/service1" GROUP BY String'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + 'string_agg(String) AS "stringAgg", string_agg(cast(Integer AS VARCHAR), \' \') AS "stringAgg2" FROM service."/service/service1" GROUP BY String', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ] )->groupBy('String', [ @@ -677,15 +678,15 @@ function <> meta::external::query::sql::transformation::queryToPure:: agg('stringAgg', row | $row.getString('String'), y | $y->joinStrings()), agg('stringAgg2', row | $row.getInteger('Integer')->toString(), y | $y->joinStrings(' ')) ])->restrict(['count', 'distinctCount', 'sum', 'avg', 'stdDevPop', 'stdDevSamp', 'stdDev', 'variancePop', 'varianceSamp', 'variance', 'minInt', 'minDate', 'minString', 'maxInt', 'maxDate', 'maxString', 'stringAgg', 'stringAgg2']) - }; - assertLambdaEquals($expected, $sqlTransformContext.lambda()); + }, false) } function <> meta::external::query::sql::transformation::queryToPure::tests::testGroupByAggregateExpressions():Boolean[1] { - let sqlString = 'SELECT Integer, sum(1) AS "sum", count(*) AS "count", sum(Integer + Float) AS "exp", EXTRACT(YEAR FROM MAX("StrictDate")) AS "aggFunctionCall", MAX("StrictDate") + INTERVAL \'1 WEEK\' AS "dateAggMath" FROM service."/service/service1" GROUP BY Integer'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT Integer, sum(1) AS "sum", count(*) AS "count", sum(Integer + Float) AS "exp", EXTRACT(YEAR FROM MAX("StrictDate")) AS "aggFunctionCall", MAX("StrictDate") + INTERVAL \'1 WEEK\' AS "dateAggMath" FROM service."/service/service1" GROUP BY Integer', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ] )->groupBy(['Integer'], [ @@ -695,16 +696,15 @@ function <> meta::external::query::sql::transformation::queryToPure:: agg('aggFunctionCall', row | $row.getStrictDate('StrictDate'), y | $y->max()->toOne()->year()), agg('dateAggMath', row | $row.getStrictDate('StrictDate'), y | $y->max()->toOne()->adjust(1, DurationUnit.WEEKS)) ]) - }; - - assertLambdaEquals($expected, $sqlTransformContext.lambda()); + }, false) } function <> meta::external::query::sql::transformation::queryToPure::tests::testGroupByMixed():Boolean[1] { - let sqlString = 'SELECT sum(Integer) AS "SUM", CASE WHEN String = \'abc\' THEN 1 WHEN String = \'def\' THEN 2 ELSE 3 END AS "Number" FROM service."/service/service1" GROUP BY 2'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT sum(Integer) AS "SUM", CASE WHEN String = \'abc\' THEN 1 WHEN String = \'def\' THEN 2 ELSE 3 END AS "Number" FROM service."/service/service1" GROUP BY 2', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ] )->extend( @@ -712,32 +712,31 @@ function <> meta::external::query::sql::transformation::queryToPure:: )->groupBy(['Number'], [ agg('SUM', row | $row.getInteger('Integer'), y | $y->sum()) ])->restrict(['SUM', 'Number']) - }; - assertLambdaEquals($expected, $sqlTransformContext.lambda()); + }, false) } function <> meta::external::query::sql::transformation::queryToPure::tests::testGroupByAggWithinCase():Boolean[1] { - let sqlString = 'SELECT CASE WHEN(SUM("Integer") <10) THEN max("Integer") ELSE min("Integer") END AS "MIN/MAX", ' + - 'CASE WHEN(SUM("Integer") < 10) THEN \'LOW\' ELSE \'HIGH\' END AS "HIGH/LOW" FROM service."/service/service1"'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test('SELECT CASE WHEN(SUM("Integer") <10) THEN max("Integer") ELSE min("Integer") END AS "MIN/MAX", ' + + 'CASE WHEN(SUM("Integer") < 10) THEN \'LOW\' ELSE \'HIGH\' END AS "HIGH/LOW" FROM service."/service/service1"', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ] )->groupBy([], [ agg('MIN/MAX', row | $row.getInteger('Integer'), y | if ($y->sum() < 10, | max($y), | min($y))), agg('HIGH/LOW', row | $row.getInteger('Integer'), y | if ($y->sum() < 10, | 'LOW', | 'HIGH')) ]) - }; - assertLambdaEquals($expected, $sqlTransformContext.lambda()); + }, false) } //HAVING function <> meta::external::query::sql::transformation::queryToPure::tests::testHaving():Boolean[1] { - let sqlString = 'SELECT count(Integer) AS "count", max(Integer) FROM service."/service/service1" GROUP BY String HAVING count(Integer) > 0 AND count(1) > 0 AND floor(max(Integer)) > 0'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT count(Integer) AS "count", max(Integer) FROM service."/service/service1" GROUP BY String HAVING count(Integer) > 0 AND count(1) > 0 AND floor(max(Integer)) > 0', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ] )->groupBy('String', [ @@ -747,15 +746,14 @@ function <> meta::external::query::sql::transformation::queryToPure:: ])->filter(row | ($row.getInteger('count') > 0) && ($row.getInteger('count(1)') > 0) && (floor($row.getInteger('max(Integer)')) > 0)) ->restrict(['count', 'max(Integer)']) - }; - assertLambdaEquals($expected, $sqlTransformContext.lambda()); + }, false) } function <> meta::external::query::sql::transformation::queryToPure::tests::testHavingNoGroupby():Boolean[1] { - let sqlString = 'SELECT \'abc\', pi() AS "pi" FROM service."/service/service1" HAVING count(1) > 0 AND count(1) < 1000'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test('SELECT \'abc\', pi() AS "pi" FROM service."/service/service1" HAVING count(1) > 0 AND count(1) < 1000', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ] )->extend([ @@ -767,15 +765,15 @@ function <> meta::external::query::sql::transformation::queryToPure:: ])->filter(row | ($row.getInteger('count(1)') > 0) && ($row.getInteger('count(1)') < 1000)) ->restrict(['abc', 'pi']) - }; - assertLambdaEquals($expected, $sqlTransformContext.lambda()); + }, false) } function <> meta::external::query::sql::transformation::queryToPure::tests::testHavingWithJoinAndColumnAlias():Boolean[1] { - let sqlString = 'SELECT table1.*, table2.String as "str" FROM service(\'/service/service1\') "table1" LEFT JOIN service(\'/service/service1\') "table2" USING (String) HAVING (COUNT(1) > 0)'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT table1.*, table2.String as "str" FROM service(\'/service/service1\') "table1" LEFT JOIN service(\'/service/service1\') "table2" USING (String) HAVING (COUNT(1) > 0)', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->renameColumns([ @@ -816,36 +814,35 @@ function <> meta::external::query::sql::transformation::queryToPure:: pair('String_table1', 'String') ]) ->restrict(['Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String', 'str']) - }; - assertLambdaEquals($expected, $sqlTransformContext.lambda()); + }, false) } function <> meta::external::query::sql::transformation::queryToPure::tests::testHavingSelectStar():Boolean[1] { - let sqlString = 'SELECT * FROM service(\'/service/service1\') HAVING (COUNT(1) > 0)'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT * FROM service(\'/service/service1\') HAVING (COUNT(1) > 0)', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->groupBy(['Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String'], agg('COUNT(1)', row | 1, y | $y->count()))->filter(row | $row.getInteger('COUNT(1)') > 0) ->restrict([ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) - }; - assertLambdaEquals($expected, $sqlTransformContext.lambda()); + }, false) } //CAST function <> meta::external::query::sql::transformation::queryToPure::tests::testCasts():Boolean[1] { - let sqlString = 'SELECT' - + ' CAST(\'2023-01-01\' AS DATE) AS "constant", CAST(String AS VARCHAR) AS "string", CAST(String AS TEXT) AS "text", CAST(String AS DATE) AS "date",' - + ' CAST(String AS INTEGER) AS "integer", CAST(String AS BIGINT) AS "bigint", CAST(String AS SMALLINT) AS "smallint", CAST(String AS BOOLEAN) AS "boolean", ' - + ' CAST(String AS DOUBLE PRECISION) AS "double", CAST(String AS NUMERIC) AS "numeric",' - + ' CAST(String AS TIMESTAMP) AS "timestamp", CAST(Integer AS TEXT) AS "integerText", CAST(Integer AS VARCHAR) AS "integerString", CAST(Integer AS Integer) AS "expression",' - + ' CAST(String AS VARCHAR(2)) AS "stringChars", CAST(Integer AS VARCHAR(2)) AS "integerStringChars",' - + ' CAST(Float AS NUMERIC) AS "floatNumeric", CAST(Decimal AS NUMERIC) AS "decimalNumeric", CAST(Decimal AS DOUBLE PRECISION) AS "decimalDoublePrecision",' - + ' CAST(Float AS DOUBLE PRECISION) AS "floatDoublePrecision", CAST(String AS NUMERIC(4, 2)) AS "numericParams" FROM service."/service/service1"'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test('SELECT' + + ' CAST(\'2023-01-01\' AS DATE) AS "constant", CAST(String AS VARCHAR) AS "string", CAST(String AS TEXT) AS "text", CAST(String AS DATE) AS "date",' + + ' CAST(String AS INTEGER) AS "integer", CAST(String AS BIGINT) AS "bigint", CAST(String AS SMALLINT) AS "smallint", CAST(String AS BOOLEAN) AS "boolean", ' + + ' CAST(String AS DOUBLE PRECISION) AS "double", CAST(String AS NUMERIC) AS "numeric",' + + ' CAST(String AS TIMESTAMP) AS "timestamp", CAST(Integer AS TEXT) AS "integerText", CAST(Integer AS VARCHAR) AS "integerString", CAST(Integer AS Integer) AS "expression",' + + ' CAST(String AS VARCHAR(2)) AS "stringChars", CAST(Integer AS VARCHAR(2)) AS "integerStringChars",' + + ' CAST(Float AS NUMERIC) AS "floatNumeric", CAST(Decimal AS NUMERIC) AS "decimalNumeric", CAST(Decimal AS DOUBLE PRECISION) AS "decimalDoublePrecision",' + + ' CAST(Float AS DOUBLE PRECISION) AS "floatDoublePrecision", CAST(String AS NUMERIC(4, 2)) AS "numericParams" FROM service."/service/service1"', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ] )->project([ @@ -871,17 +868,17 @@ function <> meta::external::query::sql::transformation::queryToPure:: col(row:TDSRow[1] | $row.getFloat('Float'), 'floatDoublePrecision'), col(row:TDSRow[1] | round(parseDecimal($row.getString('String')), 2), 'numericParams') ]) - }; - - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } //CAST function <> meta::external::query::sql::transformation::queryToPure::tests::testDateCasts():Boolean[1] { - let sqlString = 'SELECT CAST(\'2023-01-01\' AS DATE) AS "constantDate", CAST(String AS DATE) AS "date", CAST(\'2023-01-01 10:01:01\' AS TIMESTAMP) AS "constantTimestamp", CAST(\'2023-01-01 10:01:01.12\' AS TIMESTAMP) AS "constantTimestampMillis", CAST(String AS TIMESTAMP) AS "timestamp" FROM service."/service/service1"'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT CAST(\'2023-01-01\' AS DATE) AS "constantDate", CAST(String AS DATE) AS "date", CAST(\'2023-01-01 10:01:01\' AS TIMESTAMP) AS "constantTimestamp", ' + + 'CAST(\'2023-01-01 10:01:01.12\' AS TIMESTAMP) AS "constantTimestampMillis", CAST(String AS TIMESTAMP) AS "timestamp" FROM service."/service/service1"', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ] )->project([ @@ -891,17 +888,15 @@ function <> meta::external::query::sql::transformation::queryToPure:: col(row:TDSRow[1] | parseDate('2023-01-01T10:01:01.12'), 'constantTimestampMillis'), col(row:TDSRow[1] | parseDate($row.getString('String')), 'timestamp') ]) - }; - - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } function <> meta::external::query::sql::transformation::queryToPure::tests::testNullCasts():Boolean[1] { - let sqlString = 'SELECT CAST(NULL AS VARCHAR) AS "string", CAST(NULL AS TEXT) AS "text", CAST(NULL AS DATE) AS "date",' - + ' CAST(NULL AS INTEGER) AS "integer", CAST(NULL AS BOOLEAN) AS "boolean", CAST(NULL AS DOUBLE PRECISION) AS "double", CAST(NULL AS NUMERIC) AS "numeric", CAST(NULL AS TIMESTAMP) AS "timestamp" FROM service."/service/service1"'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test('SELECT CAST(NULL AS VARCHAR) AS "string", CAST(NULL AS TEXT) AS "text", CAST(NULL AS DATE) AS "date", ' + + 'CAST(NULL AS INTEGER) AS "integer", CAST(NULL AS BOOLEAN) AS "boolean", CAST(NULL AS DOUBLE PRECISION) AS "double", CAST(NULL AS NUMERIC) AS "numeric", CAST(NULL AS TIMESTAMP) AS "timestamp" FROM service."/service/service1"', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ] )->project([ @@ -914,17 +909,17 @@ function <> meta::external::query::sql::transformation::queryToPure:: col(row:TDSRow[1] | cast([], @Decimal), 'numeric'), col(row:TDSRow[1] | cast([], @DateTime), 'timestamp') ]) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } //CASE function <> meta::external::query::sql::transformation::queryToPure::tests::testMultiSearchedCaseWhen():Boolean[1] { - let sqlString = 'SELECT CASE WHEN String = \'abc\' THEN 1 WHEN String = \'def\' THEN NULL ELSE 3 END AS "Number" FROM service."/service/service1"'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT CASE WHEN String = \'abc\' THEN 1 WHEN String = \'def\' THEN NULL ELSE 3 END AS "Number" FROM service."/service/service1"', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->project( @@ -932,15 +927,15 @@ function <> meta::external::query::sql::transformation::queryToPure:: col(row:TDSRow[1] | if ($row.getString('String') == 'abc', | 1, | if ($row.getString('String') == 'def', | [], | 3)), 'Number') ] ) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } function <> meta::external::query::sql::transformation::queryToPure::tests::testCaseWhen():Boolean[1] { - let sqlString = 'SELECT CASE String WHEN \'abc\' THEN 1 WHEN \'def\' THEN 2 ELSE 3 END AS "Number" FROM service."/service/service1"'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + test( + 'SELECT CASE String WHEN \'abc\' THEN 1 WHEN \'def\' THEN 2 ELSE 3 END AS "Number" FROM service."/service/service1"', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->project( @@ -948,18 +943,17 @@ function <> meta::external::query::sql::transformation::queryToPure:: col(row:TDSRow[1] | if ($row.getString('String') == 'abc', | 1, | if ($row.getString('String') == 'def', | 2, | 3)), 'Number') ] ) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } ///ARITHMETIC function <> meta::external::query::sql::transformation::queryToPure::tests::testArithmetic():Boolean[1] { - let sqlString = 'SELECT 1 + 2 AS "plus", 1 - 2 AS "minus", 1 * 2 AS "multiply", 1 / 2 AS "divide", 1 % 2 AS "mod", 1 ^ 2 AS "pow", Integer + Float AS "column plus" FROM service."/service/service1"'; + test( + 'SELECT 1 + 2 AS "plus", 1 - 2 AS "minus", 1 * 2 AS "multiply", 1 / 2 AS "divide", 1 % 2 AS "mod", 1 ^ 2 AS "pow", Integer + Float AS "column plus" FROM service."/service/service1"', - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->project( @@ -973,16 +967,15 @@ function <> meta::external::query::sql::transformation::queryToPure:: col(row:TDSRow[1] | $row.getInteger('Integer') + $row.getFloat('Float'), 'column plus') ] ) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } function <> meta::external::query::sql::transformation::queryToPure::tests::testArithmeticNullable():Boolean[1] { - let sqlString = 'SELECT 1 + NULL AS "plus", 1 - NULL AS "minus", 1 * NULL AS "multiply", 1 / NULL AS "divide", 1 % NULL AS "mod", 1 ^ NULL AS "pow" FROM service."/service/service1"'; + test( + 'SELECT 1 + NULL AS "plus", 1 - NULL AS "minus", 1 * NULL AS "multiply", 1 / NULL AS "divide", 1 % NULL AS "mod", 1 ^ NULL AS "pow" FROM service."/service/service1"', - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ]) ->project( @@ -995,17 +988,16 @@ function <> meta::external::query::sql::transformation::queryToPure:: col(row:TDSRow[1] | []->cast(@Number), 'pow') ] ) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } //JOIN function <> meta::external::query::sql::transformation::queryToPure::tests::testLeftJoinSubqueryOn():Boolean[1] { - let sqlString = 'SELECT table1.Integer FROM service."/service/service1" "table1" LEFT OUTER JOIN (SELECT Integer FROM service."/service/service2") AS "table2" ON "table1"."Integer" = "table2"."Integer"'; + test( + 'SELECT table1.Integer FROM service."/service/service1" "table1" LEFT OUTER JOIN (SELECT Integer FROM service."/service/service2") AS "table2" ON "table1"."Integer" = "table2"."Integer"', - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ])->renameColumns([ pair('Boolean', 'Boolean_table1'), @@ -1024,17 +1016,15 @@ function <> meta::external::query::sql::transformation::queryToPure:: pair('Integer', 'Integer_table2') ]), meta::relational::metamodel::join::JoinType.LEFT_OUTER, {row1:TDSRow[1], row2:TDSRow[1] | $row1.getInteger('Integer_table1') == $row2.getInteger('Integer_table2')} )->restrict('Integer_table1')->renameColumns(pair('Integer_table1', 'Integer')) - }; - - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } function <> meta::external::query::sql::transformation::queryToPure::tests::testLeftJoinSubqueryUsing():Boolean[1] { - let sqlString = 'SELECT table1.Integer AS "Int", table2.String AS "Str" FROM service."/service/service1" AS table1 LEFT OUTER JOIN (SELECT Integer, String FROM service."/service/service1") AS table2 USING (Integer)'; + test( + 'SELECT table1.Integer AS "Int", table2.String AS "Str" FROM service."/service/service1" AS table1 LEFT OUTER JOIN (SELECT Integer, String FROM service."/service/service1") AS table2 USING (Integer)', - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ])->renameColumns([ pair('Boolean', 'Boolean_table1'), @@ -1059,16 +1049,15 @@ function <> meta::external::query::sql::transformation::queryToPure:: pair('Integer_table1', 'Int'), pair('String_table2', 'Str') ]) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }); } function <> meta::external::query::sql::transformation::queryToPure::tests::testCrossJoin():Boolean[1] { - let sqlString = 'SELECT table1.Integer AS "Int", table2.String AS "Str" FROM service."/service/service1" table1 CROSS JOIN service."/service/service2" table2'; + test( + 'SELECT table1.Integer AS "Int", table2.String AS "Str" FROM service."/service/service1" table1 CROSS JOIN service."/service/service2" table2', - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ])->renameColumns([ pair('Boolean', 'Boolean_table1'), @@ -1090,17 +1079,15 @@ function <> meta::external::query::sql::transformation::queryToPure:: pair('Integer_table1', 'Int'), pair('String_table2', 'Str') ]) - }; - - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } function <> meta::external::query::sql::transformation::queryToPure::tests::testImplicitJoin():Boolean[1] { - let sqlString = 'SELECT table1.Integer AS "Int", table2.String AS "Str" FROM service."/service/service1" table1, service."/service/service2" table2 where table1.Integer = table2.Integer'; + test( + 'SELECT table1.Integer AS "Int", table2.String AS "Str" FROM service."/service/service1" table1, service."/service/service2" table2 where table1.Integer = table2.Integer', - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ])->renameColumns([ pair('Boolean', 'Boolean_table1'), @@ -1123,17 +1110,15 @@ function <> meta::external::query::sql::transformation::queryToPure:: pair('Integer_table1', 'Int'), pair('String_table2', 'Str') ]) - }; - - assertLambdaEquals($expected, $sqlTransformContext.lambda()); + }, false) } function <> meta::external::query::sql::transformation::queryToPure::tests::testLeftJoinRelationOn():Boolean[1] { - let sqlString = 'SELECT table1.Integer AS Integer, table2.String AS String FROM service."/service/service1" AS table1 LEFT OUTER JOIN service."/service/service2" AS table2 ON (table1.Integer = table2.Integer)'; + test( + 'SELECT table1.Integer AS Integer, table2.String AS String FROM service."/service/service1" AS table1 LEFT OUTER JOIN service."/service/service2" AS table2 ON (table1.Integer = table2.Integer)', - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| FlatInput.all()->project( + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ])->renameColumns([ pair('Boolean', 'Boolean_table1'), @@ -1156,67 +1141,61 @@ function <> meta::external::query::sql::transformation::queryToPure:: pair('Integer_table1', 'Integer'), pair('String_table2', 'String') ]) - }; - - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } function <> meta::external::query::sql::transformation::queryToPure::tests::testMultiJoin():Boolean[1] { - let sqlString = 'SELECT * FROM (SELECT * FROM service(\'/service/service1\')) "table1" LEFT JOIN (SELECT * FROM service(\'/service/service2\')) "table2" ON ("table1"."String" = "table2"."ID") LEFT JOIN (SELECT * FROM service(\'/service/service2\')) "table3" ON ("table1"."String" = "table3"."String") WHERE "table2"."String" = \'ABC\''; + test( + 'SELECT * FROM (SELECT * FROM service(\'/service/service1\')) "table1" LEFT JOIN (SELECT * FROM service(\'/service/service2\')) "table2" ON ("table1"."String" = "table2"."ID") ' + + 'LEFT JOIN (SELECT * FROM service(\'/service/service2\')) "table3" ON ("table1"."String" = "table3"."String") WHERE "table2"."String" = \'ABC\'', - let sqlTransformContext = $sqlString->processQuery(); - - let expected = {|FlatInput.all() - ->project([{x|$x.booleanIn}, {x|$x.integerIn}, {x|$x.floatIn}, {x|$x.decimalIn}, {x|$x.strictDateIn}, {x|$x.dateTimeIn}, {x|$x.stringIn}], ['Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String']) - ->renameColumns([ - pair('Boolean', 'Boolean_table1'), - pair('Integer', 'Integer_table1'), - pair('Float', 'Float_table1'), - pair('Decimal', 'Decimal_table1'), - pair('StrictDate', 'StrictDate_table1'), - pair('DateTime', 'DateTime_table1'), - pair('String', 'String_table1') - ]) - ->join(FlatInput.all() - ->project([{x|$x.idIn}, {x|$x.integerIn}, {x|$x.stringIn}], ['ID', 'Integer', 'String']) - ->renameColumns([ - pair('ID', 'ID_table2'), - pair('Integer', 'Integer_table2'), - pair('String', 'String_table2') - ]), - meta::relational::metamodel::join::JoinType.LEFT_OUTER, - {row1, row2|$row1.getString('String_table1') == $row2.getInteger('ID_table2')}) - ->join(meta::external::query::sql::transformation::queryToPure::tests::FlatInput.all() - ->project([{x|$x.idIn}, {x|$x.integerIn}, {x|$x.stringIn}], ['ID', 'Integer', 'String']) - ->renameColumns([ - pair('ID', 'ID_table3'), - pair('Integer', 'Integer_table3'), - pair('String', 'String_table3') - ]), - meta::relational::metamodel::join::JoinType.LEFT_OUTER, - {row1, row2|$row1.getString('String_table1') == $row2.getString('String_table3')}) - ->filter({row|$row.getString('String_table2') == 'ABC'}) - ->renameColumns([ - pair('Boolean_table1', 'Boolean'), - pair('Float_table1', 'Float'), - pair('Decimal_table1', 'Decimal'), - pair('StrictDate_table1', 'StrictDate'), - pair('DateTime_table1', 'DateTime') - ]) - }; - - assertLambdaEquals($expected, $sqlTransformContext.lambda()); + {|FlatInput.all() + ->project([{x|$x.booleanIn}, {x|$x.integerIn}, {x|$x.floatIn}, {x|$x.decimalIn}, {x|$x.strictDateIn}, {x|$x.dateTimeIn}, {x|$x.stringIn}], ['Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String']) + ->renameColumns([ + pair('Boolean', 'Boolean_table1'), + pair('Integer', 'Integer_table1'), + pair('Float', 'Float_table1'), + pair('Decimal', 'Decimal_table1'), + pair('StrictDate', 'StrictDate_table1'), + pair('DateTime', 'DateTime_table1'), + pair('String', 'String_table1') + ]) + ->join(FlatInput.all() + ->project([{x|$x.idIn}, {x|$x.integerIn}, {x|$x.stringIn}], ['ID', 'Integer', 'String']) + ->renameColumns([ + pair('ID', 'ID_table2'), + pair('Integer', 'Integer_table2'), + pair('String', 'String_table2') + ]), + meta::relational::metamodel::join::JoinType.LEFT_OUTER, + {row1, row2|$row1.getString('String_table1') == $row2.getInteger('ID_table2')}) + ->join(meta::external::query::sql::transformation::queryToPure::tests::FlatInput.all() + ->project([{x|$x.idIn}, {x|$x.integerIn}, {x|$x.stringIn}], ['ID', 'Integer', 'String']) + ->renameColumns([ + pair('ID', 'ID_table3'), + pair('Integer', 'Integer_table3'), + pair('String', 'String_table3') + ]), + meta::relational::metamodel::join::JoinType.LEFT_OUTER, + {row1, row2|$row1.getString('String_table1') == $row2.getString('String_table3')}) + ->filter({row|$row.getString('String_table2') == 'ABC'}) + ->renameColumns([ + pair('Boolean_table1', 'Boolean'), + pair('Float_table1', 'Float'), + pair('Decimal_table1', 'Decimal'), + pair('StrictDate_table1', 'StrictDate'), + pair('DateTime_table1', 'DateTime') + ])}, false) } //UNION function <> meta::external::query::sql::transformation::queryToPure::tests::testUnion():Boolean[1] { - let sqlString = 'SELECT Integer FROM service."/service/service1" UNION SELECT Integer FROM service."/service/service2"'; + test('SELECT Integer FROM service."/service/service1" UNION SELECT Integer FROM service."/service/service2"', - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| + {| FlatInput.all() ->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], @@ -1229,17 +1208,16 @@ function <> meta::external::query::sql::transformation::queryToPure:: [ 'ID', 'Integer', 'String' ]) ->restrict('Integer') ) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } //CURRENT TIME function <> meta::external::query::sql::transformation::queryToPure::tests::testCurrentTime():Boolean[1] { - let sqlString = 'SELECT CURRENT_TIME, CURRENT_TIMESTAMP, CURRENT_DATE, CURRENT_TIME AS time, CURRENT_TIMESTAMP AS timestamp, CURRENT_DATE AS date FROM service."/service/service1"'; + test( + 'SELECT CURRENT_TIME, CURRENT_TIMESTAMP, CURRENT_DATE, CURRENT_TIME AS time, CURRENT_TIMESTAMP AS timestamp, CURRENT_DATE AS date FROM service."/service/service1"', - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| + {| FlatInput.all() ->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], @@ -1252,18 +1230,17 @@ function <> meta::external::query::sql::transformation::queryToPure:: col(row:TDSRow[1] | now(), 'timestamp'), col(row:TDSRow[1] | today(), 'date') ]) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } //DATE_TRUNC function <> meta::external::query::sql::transformation::queryToPure::tests::testDateTrunc():Boolean[1] { - let sqlString = 'SELECT date_trunc(\'year\', StrictDate) AS "YEAR", date_trunc(\'quarter\', CAST(\'2023-01-01\' AS DATE)) AS "QUARTER", date_trunc(\'month\', StrictDate) AS "MONTH", date_trunc(\'week\', StrictDate) AS "WEEK", ' + - 'date_trunc(\'day\', StrictDate) AS "DAY", date_trunc(\'hour\', StrictDate) AS "HOUR", date_trunc(\'minute\', StrictDate) AS "MINUTE", date_trunc(\'second\', StrictDate) AS "SECOND" FROM service."/service/service1"'; + test( + 'SELECT date_trunc(\'year\', StrictDate) AS "YEAR", date_trunc(\'quarter\', CAST(\'2023-01-01\' AS DATE)) AS "QUARTER", date_trunc(\'month\', StrictDate) AS "MONTH", date_trunc(\'week\', StrictDate) AS "WEEK", ' + + 'date_trunc(\'day\', StrictDate) AS "DAY", date_trunc(\'hour\', StrictDate) AS "HOUR", date_trunc(\'minute\', StrictDate) AS "MINUTE", date_trunc(\'second\', StrictDate) AS "SECOND" FROM service."/service/service1"', - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| + {| FlatInput.all() ->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], @@ -1278,19 +1255,17 @@ function <> meta::external::query::sql::transformation::queryToPure:: col(row:TDSRow[1] | firstSecondOfMinute($row.getStrictDate('StrictDate')), 'MINUTE'), col(row:TDSRow[1] | firstMillisecondOfSecond($row.getStrictDate('StrictDate')), 'SECOND') ]) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } //DATE_PART function <> meta::external::query::sql::transformation::queryToPure::tests::testDatePart():Boolean[1] { - let sqlString = 'SELECT date_part(\'year\', StrictDate) AS "YEAR", date_part(\'quarter\', CAST(\'2023-01-01\' AS DATE)) AS "QUARTER", date_part(\'month\', StrictDate) AS "MONTH", date_part(\'week\', StrictDate) AS "WEEK", ' + + test('SELECT date_part(\'year\', StrictDate) AS "YEAR", date_part(\'quarter\', CAST(\'2023-01-01\' AS DATE)) AS "QUARTER", date_part(\'month\', StrictDate) AS "MONTH", date_part(\'week\', StrictDate) AS "WEEK", ' + 'date_part(\'dow\', StrictDate) AS "DOW", date_part(\'day\', StrictDate) AS "DAY", date_part(\'doy\', StrictDate) AS "DOY", date_part(\'hour\', StrictDate) AS "HOUR", date_part(\'minute\', StrictDate) AS "MINUTE", ' + - 'date_part(\'second\', StrictDate) AS "SECOND", date_part(\'epoch\', StrictDate) AS "EPOCH" FROM service."/service/service1"'; + 'date_part(\'second\', StrictDate) AS "SECOND", date_part(\'epoch\', StrictDate) AS "EPOCH" FROM service."/service/service1"', - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| + {| FlatInput.all() ->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], @@ -1308,19 +1283,18 @@ function <> meta::external::query::sql::transformation::queryToPure:: col(row:TDSRow[1] | second($row.getStrictDate('StrictDate')), 'SECOND'), col(row:TDSRow[1] | toEpochValue($row.getStrictDate('StrictDate')), 'EPOCH') ]) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } //EXTRACT function <> meta::external::query::sql::transformation::queryToPure::tests::testExtract():Boolean[1] { - let sqlString = 'SELECT EXTRACT(\'year\' FROM StrictDate) AS "YEAR", EXTRACT(\'quarter\' FROM CAST(\'2023-01-01\' AS DATE)) AS "QUARTER", EXTRACT(\'month\' FROM StrictDate) AS "MONTH", EXTRACT(\'week\' FROM StrictDate) AS "WEEK", ' + - 'EXTRACT(\'dow\' FROM StrictDate) AS "DOW", EXTRACT(\'day\' FROM StrictDate) AS "DAY", EXTRACT(\'doy\' FROM StrictDate) AS "DOY", EXTRACT(\'hour\' FROM StrictDate) AS "HOUR", EXTRACT(\'minute\' FROM StrictDate) AS "MINUTE", ' + - 'EXTRACT(\'second\' FROM StrictDate) AS "SECOND", EXTRACT(\'epoch\' FROM StrictDate) AS "EPOCH" FROM service."/service/service1"'; + test( + 'SELECT EXTRACT(\'year\' FROM StrictDate) AS "YEAR", EXTRACT(\'quarter\' FROM CAST(\'2023-01-01\' AS DATE)) AS "QUARTER", EXTRACT(\'month\' FROM StrictDate) AS "MONTH", EXTRACT(\'week\' FROM StrictDate) AS "WEEK", ' + + 'EXTRACT(\'dow\' FROM StrictDate) AS "DOW", EXTRACT(\'day\' FROM StrictDate) AS "DAY", EXTRACT(\'doy\' FROM StrictDate) AS "DOY", EXTRACT(\'hour\' FROM StrictDate) AS "HOUR", EXTRACT(\'minute\' FROM StrictDate) AS "MINUTE", ' + + 'EXTRACT(\'second\' FROM StrictDate) AS "SECOND", EXTRACT(\'epoch\' FROM StrictDate) AS "EPOCH" FROM service."/service/service1"', - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| + {| FlatInput.all() ->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], @@ -1338,27 +1312,26 @@ function <> meta::external::query::sql::transformation::queryToPure:: col(row:TDSRow[1] | second($row.getStrictDate('StrictDate')), 'SECOND'), col(row:TDSRow[1] | toEpochValue($row.getStrictDate('StrictDate')), 'EPOCH') ]) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } //INTERVAL function <> meta::external::query::sql::transformation::queryToPure::tests::testIntervalArithmetic():Boolean[1] { - let sqlString = 'SELECT ' + - 'StrictDate + INTERVAL \'1 YEAR 3 WEEKS 2 DAYS\' AS "INTERVAL_ADD", ' + - 'StrictDate + 1 + INTERVAL \'1 YEAR 3 WEEKS 2 DAYS\' AS "INTERVAL_MULTI_ADD", ' + - 'StrictDate + 1 AS "NUMERIC_ADD", ' + - 'StrictDate + 6 * INTERVAL \'1 YEAR 3 WEEKS 2 DAYS\' AS "INTERVAL_TIMES", ' + - 'StrictDate + NULL * INTERVAL \'1 YEAR 3 WEEKS 2 DAYS\' AS "INTERVAL_TIMES_NULL", ' + - 'StrictDate + NULL + INTERVAL \'1 YEAR 3 WEEKS 2 DAYS\' AS "INTERVAL_ADD_NULL", ' + - '(CAST(\'2023-01-01\' AS DATE) + 2 * INTERVAL \'1 DAY\') + 3 * INTERVAL \'2 DAY\' AS "INTERVAL_MIX", ' + - 'StrictDate + EXTRACT(\'year\' FROM StrictDate) * INTERVAL \'2 YEAR 3 DAYS\' AS "INTERVAL_MIX2", ' + - 'CAST((DATE_TRUNC( \'DAY\', CAST("StrictDate" AS DATE) ) + (EXTRACT(DOW FROM "StrictDate") * INTERVAL \'1 DAY\')) AS DATE) AS "INTERVAL_MIX3" ' + - 'FROM service."/service/service1"'; - - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| + test( + 'SELECT ' + + 'StrictDate + INTERVAL \'1 YEAR 3 WEEKS 2 DAYS\' AS "INTERVAL_ADD", ' + + 'StrictDate + 1 + INTERVAL \'1 YEAR 3 WEEKS 2 DAYS\' AS "INTERVAL_MULTI_ADD", ' + + 'StrictDate + 1 AS "NUMERIC_ADD", ' + + 'StrictDate + 6 * INTERVAL \'1 YEAR 3 WEEKS 2 DAYS\' AS "INTERVAL_TIMES", ' + + 'StrictDate + NULL * INTERVAL \'1 YEAR 3 WEEKS 2 DAYS\' AS "INTERVAL_TIMES_NULL", ' + + 'StrictDate + NULL + INTERVAL \'1 YEAR 3 WEEKS 2 DAYS\' AS "INTERVAL_ADD_NULL", ' + + '(CAST(\'2023-01-01\' AS DATE) + 2 * INTERVAL \'1 DAY\') + 3 * INTERVAL \'2 DAY\' AS "INTERVAL_MIX", ' + + 'StrictDate + EXTRACT(\'year\' FROM StrictDate) * INTERVAL \'2 YEAR 3 DAYS\' AS "INTERVAL_MIX2", ' + + 'CAST((DATE_TRUNC( \'DAY\', CAST("StrictDate" AS DATE) ) + (EXTRACT(DOW FROM "StrictDate") * INTERVAL \'1 DAY\')) AS DATE) AS "INTERVAL_MIX3" ' + + 'FROM service."/service/service1"', + + {| FlatInput.all() ->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], @@ -1374,19 +1347,20 @@ function <> meta::external::query::sql::transformation::queryToPure:: col(row:TDSRow[1] | $row.getStrictDate('StrictDate')->adjust(year($row.getStrictDate('StrictDate')) * 2, DurationUnit.YEARS)->adjust(year($row.getStrictDate('StrictDate')) * 3, DurationUnit.DAYS), 'INTERVAL_MIX2'), col(row:TDSRow[1] | $row.getStrictDate('StrictDate')->firstHourOfDay()->adjust(($row.getStrictDate('StrictDate')->dayOfWeekNumber() * 1), DurationUnit.DAYS), 'INTERVAL_MIX3') ]) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } //STRING FUNCTIONS function <> meta::external::query::sql::transformation::queryToPure::tests::testStringFunctions():Boolean[1] { - let sqlString = 'SELECT ascii(String) AS "ASCII", chr(Integer) AS "CHR", concat(String, \'abc\') AS "CONCAT", String || \'abc\' AS "CONCAT2", regexp_like(String, \'test\') AS "MATCH", char_length(String) AS "CHAR_LENGTH", length(String) AS "LENGTH", ltrim(String) AS "LTRIM", ltrim(String, \' \') AS "LTRIM2", md5(String) AS "MD5", upper(String) AS "UPPER", ' + - 'lower(String) AS "LOWER", repeat(String, 2) AS "REPEAT", replace(String, \'A\', \'a\') AS "REPLACE", starts_with(String, \'a\') AS "STARTSWITH", strpos(String, \'abc\') AS "STRPOS", reverse(String) AS "REVERSE", rtrim(String) AS "RTRIM", rtrim(String, \' \') AS "RTRIM2", ' + - 'sha256(String) AS "SHA256", split_part(String, \',\', 1) AS "SPLITPART", split_part(String, \',\', Integer) AS "SPLITPART2", substring(String, 1) AS "SUBSTRING", substr(String, 1, 2) AS "SUBSTR", btrim(String) AS "TRIM", btrim(String, \' \') AS "TRIM2" FROM service."/service/service1"'; + test( + 'SELECT ascii(String) AS "ASCII", chr(Integer) AS "CHR", concat(String, \'abc\') AS "CONCAT", String || \'abc\' AS "CONCAT2", regexp_like(String, \'test\') AS "MATCH", ' + + 'char_length(String) AS "CHAR_LENGTH", length(String) AS "LENGTH", ltrim(String) AS "LTRIM", ltrim(String, \' \') AS "LTRIM2", md5(String) AS "MD5", upper(String) AS "UPPER", ' + + 'lower(String) AS "LOWER", repeat(String, 2) AS "REPEAT", replace(String, \'A\', \'a\') AS "REPLACE", starts_with(String, \'a\') AS "STARTSWITH", strpos(String, \'abc\') AS "STRPOS",' + + 'reverse(String) AS "REVERSE", rtrim(String) AS "RTRIM", rtrim(String, \' \') AS "RTRIM2", sha256(String) AS "SHA256", split_part(String, \',\', 1) AS "SPLITPART", ' + + 'split_part(String, \',\', Integer) AS "SPLITPART2", substring(String, 1) AS "SUBSTRING", substr(String, 1, 2) AS "SUBSTR", btrim(String) AS "TRIM", btrim(String, \' \') AS "TRIM2" FROM service."/service/service1"', - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| + {| FlatInput.all() ->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], @@ -1419,18 +1393,18 @@ function <> meta::external::query::sql::transformation::queryToPure:: col(row:TDSRow[1] | trim($row.getString('String')), 'TRIM'), col(row:TDSRow[1] | trim($row.getString('String')), 'TRIM2') ]) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } function <> meta::external::query::sql::transformation::queryToPure::tests::testStringFunctionsNullable():Boolean[1] { - let sqlString = 'SELECT ascii(NULL) AS "ASCII", chr(NULL) AS "CHR", regexp_like(NULL, \'test\') AS "MATCH", char_length(NULL) AS "CHAR_LENGTH", length(NULL) AS "LENGTH", ltrim(NULL) AS "LTRIM", ltrim(NULL, \' \') AS "LTRIM2", md5(NULL) AS "MD5", upper(NULL) AS "UPPER", ' + - 'lower(NULL) AS "LOWER", replace(NULL, \'A\', \'a\') AS "REPLACE", starts_with(NULL, \'a\') AS "STARTSWITH", strpos(NULL, \'abc\') AS "STRPOS", reverse(NULL) AS "REVERSE", rtrim(NULL) AS "RTRIM", rtrim(NULL, \' \') AS "RTRIM2", ' + - 'sha256(NULL) AS "SHA256", substring(NULL, 1) AS "SUBSTRING", substr(NULL, 1, 2) AS "SUBSTR", btrim(NULL) AS "TRIM", btrim(NULL, \' \') AS "TRIM2" FROM service."/service/service1"'; + test( + 'SELECT ascii(NULL) AS "ASCII", chr(NULL) AS "CHR", regexp_like(NULL, \'test\') AS "MATCH", char_length(NULL) AS "CHAR_LENGTH", length(NULL) AS "LENGTH", ltrim(NULL) AS "LTRIM", ' + + 'ltrim(NULL, \' \') AS "LTRIM2", md5(NULL) AS "MD5", upper(NULL) AS "UPPER", lower(NULL) AS "LOWER", replace(NULL, \'A\', \'a\') AS "REPLACE", starts_with(NULL, \'a\') AS "STARTSWITH", ' + + 'strpos(NULL, \'abc\') AS "STRPOS", reverse(NULL) AS "REVERSE", rtrim(NULL) AS "RTRIM", rtrim(NULL, \' \') AS "RTRIM2", sha256(NULL) AS "SHA256", substring(NULL, 1) AS "SUBSTRING", ' + + 'substr(NULL, 1, 2) AS "SUBSTR", btrim(NULL) AS "TRIM", btrim(NULL, \' \') AS "TRIM2" FROM service."/service/service1"', - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| + {| FlatInput.all() ->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], @@ -1458,21 +1432,21 @@ function <> meta::external::query::sql::transformation::queryToPure:: col(row:TDSRow[1] | []->cast(@String), 'TRIM'), col(row:TDSRow[1] | []->cast(@String), 'TRIM2') ]) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } //MATH FUNCTIONS function <> meta::external::query::sql::transformation::queryToPure::tests::testMathFunctions():Boolean[1] { - let sqlString = 'SELECT abs(Integer) AS "ABS", acos(Integer) AS "ACOS", asin(Integer) AS "ASIN", atan(Integer) AS "ATAN", atan2(Integer, 1) AS "ATAN2", ' + - 'ceil(Integer) AS "CEIL", ceiling(Integer) AS "CEILING", cos(Integer) AS "COS", cot(Integer) AS "COT", degrees(Integer) AS "DEGREES", div(Integer, 1) AS "DIV", exp(Integer) AS "EXP", floor(Float) AS "FLOOR", ' + - 'log(Integer) AS "LOG", ln(Integer) AS "LN", mod(Integer, 1) AS "MOD", pi() AS "PI", power(Integer, 2) AS "POWER", radians(Integer) AS "RADIANS", round(Integer) AS "ROUND", round(Decimal, 1) AS "ROUND_DEC", sign(Integer) AS "SIGN", ' + - 'sin(Integer) AS "SIN", sqrt(Integer) AS "SQRT", tan(Integer) AS "TAN", trunc(Integer) AS "TRUNC" FROM service."/service/service1"'; + test( + 'SELECT abs(Integer) AS "ABS", acos(Integer) AS "ACOS", asin(Integer) AS "ASIN", atan(Integer) AS "ATAN", atan2(Integer, 1) AS "ATAN2", ' + + 'ceil(Integer) AS "CEIL", ceiling(Integer) AS "CEILING", cos(Integer) AS "COS", cot(Integer) AS "COT", degrees(Integer) AS "DEGREES", div(Integer, 1) AS "DIV", ' + + 'exp(Integer) AS "EXP", floor(Float) AS "FLOOR", log(Integer) AS "LOG", ln(Integer) AS "LN", mod(Integer, 1) AS "MOD", pi() AS "PI", power(Integer, 2) AS "POWER", ' + + 'radians(Integer) AS "RADIANS", round(Integer) AS "ROUND", round(Decimal, 1) AS "ROUND_DEC", sign(Integer) AS "SIGN", ' + + 'sin(Integer) AS "SIN", sqrt(Integer) AS "SQRT", tan(Integer) AS "TAN", trunc(Integer) AS "TRUNC" FROM service."/service/service1"', - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| + {| FlatInput.all() ->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], @@ -1505,19 +1479,18 @@ function <> meta::external::query::sql::transformation::queryToPure:: col(row:TDSRow[1] | tan($row.getInteger('Integer')), 'TAN'), col(row:TDSRow[1] | if ($row.getInteger('Integer') > 0, | floor($row.getInteger('Integer')), | ceiling($row.getInteger('Integer'))), 'TRUNC') ]) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } function <> meta::external::query::sql::transformation::queryToPure::tests::testMathFunctionsNullable():Boolean[1] { - let sqlString = 'SELECT abs(NULL) AS "ABS", acos(NULL) AS "ACOS", asin(NULL) AS "ASIN", atan(NULL) AS "ATAN", atan2(NULL, 1) AS "ATAN2", ' + - 'ceil(NULL) AS "CEIL", ceiling(NULL) AS "CEILING", cos(NULL) AS "COS", cot(NULL) AS "COT", degrees(NULL) AS "DEGREES", div(NULL, 1) AS "DIV", exp(NULL) AS "EXP", floor(NULL) AS "FLOOR", ' + - 'log(NULL) AS "LOG", ln(NULL) AS "LN", mod(NULL, 1) AS "MOD", power(NULL, 2) AS "POWER", radians(NULL) AS "RADIANS", round(NULL) AS "ROUND", round(NULL, 1) AS "ROUND_DEC", sign(NULL) AS "SIGN", ' + - 'sin(NULL) AS "SIN", sqrt(NULL) AS "SQRT", tan(NULL) AS "TAN", trunc(NULL) AS "TRUNC" FROM service."/service/service1"'; + test( + 'SELECT abs(NULL) AS "ABS", acos(NULL) AS "ACOS", asin(NULL) AS "ASIN", atan(NULL) AS "ATAN", atan2(NULL, 1) AS "ATAN2", ' + + 'ceil(NULL) AS "CEIL", ceiling(NULL) AS "CEILING", cos(NULL) AS "COS", cot(NULL) AS "COT", degrees(NULL) AS "DEGREES", div(NULL, 1) AS "DIV", exp(NULL) AS "EXP", floor(NULL) AS "FLOOR", ' + + 'log(NULL) AS "LOG", ln(NULL) AS "LN", mod(NULL, 1) AS "MOD", power(NULL, 2) AS "POWER", radians(NULL) AS "RADIANS", round(NULL) AS "ROUND", round(NULL, 1) AS "ROUND_DEC", sign(NULL) AS "SIGN", ' + + 'sin(NULL) AS "SIN", sqrt(NULL) AS "SQRT", tan(NULL) AS "TAN", trunc(NULL) AS "TRUNC" FROM service."/service/service1"', - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| + {| FlatInput.all() ->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], @@ -1549,17 +1522,16 @@ function <> meta::external::query::sql::transformation::queryToPure:: col(row:TDSRow[1] | []->cast(@Float), 'TAN'), col(row:TDSRow[1] | []->cast(@Integer), 'TRUNC') ]) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } //COLLECTION FUNCTIONS function <> meta::external::query::sql::transformation::queryToPure::tests::testCollectionFunctions():Boolean[1] { - let sqlString = 'SELECT coalesce(NULL, Integer, 1) AS "COALESCE" FROM service."/service/service1"'; + test( + 'SELECT coalesce(NULL, Integer, 1) AS "COALESCE" FROM service."/service/service1"', - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| + {| FlatInput.all() ->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], @@ -1567,28 +1539,21 @@ function <> meta::external::query::sql::transformation::queryToPure:: ->project([ col(row:TDSRow[1] | meta::pure::tds::extensions::firstNotNull([$row.getInteger('Integer'), 1]), 'COALESCE') ]) - }; - - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); - - let plan = $sqlTransformContext->getPlan($sqlTransformContext.sources, $sqlTransformContext.extensions); - let sql = $plan.rootExecutionNode->cast(@meta::relational::mapping::RelationalTdsInstantiationExecutionNode).executionNodes->cast(@meta::relational::mapping::SQLExecutionNode).sqlQuery; - - assertEquals('select coalesce("root".integer, 1) as "COALESCE" from flat as "root"', $sql); + }) } //GROUP FUNCTIONS function <> meta::external::query::sql::transformation::queryToPure::tests::testGroupFunctions():Boolean[1] { - let sqlString = 'SELECT String, ' + - 'percentile_cont(0.1) WITHIN GROUP (ORDER BY Integer ASC) AS "PERCENTILE_CONT_ASC", ' + - 'percentile_cont(0.2) WITHIN GROUP (ORDER BY Integer DESC) AS "PERCENTILE_CONT_DESC", ' + - 'percentile_disc(0.3) WITHIN GROUP (ORDER BY Integer ASC) AS "PERCENTILE_DISC_ASC", ' + - 'percentile_disc(0.4) WITHIN GROUP (ORDER BY Integer DESC) AS "PERCENTILE_DISC_DESC" ' + - 'FROM service."/service/service1" GROUP BY String'; + test( + 'SELECT String, ' + + 'percentile_cont(0.1) WITHIN GROUP (ORDER BY Integer ASC) AS "PERCENTILE_CONT_ASC", ' + + 'percentile_cont(0.2) WITHIN GROUP (ORDER BY Integer DESC) AS "PERCENTILE_CONT_DESC", ' + + 'percentile_disc(0.3) WITHIN GROUP (ORDER BY Integer ASC) AS "PERCENTILE_DISC_ASC", ' + + 'percentile_disc(0.4) WITHIN GROUP (ORDER BY Integer DESC) AS "PERCENTILE_DISC_DESC" ' + + 'FROM service."/service/service1" GROUP BY String', - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| + {| FlatInput.all() ->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], @@ -1599,22 +1564,20 @@ function <> meta::external::query::sql::transformation::queryToPure:: agg('PERCENTILE_DISC_ASC', row | $row.getInteger('Integer'), y | $y->percentile(0.3, true, false)), agg('PERCENTILE_DISC_DESC', row | $row.getInteger('Integer'), y | $y->percentile(0.4, false, false)) ]) - }; - - assertLambdaEquals($expected, $sqlTransformContext.lambda()); + }, false) } //WINDOW FUNCTIONS function <> meta::external::query::sql::transformation::queryToPure::tests::testWindowFunctions():Boolean[1] { - let sqlString = 'SELECT String AS "string", Integer, ' + - 'row_number() OVER (PARTITION BY String ORDER BY Integer ASC) AS "ROW", ' + - 'dense_rank() OVER (PARTITION BY String ORDER BY Integer DESC) AS "DENSE RANK", ' + - 'rank() OVER (PARTITION BY String ORDER BY Integer ASC) AS "RANK" ' + - 'FROM service."/service/service1"'; + test( + 'SELECT String AS "string", Integer, ' + + 'row_number() OVER (PARTITION BY String ORDER BY Integer ASC) AS "ROW", ' + + 'dense_rank() OVER (PARTITION BY String ORDER BY Integer DESC) AS "DENSE RANK", ' + + 'rank() OVER (PARTITION BY String ORDER BY Integer ASC) AS "RANK" ' + + 'FROM service."/service/service1"', - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| + {| FlatInput.all() ->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], @@ -1624,21 +1587,19 @@ function <> meta::external::query::sql::transformation::queryToPure:: ->olapGroupBy(['String'], desc('Integer'), y | $y->meta::pure::functions::math::olap::denseRank(), 'DENSE RANK') ->olapGroupBy(['String'], asc('Integer'), y | $y->meta::pure::functions::math::olap::rank(), 'RANK') ->restrict(['string', 'Integer', 'ROW', 'DENSE RANK', 'RANK']) - }; - - assertLambdaEquals($expected, $sqlTransformContext.lambda()); + }, false) } function <> meta::external::query::sql::transformation::queryToPure::tests::testProjectWithWindowFunctions():Boolean[1] { - let sqlString = 'SELECT String, abs(Integer) AS "ABS", ' + - 'row_number() OVER (PARTITION BY upper(String) ORDER BY EXTRACT(YEAR FROM StrictDate) + 10 ASC) AS "ROW", ' + - 'dense_rank() OVER (PARTITION BY String ORDER BY Integer DESC) AS "DENSE RANK", ' + - 'rank() OVER (PARTITION BY String ORDER BY Integer ASC) AS "RANK" ' + - 'FROM service."/service/service1"'; + test( + 'SELECT String, abs(Integer) AS "ABS", ' + + 'row_number() OVER (PARTITION BY upper(String) ORDER BY EXTRACT(YEAR FROM StrictDate) + 10 ASC) AS "ROW", ' + + 'dense_rank() OVER (PARTITION BY String ORDER BY Integer DESC) AS "DENSE RANK", ' + + 'rank() OVER (PARTITION BY String ORDER BY Integer ASC) AS "RANK" ' + + 'FROM service."/service/service1"', - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| + {| FlatInput.all() ->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn], @@ -1652,21 +1613,19 @@ function <> meta::external::query::sql::transformation::queryToPure:: ->olapGroupBy(['String'], desc('Integer'), y | $y->meta::pure::functions::math::olap::denseRank(), 'DENSE RANK') ->olapGroupBy(['String'], asc('Integer'), y | $y->meta::pure::functions::math::olap::rank(), 'RANK') ->restrict(['String', 'ABS', 'ROW', 'DENSE RANK', 'RANK']) - }; - - assertLambdaEquals($expected, $sqlTransformContext.lambda()); + }, false) } function <> meta::external::query::sql::transformation::queryToPure::tests::testGroupByWithWindowFunctions():Boolean[1] { - let sqlString = 'SELECT String, Integer, sum(Integer) AS "SUM", ' + - 'row_number() OVER (PARTITION BY String ORDER BY Integer ASC) AS "ROW", ' + - 'dense_rank() OVER (PARTITION BY String ORDER BY Integer DESC) AS "DENSE RANK", ' + - 'rank() OVER (PARTITION BY String ORDER BY Integer ASC) AS "RANK" ' + - 'FROM service."/service/service1" GROUP BY String, Integer'; + test( + 'SELECT String, Integer, sum(Integer) AS "SUM", ' + + 'row_number() OVER (PARTITION BY String ORDER BY Integer ASC) AS "ROW", ' + + 'dense_rank() OVER (PARTITION BY String ORDER BY Integer DESC) AS "DENSE RANK", ' + + 'rank() OVER (PARTITION BY String ORDER BY Integer ASC) AS "RANK" ' + + 'FROM service."/service/service1" GROUP BY String, Integer', - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| + {| FlatInput.all() ->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], @@ -1675,23 +1634,21 @@ function <> meta::external::query::sql::transformation::queryToPure: ->olapGroupBy(['String'], desc('Integer'), y | $y->meta::pure::functions::math::olap::denseRank(), 'DENSE RANK') ->olapGroupBy(['String'], asc('Integer'), y | $y->meta::pure::functions::math::olap::rank(), 'RANK') ->groupBy(['String', 'Integer'], agg('SUM', row | $row.getInteger('Integer'), y | $y->sum())) - }; - - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } //LIKE function <> meta::external::query::sql::transformation::queryToPure::tests::testLike():Boolean[1] { - let sqlString = 'SELECT ' + - 'String like \'b\' AS "EQUAL", ' + - 'String like \'b%\' AS "STARTS_WITH", ' + - 'String like \'%b\' AS "ENDS_WITH", ' + - 'String like \'%b%\' AS "CONTAINS"' + - 'FROM service."/service/service1"'; + test( + 'SELECT ' + + 'String like \'b\' AS "EQUAL", ' + + 'String like \'b%\' AS "STARTS_WITH", ' + + 'String like \'%b\' AS "ENDS_WITH", ' + + 'String like \'%b%\' AS "CONTAINS"' + + 'FROM service."/service/service1"', - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| + {| FlatInput.all() ->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], @@ -1700,53 +1657,53 @@ function <> meta::external::query::sql::transformation::queryToPure:: col(row:TDSRow[1] | equal($row.getString('String'), 'b'), 'EQUAL'), col(row:TDSRow[1] | startsWith($row.getString('String'), 'b'), 'STARTS_WITH'), col(row:TDSRow[1] | endsWith($row.getString('String'), 'b'), 'ENDS_WITH'), - col(row:TDSRow[1] | contains($row.getString('String'), 'b'), 'CONTAINS') + col(row:TDSRow[1] | contains($row.getString('String'), 'b'), 'CONTAINS') ]) - }; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + }) } function <> meta::external::query::sql::transformation::queryToPure::tests::testFromScopingSimple():Boolean[1] { - let sqlString = 'SELECT * FROM service."/service/service1"'; - let sqlTransformContext = $sqlString->processQuery(true); - let expected = {| FlatInput.all()->project( + test( + 'SELECT * FROM service."/service/service1"', + + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ])->from(dummyMapping, dummyRuntime()) - }->meta::pure::router::preeval::preval(sqlExtensions()); - - assertLambdaJSONEquals($expected, $sqlTransformContext.lambda()); + }->meta::pure::router::preeval::preval(sqlExtensions()), testSources(), true, false, true) } function <> meta::external::query::sql::transformation::queryToPure::tests::testFromScopingNoMapping():Boolean[1] { - let sqlString = 'SELECT * FROM service."/service/service1"'; let mapping = meta::external::query::sql::transformation::queryToPure::emptyMapping(); + let sources = serviceToSource(createService('/service/service1', | FlatInput.all()->project(x | $x.stringIn, 'String'), [], dummyRuntime())); - let sqlTransformContext = $sqlString->processQuery(serviceToSource(createService('/service/service1', | FlatInput.all()->project(x | $x.stringIn, 'String'), [], dummyRuntime())), true); - let expected = {| FlatInput.all()->project(x | $x.stringIn, 'String')->from($mapping, dummyRuntime())}->meta::pure::router::preeval::preval(sqlExtensions()); + test( + 'SELECT * FROM service."/service/service1"', - assertLambdaJSONEquals($expected, $sqlTransformContext.lambda()); + {| + FlatInput.all()->project(x | $x.stringIn, 'String')->from($mapping, dummyRuntime()) + }->meta::pure::router::preeval::preval(sqlExtensions()), $sources, true, false, true); } function <> meta::external::query::sql::transformation::queryToPure::tests::testFromScopingSimpleNested():Boolean[1] { - let sqlString = 'SELECT * FROM (SELECT Integer FROM service."/service/service1")'; - let sqlTransformContext = $sqlString->processQuery(true); - let expected = {| FlatInput.all()->project( + test( + 'SELECT * FROM (SELECT Integer FROM service."/service/service1")', + + {| + FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ])->restrict('Integer')->from(dummyMapping, dummyRuntime()) - }->meta::pure::router::preeval::preval(sqlExtensions()); - - assertLambdaJSONEquals($expected, $sqlTransformContext.lambda()); + }->meta::pure::router::preeval::preval(sqlExtensions()), testSources(), true, false, true) } function <> meta::external::query::sql::transformation::queryToPure::tests::testFromScopingJoin():Boolean[1] { - let sqlString = 'SELECT table1.Integer FROM service."/service/service1" "table1" LEFT OUTER JOIN (SELECT Integer AS "int" FROM service."/service/service2") AS "table2" ON "table1"."Integer" = "table2"."int"'; + test( + 'SELECT table1.Integer FROM service."/service/service1" "table1" LEFT OUTER JOIN (SELECT Integer AS "int" FROM service."/service/service2") AS "table2" ON "table1"."Integer" = "table2"."int"', - let sqlTransformContext = $sqlString->processQuery(true); - let expected = {| FlatInput.all()->project( + {| FlatInput.all()->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], [ 'Boolean', 'Integer', 'Float', 'Decimal', 'StrictDate', 'DateTime', 'String' ])->restrict('Integer')->from(dummyMapping, dummyRuntime()) ->join(FlatInput.all()->project( @@ -1754,17 +1711,15 @@ function <> meta::external::query::sql::transformation::queryToPure: ['ID', 'Integer', 'String'] )->renameColumns(pair('Integer', 'int'))->restrict(['int'])->from(dummyMapping, dummyRuntime()), meta::relational::metamodel::join::JoinType.LEFT_OUTER, {row1:TDSRow[1], row2:TDSRow[1] | $row1.getInteger('Integer') == $row2.getInteger('int')} )->restrict('Integer') - }->meta::pure::router::preeval::preval(sqlExtensions()); - - assertLambdaJSONEquals($expected, $sqlTransformContext.lambda()); + }->meta::pure::router::preeval::preval(sqlExtensions())) } function <> meta::external::query::sql::transformation::queryToPure::tests::testFromScopingUnion():Boolean[1] { - let sqlString = 'SELECT Integer FROM service."/service/service1" UNION SELECT Integer FROM service."/service/service2"'; + test( + 'SELECT Integer FROM service."/service/service1" UNION SELECT Integer FROM service."/service/service2"', - let sqlTransformContext = $sqlString->processQuery(true); - let expected = {| + {| FlatInput.all() ->project( [ x | $x.booleanIn, x | $x.integerIn, x | $x.floatIn, x | $x.decimalIn, x | $x.strictDateIn, x | $x.dateTimeIn, x | $x.stringIn ], @@ -1777,16 +1732,15 @@ function <> meta::external::query::sql::transformation::queryToPure:: [ 'ID', 'Integer', 'String' ]) ->restrict('Integer')->from(dummyMapping, dummyRuntime()) ) - }->meta::pure::router::preeval::preval(sqlExtensions()); - - assertLambdaJSONEquals($expected, $sqlTransformContext.lambda()); + }->meta::pure::router::preeval::preval(sqlExtensions()), testSources(), true, false, true) } function <> meta::external::query::sql::transformation::queryToPure::tests::testParameterizedAllSupplied():Boolean[1] { - let sqlString = 'SELECT * FROM service(\'/service/service4/{id}\', id => \'abc\', ints => [1,2,3], date => \'2023-01-01\') LIMIT 1'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {id:String[1], ints:Integer[*], date:StrictDate[0..1]| + test( + 'SELECT * FROM service(\'/service/service4/{id}\', id => \'abc\', ints => [1,2,3], date => \'2023-01-01\') LIMIT 1', + + {id:String[1], ints:Integer[*], date:StrictDate[0..1]| FlatInput.all()->filter(f | $f.idIn == $id && $f.integerIn->in($ints) && ($f.strictDateIn > $date))->project( [ x | $x.idIn, x | $x.integerIn, x | $x.enumVal @@ -1794,16 +1748,15 @@ function <> meta::external::query::sql::transformation::queryToPure:: [ 'ID', 'Integer', 'Enum' ] - )->limit(1);}; - - assertLambdaEquals($expected, $sqlTransformContext.lambda()); + )->limit(1)}, false) } function <> meta::external::query::sql::transformation::queryToPure::tests::testParameterizedNotSupplied():Boolean[1] { - let sqlString = 'SELECT * FROM service(\'/service/service4/{id}\', id => \'abc\') LIMIT 1'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {id:String[1], ints:Integer[*], date:StrictDate[0..1]| + test( + 'SELECT * FROM service(\'/service/service4/{id}\', id => \'abc\') LIMIT 1', + + {id:String[1], ints:Integer[*], date:StrictDate[0..1]| FlatInput.all()->filter(f | $f.idIn == $id && $f.integerIn->in($ints) && ($f.strictDateIn > $date))->project( [ @@ -1812,15 +1765,15 @@ function <> meta::external::query::sql::transformation::queryToPure:: [ 'ID', 'Integer', 'Enum' ] - )->limit(1);}; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + )->limit(1)}) } function <> meta::external::query::sql::transformation::queryToPure::tests::testMultiLineQuery():Boolean[1] { - let sqlString = 'SELECT * FROM service."/service/service3"'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {| + test( + 'SELECT * FROM service."/service/service3"', + + {| let const = 123; FlatInput.all()->project( [ @@ -1829,15 +1782,15 @@ function <> meta::external::query::sql::transformation::queryToPure:: [ 'ID', 'Integer', 'The Enum Value', 'The Type', 'Const' ] - );}; - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + );}) } function <> meta::external::query::sql::transformation::queryToPure::tests::testParameterizedMultiLineJoin():Boolean[1] { - let sqlString = 'SELECT * FROM service(\'/service/service4/{id}\', id => \'abc\') union select ID, Integer, "The Enum Value" from service(\'/service/service3\')'; - let sqlTransformContext = $sqlString->processQuery(); - let expected = {id_1:String[1], ints_1:Integer[*], date_1:StrictDate[0..1]| + test( + 'SELECT * FROM service(\'/service/service4/{id}\', id => \'abc\') union select ID, Integer, "The Enum Value" from service(\'/service/service3\')', + + {id_1:String[1], ints_1:Integer[*], date_1:StrictDate[0..1]| let const_3 = 123; @@ -1855,9 +1808,7 @@ function <> meta::external::query::sql::transformation::queryToPure:: [ 'ID', 'Integer', 'The Enum Value', 'The Type', 'Const' ] - )->restrict(['ID', 'Integer', 'The Enum Value']));}; - - assertLambdaAndJSONEquals($expected, $sqlTransformContext.lambda()); + )->restrict(['ID', 'Integer', 'The Enum Value']));}) } @@ -1909,13 +1860,13 @@ function <> meta::external::query::sql::transformation::queryToPure:: let actualSchema = $sqlString->processQuery(true).columns()->meta::external::query::sql::tdsColsToSchema(); - let expectedCol1 = ^PrimitiveValueSchemaColumn(name='Boolean', type=meta::external::query::sql::PrimitiveType.Boolean); - let expectedCol2 = ^PrimitiveValueSchemaColumn(name='Integer', type=meta::external::query::sql::PrimitiveType.Integer); - let expectedCol3 = ^PrimitiveValueSchemaColumn(name='Float', type=meta::external::query::sql::PrimitiveType.Float); - let expectedCol4 = ^PrimitiveValueSchemaColumn(name='Decimal', type=meta::external::query::sql::PrimitiveType.Decimal); - let expectedCol5 = ^PrimitiveValueSchemaColumn(name='StrictDate', type=meta::external::query::sql::PrimitiveType.StrictDate); - let expectedCol6 = ^PrimitiveValueSchemaColumn(name='DateTime', type=meta::external::query::sql::PrimitiveType.DateTime); - let expectedCol7 = ^PrimitiveValueSchemaColumn(name='String', type=meta::external::query::sql::PrimitiveType.String); + let expectedCol1 = ^PrimitiveSchemaColumn(name='Boolean', type=meta::external::query::sql::schema::metamodel::PrimitiveType.Boolean); + let expectedCol2 = ^PrimitiveSchemaColumn(name='Integer', type=meta::external::query::sql::schema::metamodel::PrimitiveType.Integer); + let expectedCol3 = ^PrimitiveSchemaColumn(name='Float', type=meta::external::query::sql::schema::metamodel::PrimitiveType.Float); + let expectedCol4 = ^PrimitiveSchemaColumn(name='Decimal', type=meta::external::query::sql::schema::metamodel::PrimitiveType.Decimal); + let expectedCol5 = ^PrimitiveSchemaColumn(name='StrictDate', type=meta::external::query::sql::schema::metamodel::PrimitiveType.StrictDate); + let expectedCol6 = ^PrimitiveSchemaColumn(name='DateTime', type=meta::external::query::sql::schema::metamodel::PrimitiveType.DateTime); + let expectedCol7 = ^PrimitiveSchemaColumn(name='String', type=meta::external::query::sql::schema::metamodel::PrimitiveType.String); let expectedSchema = ^Schema(columns=[$expectedCol1, $expectedCol2, $expectedCol3, $expectedCol4, $expectedCol5, $expectedCol6, $expectedCol7]); assertEquals($expectedSchema, $actualSchema); @@ -1927,9 +1878,9 @@ function <> meta::external::query::sql::transformation::queryToPure:: let actualSchema = $sqlString->processQuery(true).columns()->meta::external::query::sql::tdsColsToSchema(); - let expectedCol1 = ^PrimitiveValueSchemaColumn(name='ID', type=meta::external::query::sql::PrimitiveType.Integer); - let expectedCol2 = ^PrimitiveValueSchemaColumn(name='Integer', type=meta::external::query::sql::PrimitiveType.Integer); - let expectedCol3 = ^PrimitiveValueSchemaColumn(name='String', type=meta::external::query::sql::PrimitiveType.String); + let expectedCol1 = ^PrimitiveSchemaColumn(name='ID', type=meta::external::query::sql::schema::metamodel::PrimitiveType.Integer); + let expectedCol2 = ^PrimitiveSchemaColumn(name='Integer', type=meta::external::query::sql::schema::metamodel::PrimitiveType.Integer); + let expectedCol3 = ^PrimitiveSchemaColumn(name='String', type=meta::external::query::sql::schema::metamodel::PrimitiveType.String); let expectedSchema = ^Schema(columns=[$expectedCol1, $expectedCol2, $expectedCol3]); assertEquals($expectedSchema, $actualSchema); @@ -1941,13 +1892,13 @@ function <> meta::external::query::sql::transformation::queryToPure:: let actualSchema = $sqlString->processQuery(true).columns()->meta::external::query::sql::tdsColsToSchema(); - let expectedCol1 = ^PrimitiveValueSchemaColumn(name='ID', type=meta::external::query::sql::PrimitiveType.Integer); - let expectedCol2 = ^PrimitiveValueSchemaColumn(name='Integer', type=meta::external::query::sql::PrimitiveType.Integer); - let expectedCol3 = ^EnumValueSchemaColumn(name='The Enum Value', type=MyEnum->elementToPath()); - let expectedCol4 = ^EnumValueSchemaColumn(name='The Type', type=FieldType->elementToPath()); - let expectedCol5 = ^PrimitiveValueSchemaColumn(name='Const', type=meta::external::query::sql::PrimitiveType.Integer); - let expectedEnum1 = ^meta::external::query::sql::Enum(type=MyEnum->elementToPath(), values=MyEnum->enumValues()->map(v:meta::pure::metamodel::type::Enum[1] | $v->id())); - let expectedEnum2 = ^meta::external::query::sql::Enum(type=FieldType->elementToPath(), values=FieldType->enumValues()->map(v:meta::pure::metamodel::type::Enum[1] | $v->id())); + let expectedCol1 = ^PrimitiveSchemaColumn(name='ID', type=meta::external::query::sql::schema::metamodel::PrimitiveType.Integer); + let expectedCol2 = ^PrimitiveSchemaColumn(name='Integer', type=meta::external::query::sql::schema::metamodel::PrimitiveType.Integer); + let expectedCol3 = ^EnumSchemaColumn(name='The Enum Value', type=MyEnum->elementToPath()); + let expectedCol4 = ^EnumSchemaColumn(name='The Type', type=FieldType->elementToPath()); + let expectedCol5 = ^PrimitiveSchemaColumn(name='Const', type=meta::external::query::sql::schema::metamodel::PrimitiveType.Integer); + let expectedEnum1 = ^meta::external::query::sql::schema::metamodel::Enum(type=MyEnum->elementToPath(), values=MyEnum->enumValues()->map(v:meta::pure::metamodel::type::Enum[1] | $v->id())); + let expectedEnum2 = ^meta::external::query::sql::schema::metamodel::Enum(type=FieldType->elementToPath(), values=FieldType->enumValues()->map(v:meta::pure::metamodel::type::Enum[1] | $v->id())); let expectedSchema = ^Schema(columns=[$expectedCol1, $expectedCol2, $expectedCol3, $expectedCol4,$expectedCol5], enums=[$expectedEnum1, $expectedEnum2]); assertEquals($expectedSchema, $actualSchema); @@ -1996,16 +1947,16 @@ function <> meta::external::query::sql::transformation::queryToPure:: doNameTest(^ArrayLiteral(values = [^IntegerLiteral(value = 1), ^IntegerLiteral(value = 2)]), '1,2'); doNameTest(^NullLiteral(), 'NULL'); - doNameTest(^LikePredicate(value = ^StringLiteral(value = 'a', quoted = false), + doNameTest(^LikePredicate(value = ^StringLiteral(value = 'a', quoted = false), pattern = ^StringLiteral(value = 'b', quoted = false), ignoreCase = false), 'a LIKE b'); - doNameTest(^LikePredicate(value = ^StringLiteral(value = 'a', quoted = false), + doNameTest(^LikePredicate(value = ^StringLiteral(value = 'a', quoted = false), + pattern = ^StringLiteral(value = 'b', quoted = false), + ignoreCase = true), 'a ILIKE b'); + doNameTest(^LikePredicate(value = ^StringLiteral(value = 'a', quoted = false), pattern = ^StringLiteral(value = 'b', quoted = false), - ignoreCase = true), 'a ILIKE b'); - doNameTest(^LikePredicate(value = ^StringLiteral(value = 'a', quoted = false), - pattern = ^StringLiteral(value = 'b', quoted = false), escape = ^StringLiteral(value = '!', quoted = false), - ignoreCase = false), 'a LIKE b ESCAPE !'); + ignoreCase = false), 'a LIKE b ESCAPE !'); doNameTest(^LogicalBinaryExpression(left = ^BooleanLiteral(value = true), right = ^BooleanLiteral(value = false),type = LogicalBinaryType.AND), 'true AND false'); doNameTest(^LogicalBinaryExpression(left = ^BooleanLiteral(value = true), right = ^BooleanLiteral(value = false),type = LogicalBinaryType.OR), 'true OR false'); @@ -2021,6 +1972,36 @@ function <> meta::external::query::sql::transformation::queryToPure:: defaultValue = ^IntegerLiteral(value = 1)), 'CASE WHEN true = false THEN 2 ELSE 1 END'); } + +function meta::external::query::sql::transformation::queryToPure::tests::testSources():SQLSource[*] +{ + [ + serviceToSource(Service1()), + serviceToSource(Service2()), + serviceToSource(Service3()), + serviceToSource(Service4()) + ] +} + +function meta::external::query::sql::transformation::queryToPure::tests::test(sql:String[1], expected:FunctionDefinition[1]):Boolean[1] +{ + test($sql, $expected, true); +} + +function meta::external::query::sql::transformation::queryToPure::tests::test(sql:String[1], expected:FunctionDefinition[1], assertJSON:Boolean[1]):Boolean[1] +{ + test($sql, $expected, testSources(), false, true, $assertJSON); +} + +function meta::external::query::sql::transformation::queryToPure::tests::test(sql:String[1], expected:FunctionDefinition[1], sources:SQLSource[*], scopeWithFrom:Boolean[1], assertLambda:Boolean[1], assertJSON:Boolean[1]):Boolean[1] +{ + let sqlTransformContext = $sql->processQuery($sources, $scopeWithFrom); + let actual = $sqlTransformContext.lambda(); + + if ($assertLambda, | assertLambdaEquals($expected, $actual), | true); + if ($assertJSON, | assertLambdaJSONEquals($expected, $actual), | true); +} + function meta::external::query::sql::transformation::queryToPure::tests::processQuery(sql: String[1]): SqlTransformContext[1] { processQuery($sql, false); @@ -2044,9 +2025,4 @@ function meta::external::query::sql::transformation::queryToPure::tests::process let context = rootContext($sources, $extensions); $query->processRootQuery(^$context(scopeWithFrom = $scopeWithFrom)); -} - -function meta::external::query::sql::transformation::queryToPure::tests::forceGetStringType(col: TDSColumn[1]): String[1] -{ - $col.type->toOne()->elementToPath(); -} +} \ No newline at end of file diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-pure/src/main/resources/core_external_query_sql/binding/model.pure b/legend-engine-xts-sql/legend-engine-xt-sql-pure/src/main/resources/core_external_query_sql/binding/model.pure deleted file mode 100644 index 899ee07cf30..00000000000 --- a/legend-engine-xts-sql/legend-engine-xt-sql-pure/src/main/resources/core_external_query_sql/binding/model.pure +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2023 Goldman Sachs -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - - -Class meta::external::query::sql::SchemaColumn -{ - <> name: String[1]; -} - -Class meta::external::query::sql::PrimitiveValueSchemaColumn extends meta::external::query::sql::SchemaColumn -{ - <> type: meta::external::query::sql::PrimitiveType[1]; -} - -Class meta::external::query::sql::EnumValueSchemaColumn extends meta::external::query::sql::SchemaColumn -{ - <> type: String[1]; -} - - -Class meta::external::query::sql::Schema -{ - <> columns: meta::external::query::sql::SchemaColumn[*]; - <> enums: meta::external::query::sql::Enum[*]; -} - -Enum meta::external::query::sql::PrimitiveType -{ - Boolean, - StrictDate, - Number, - String, - LatestDate, - Float, - DateTime, - Date, - Integer, - Decimal -} - -Class meta::external::query::sql::Enum -{ - <> type: String[1]; - <> values: String[*]; -} - -function meta::external::query::sql::stringToPrimitiveType(dt : String[1]) : meta::external::query::sql::PrimitiveType[1] -{ - let type = newMap( - [ - pair('Boolean', meta::external::query::sql::PrimitiveType.Boolean), - pair('StrictDate', meta::external::query::sql::PrimitiveType.StrictDate), - pair('Number', meta::external::query::sql::PrimitiveType.Number), - pair('String', meta::external::query::sql::PrimitiveType.String), - pair('LatestDate', meta::external::query::sql::PrimitiveType.LatestDate), - pair('Float', meta::external::query::sql::PrimitiveType.Float), - pair('DateTime', meta::external::query::sql::PrimitiveType.DateTime), - pair('Date', meta::external::query::sql::PrimitiveType.Date), - pair('Integer', meta::external::query::sql::PrimitiveType.Integer), - pair('Decimal', meta::external::query::sql::PrimitiveType.Decimal) - ] - )->get($dt); - if($type->isEmpty(), |fail('Unknown primitive type: ' + $dt), |[]); - $type->toOne(); -} - -function meta::external::query::sql::tdsColToSchemaCol(col: TDSColumn[1]): meta::external::query::sql::SchemaColumn[1] -{ - let columnName = $col.name; - if($col.type->isEmpty(), |fail('Column type is empty for col: ' + $columnName), |[]); - let tdsType = $col.type->toOne(); - let columnPrimitiveType = $col.type->match([ - s:PrimitiveType[1] | ^meta::external::query::sql::PrimitiveValueSchemaColumn(type=$tdsType->meta::pure::functions::meta::elementToPath()->meta::external::query::sql::stringToPrimitiveType(),name=$columnName), - e:Enumeration[1] | ^meta::external::query::sql::EnumValueSchemaColumn(type=$tdsType->meta::pure::functions::meta::elementToPath(),name=$columnName), - a:Any[*] | fail('Unsupported type on column: ' + $columnName + ' (' + $tdsType->meta::pure::functions::meta::elementToPath() + '), only primitive types and enums are supported'); - ])->cast(@meta::external::query::sql::SchemaColumn); -} - -function meta::external::query::sql::tdsColsToSchema(cols: TDSColumn[*]): meta::external::query::sql::Schema[1] -{ - let schemaCols = $cols->map(v:TDSColumn[1] | $v->meta::external::query::sql::tdsColToSchemaCol()); - let enums = $cols - ->map(c:TDSColumn[1] | $c.type->match([ - e:Enumeration[1] | ^meta::external::query::sql::Enum(type=$e->meta::pure::functions::meta::elementToPath(), values=$e->enumValues()->map( v:Enum[1] | $v->id())), - a:Any[*] | [] - ])) - ->distinct(); - ^meta::external::query::sql::Schema(columns=$schemaCols, enums=$enums); -} diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-pure/src/main/resources/core_external_query_sql/binding/schema.pure b/legend-engine-xts-sql/legend-engine-xt-sql-pure/src/main/resources/core_external_query_sql/binding/schema.pure new file mode 100644 index 00000000000..f75ada02117 --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-pure/src/main/resources/core_external_query_sql/binding/schema.pure @@ -0,0 +1,58 @@ +// Copyright 2023 Goldman Sachs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +function meta::external::query::sql::schema::stringToPrimitiveType(dt : String[1]) : meta::external::query::sql::schema::metamodel::PrimitiveType[1] +{ + let type = newMap( + [ + pair('Boolean', meta::external::query::sql::schema::metamodel::PrimitiveType.Boolean), + pair('StrictDate', meta::external::query::sql::schema::metamodel::PrimitiveType.StrictDate), + pair('Number', meta::external::query::sql::schema::metamodel::PrimitiveType.Number), + pair('String', meta::external::query::sql::schema::metamodel::PrimitiveType.String), + pair('LatestDate', meta::external::query::sql::schema::metamodel::PrimitiveType.LatestDate), + pair('Float', meta::external::query::sql::schema::metamodel::PrimitiveType.Float), + pair('DateTime', meta::external::query::sql::schema::metamodel::PrimitiveType.DateTime), + pair('Date', meta::external::query::sql::schema::metamodel::PrimitiveType.Date), + pair('Integer', meta::external::query::sql::schema::metamodel::PrimitiveType.Integer), + pair('Decimal', meta::external::query::sql::schema::metamodel::PrimitiveType.Decimal) + ] + )->get($dt); + if($type->isEmpty(), |fail('Unknown primitive type: ' + $dt), |[]); + $type->toOne(); +} + +function meta::external::query::sql::schema::tdsColToSchemaCol(col: TDSColumn[1]): meta::external::query::sql::schema::metamodel::SchemaColumn[1] +{ + let columnName = $col.name; + if($col.type->isEmpty(), |fail('Column type is empty for col: ' + $columnName), |[]); + let tdsType = $col.type->toOne(); + let columnPrimitiveType = $col.type->match([ + s:PrimitiveType[1] | ^meta::external::query::sql::schema::metamodel::PrimitiveSchemaColumn(type=$tdsType->meta::pure::functions::meta::elementToPath()->meta::external::query::sql::schema::stringToPrimitiveType(),name=$columnName), + e:Enumeration[1] | ^meta::external::query::sql::schema::metamodel::EnumSchemaColumn(type=$tdsType->meta::pure::functions::meta::elementToPath(),name=$columnName), + a:Any[*] | fail('Unsupported type on column: ' + $columnName + ' (' + $tdsType->meta::pure::functions::meta::elementToPath() + '), only primitive types and enums are supported'); + ])->cast(@meta::external::query::sql::schema::metamodel::SchemaColumn); +} + +function meta::external::query::sql::tdsColsToSchema(cols: TDSColumn[*]): meta::external::query::sql::schema::metamodel::Schema[1] +{ + let schemaCols = $cols->map(v:TDSColumn[1] | $v->meta::external::query::sql::schema::tdsColToSchemaCol()); + let enums = $cols + ->map(c:TDSColumn[1] | $c.type->match([ + e:Enumeration[1] | ^meta::external::query::sql::schema::metamodel::Enum(type=$e->meta::pure::functions::meta::elementToPath(), values=$e->enumValues()->map( v:Enum[1] | $v->id())), + a:Any[*] | [] + ])) + ->distinct(); + ^meta::external::query::sql::schema::metamodel::Schema(columns=$schemaCols, enums=$enums); +} diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-query/pom.xml b/legend-engine-xts-sql/legend-engine-xt-sql-query/pom.xml index 8a69596ec87..18db6143fc2 100644 --- a/legend-engine-xts-sql/legend-engine-xt-sql-query/pom.xml +++ b/legend-engine-xts-sql/legend-engine-xt-sql-query/pom.xml @@ -33,10 +33,6 @@ org.finos.legend.pure legend-pure-m3-core - - org.finos.legend.pure - legend-pure-runtime-java-engine-compiled - diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/SQLExecutor.java b/legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/SQLExecutor.java new file mode 100644 index 00000000000..798afbdf56b --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/SQLExecutor.java @@ -0,0 +1,293 @@ +// Copyright 2023 Goldman Sachs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package org.finos.legend.engine.query.sql.api; + +import org.eclipse.collections.api.RichIterable; +import org.eclipse.collections.api.block.function.Function; +import org.eclipse.collections.api.block.function.Function3; +import org.eclipse.collections.api.collection.MutableCollection; +import org.eclipse.collections.api.factory.Lists; +import org.eclipse.collections.api.factory.Maps; +import org.eclipse.collections.api.list.MutableList; +import org.eclipse.collections.api.map.MutableMap; +import org.eclipse.collections.api.multimap.MutableMultimap; +import org.eclipse.collections.api.tuple.Pair; +import org.eclipse.collections.impl.map.mutable.UnifiedMap; +import org.eclipse.collections.impl.tuple.Tuples; +import org.eclipse.collections.impl.utility.Iterate; +import org.eclipse.collections.impl.utility.ListIterate; +import org.eclipse.collections.impl.utility.internal.IterableIterate; +import org.finos.legend.engine.language.pure.compiler.toPureGraph.PureModel; +import org.finos.legend.engine.language.pure.modelManager.ModelManager; +import org.finos.legend.engine.plan.execution.PlanExecutor; +import org.finos.legend.engine.plan.execution.result.ConstantResult; +import org.finos.legend.engine.plan.execution.result.Result; +import org.finos.legend.engine.plan.generation.transformers.PlanTransformer; +import org.finos.legend.engine.plan.platform.PlanPlatform; +import org.finos.legend.engine.protocol.pure.PureClientVersions; +import org.finos.legend.engine.protocol.pure.v1.model.context.PureModelContext; +import org.finos.legend.engine.protocol.pure.v1.model.context.PureModelContextData; +import org.finos.legend.engine.protocol.pure.v1.model.context.PureModelContextPointer; +import org.finos.legend.engine.protocol.pure.v1.model.executionPlan.SingleExecutionPlan; +import org.finos.legend.engine.protocol.pure.v1.model.valueSpecification.raw.Lambda; +import org.finos.legend.engine.protocol.sql.metamodel.ProtocolToMetamodelTranslator; +import org.finos.legend.engine.protocol.sql.metamodel.Query; +import org.finos.legend.engine.protocol.sql.schema.metamodel.MetamodelToProtocolTranslator; +import org.finos.legend.engine.protocol.sql.schema.metamodel.Schema; +import org.finos.legend.engine.query.sql.api.sources.SQLContext; +import org.finos.legend.engine.query.sql.api.sources.SQLSource; +import org.finos.legend.engine.query.sql.api.sources.SQLSourceProvider; +import org.finos.legend.engine.query.sql.api.sources.SQLSourceResolvedContext; +import org.finos.legend.engine.query.sql.api.sources.SQLSourceTranslator; +import org.finos.legend.engine.query.sql.api.sources.TableSource; +import org.finos.legend.engine.query.sql.api.sources.TableSourceExtractor; +import org.finos.legend.engine.shared.core.ObjectMapperFactory; +import org.finos.legend.engine.shared.core.operational.errorManagement.EngineException; +import org.finos.legend.engine.shared.core.operational.logs.LogInfo; +import org.finos.legend.engine.shared.core.operational.logs.LoggingEventType; +import org.finos.legend.engine.shared.core.operational.prometheus.MetricsHandler; +import org.finos.legend.pure.generated.Root_meta_external_query_sql_metamodel_Query; +import org.finos.legend.pure.generated.Root_meta_external_query_sql_schema_metamodel_Schema; +import org.finos.legend.pure.generated.Root_meta_external_query_sql_transformation_queryToPure_PlanGenerationResult; +import org.finos.legend.pure.generated.Root_meta_external_query_sql_transformation_queryToPure_SQLSource; +import org.finos.legend.pure.generated.Root_meta_external_query_sql_transformation_queryToPure_SqlTransformContext; +import org.finos.legend.pure.generated.Root_meta_pure_executionPlan_ExecutionPlan; +import org.finos.legend.pure.generated.Root_meta_pure_extension_Extension; +import org.finos.legend.pure.generated.core_external_format_json_toJSON; +import org.finos.legend.pure.generated.core_external_query_sql_binding_fromPure_fromPure; +import org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.function.FunctionDefinition; +import org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.function.LambdaFunction; +import org.finos.legend.pure.m3.execution.ExecutionSupport; +import org.pac4j.core.profile.CommonProfile; +import org.slf4j.Logger; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.finos.legend.engine.plan.generation.PlanGenerator.transformExecutionPlan; + +public class SQLExecutor +{ + private static final Logger LOGGER = org.slf4j.LoggerFactory.getLogger(SQLExecutor.class); + private final ModelManager modelManager; + private final PlanExecutor planExecutor; + private final Function> routerExtensions; + private final Iterable transformers; + private final MutableMap providers; + + public SQLExecutor(ModelManager modelManager, PlanExecutor planExecutor, + Function> routerExtensions, + List providers, + Iterable transformers) + { + this.modelManager = modelManager; + this.planExecutor = planExecutor; + this.routerExtensions = routerExtensions; + this.transformers = transformers; + this.providers = ListIterate.groupByUniqueKey(providers, SQLSourceProvider::getType); + } + + + public Result execute(Query query, String user, SQLContext context, MutableList profiles) + { + return process(query, (transformedContext, pureModel, sources) -> + { + long start = System.currentTimeMillis(); + LOGGER.info(new LogInfo(profiles, LoggingEventType.EXECUTE_INTERACTIVE_STOP, (double) System.currentTimeMillis() - start).toString()); + + Root_meta_external_query_sql_transformation_queryToPure_PlanGenerationResult plans = planResult(transformedContext, pureModel, sources); + + Map arguments = UnifiedMap.newMapWith(IterableIterate.collectIf(plans._arguments(), p -> p._value() != null || p._plan() != null, p -> + { + Result result; + + if (p._value() != null) + { + Object value = p._value() instanceof org.finos.legend.pure.m3.coreinstance.meta.pure.functions.collection.List + ? ((org.finos.legend.pure.m3.coreinstance.meta.pure.functions.collection.List) p._value())._values() + : p._value(); + result = new ConstantResult(value); + } + else + { + Root_meta_pure_executionPlan_ExecutionPlan l = PlanPlatform.JAVA.bindPlan(p._plan(), null, pureModel, routerExtensions.apply(pureModel)); + SingleExecutionPlan m = transformExecutionPlan(l, pureModel, PureClientVersions.production, profiles, routerExtensions.apply(pureModel), transformers); + result = planExecutor.execute(m, Maps.mutable.empty(), "pentej", profiles); + } + + return Tuples.pair(p._name(), result); + })); + + Root_meta_pure_executionPlan_ExecutionPlan plan = plans._plan(); + plan = PlanPlatform.JAVA.bindPlan(plan, null, pureModel, routerExtensions.apply(pureModel)); + SingleExecutionPlan transformedPlan = transformExecutionPlan(plan, pureModel, PureClientVersions.production, profiles, routerExtensions.apply(pureModel), transformers); + + Result result = planExecutor.execute(transformedPlan, arguments, user, profiles); + + MetricsHandler.observe("execute", start, System.currentTimeMillis()); + + return result; + }, context, profiles); + } + + public Lambda lambda(Query query, SQLContext context, MutableList profiles) + { + return process(query, + (transformedContext, pureModel, sources) -> + { + LambdaFunction lambda = transformedContext.lambda(pureModel.getExecutionSupport()); + return transformLambda(lambda, pureModel); + }, + (sources, extensions, pureModel) -> core_external_query_sql_binding_fromPure_fromPure.Root_meta_external_query_sql_transformation_queryToPure_rootContext_SQLSource_MANY__Extension_MANY__SqlTransformContext_1_(sources, extensions, pureModel.getExecutionSupport())._scopeWithFrom(false), + context, profiles); + } + + public SingleExecutionPlan plan(Query query, SQLContext context, MutableList profiles) + { + return process(query, (transformedContext, pureModel, sources) -> transformExecutionPlan(planResult(transformedContext, pureModel, sources)._plan(), pureModel, PureClientVersions.production, profiles, routerExtensions.apply(pureModel), transformers), context, profiles); + } + + public Schema schema(Query query, MutableList profiles) + { + SQLContext context = new SQLContext(query, Maps.mutable.of()); + return process(query, (t, pm, sources) -> + { + Root_meta_external_query_sql_schema_metamodel_Schema schema = core_external_query_sql_binding_fromPure_fromPure.Root_meta_external_query_sql_transformation_queryToPure_getSchema_SqlTransformContext_1__Schema_1_(t, pm.getExecutionSupport()); + return new MetamodelToProtocolTranslator().translate(schema); + }, context, profiles); + } + + private Root_meta_external_query_sql_transformation_queryToPure_PlanGenerationResult planResult(Root_meta_external_query_sql_transformation_queryToPure_SqlTransformContext transformedContext, PureModel pureModel, RichIterable sources) + { + return core_external_query_sql_binding_fromPure_fromPure.Root_meta_external_query_sql_transformation_queryToPure_getPlanResult_SqlTransformContext_1__SQLSource_MANY__Extension_MANY__PlanGenerationResult_1_(transformedContext, sources, routerExtensions.apply(pureModel), pureModel.getExecutionSupport()); + } + + private T process(Query query, Function3, T> func, SQLContext context, MutableList profiles) + { + return process(query, func, (sources, extensions, pureModel) -> core_external_query_sql_binding_fromPure_fromPure.Root_meta_external_query_sql_transformation_queryToPure_rootContext_SQLSource_MANY__Extension_MANY__SqlTransformContext_1_(sources, extensions, pureModel.getExecutionSupport()), context, profiles); + } + + private T process(Query query, + Function3, T> func, + Function3, RichIterable, PureModel, Root_meta_external_query_sql_transformation_queryToPure_SqlTransformContext> transformContextFunc, + SQLContext context, + MutableList profiles) + { + Pair, PureModelContext> sqlSourcesAndPureModel = getSourcesAndModel(query, context, profiles); + RichIterable sources = sqlSourcesAndPureModel.getOne(); + PureModelContext pureModelContext = sqlSourcesAndPureModel.getTwo(); + + PureModel pureModel = modelManager.loadModel(pureModelContext, PureClientVersions.production, profiles, ""); + + Root_meta_external_query_sql_metamodel_Query compiledQuery = new ProtocolToMetamodelTranslator().translate(query, pureModel); + + RichIterable compiledSources = new SQLSourceTranslator().translate(sources, pureModel); + LOGGER.info("{}", new LogInfo(profiles, LoggingEventType.GENERATE_PLAN_START)); + + Root_meta_external_query_sql_transformation_queryToPure_SqlTransformContext transformedContext = core_external_query_sql_binding_fromPure_fromPure.Root_meta_external_query_sql_transformation_queryToPure_processRootQuery_Query_1__SqlTransformContext_1__SqlTransformContext_1_( + compiledQuery, transformContextFunc.value(compiledSources, routerExtensions.apply(pureModel), pureModel), pureModel.getExecutionSupport()); + + return func.value(transformedContext, pureModel, compiledSources); + } + + private Pair, PureModelContext> getSourcesAndModel(Query query, SQLContext context, MutableList profiles) + { + Set tables = new TableSourceExtractor().visit(query); + + MutableMultimap grouped = Iterate.groupBy(tables, TableSource::getType); + + boolean schemasValid = Iterate.allSatisfy(grouped.keySet(), providers::containsKey); + + if (!schemasValid) + { + throw new IllegalArgumentException("Unsupported schema types " + String.join(", ", grouped.keySet().select(k -> !providers.containsKey(k)))); + } + + RichIterable resolved = grouped.keySet().collect(k -> resolve(grouped.get(k), context, providers.get(k), profiles)); + + MutableList allContexts = IterableIterate.flatCollect(resolved, SQLSourceResolvedContext::getPureModelContexts); + boolean allCompatiblePointers = allContexts.allSatisfy(p -> p instanceof PureModelContextPointer && allContexts.allSatisfy(p2 -> ((PureModelContextPointer) p).safeEqual(p, p2))); + + PureModelContext pureModelContext; + + //this means all pointers are from same source, so we can combine to utilise model cache. + if (allCompatiblePointers) + { + pureModelContext = allContexts.collectIf(p -> p instanceof PureModelContextPointer, p -> ((PureModelContextPointer) p)) + .injectInto(null, (d, e) -> e.combine((PureModelContextPointer) d)); + } + else + { + pureModelContext = resolved.injectInto(PureModelContextData.newPureModelContextData(), (p, p2) -> PureModelContextData.combine(p, PureModelContextData.newPureModelContextData(), ListIterate.collect(p2.getPureModelContexts(), c -> modelManager.loadData(c, PureClientVersions.production, profiles)).toArray(new PureModelContextData[]{}))); + } + RichIterable sources = resolved.flatCollect(SQLSourceResolvedContext::getSources); + return Tuples.pair(sources, pureModelContext); + } + + + private SQLSourceResolvedContext resolve(MutableCollection tables, SQLContext context, SQLSourceProvider extension, MutableList profiles) + { + return extension.resolve(tables.toList(), context, profiles); + } + + static String serializeToJSON(Object pureObject, PureModel pureModel, Boolean alloyJSON) + { + return alloyJSON + ? org.finos.legend.pure.generated.core_pure_protocol_protocol.Root_meta_alloy_metadataServer_alloyToJSON_Any_1__String_1_(pureObject, pureModel.getExecutionSupport()) + : core_external_format_json_toJSON.Root_meta_json_toJSON_Any_MANY__Integer_$0_1$__Config_1__String_1_( + Lists.mutable.with(pureObject), + 1000L, + core_external_format_json_toJSON.Root_meta_json_config_Boolean_1__Boolean_1__Boolean_1__Boolean_1__Config_1_(true, false, false, false, pureModel.getExecutionSupport()), + pureModel.getExecutionSupport() + ); + } + + private Lambda transformLambda(LambdaFunction lambda, PureModel pureModel) + { + Object protocol = transformToVersionedModel(lambda, PureClientVersions.production, routerExtensions.apply(pureModel), pureModel.getExecutionSupport()); + return transform(protocol, Lambda.class, pureModel); + } + + private T transform(Object object, java.lang.Class clazz, PureModel pureModel) + { + String json = serializeToJSON(object, pureModel, true); + try + { + return ObjectMapperFactory.getNewStandardObjectMapperWithPureProtocolExtensionSupports().readValue(json, clazz); + } + catch (Exception e) + { + throw new EngineException(e.getMessage()); + } + } + + public Object transformToVersionedModel(FunctionDefinition lambda, String version, RichIterable extensions, ExecutionSupport executionSupport) + { + try + { + Class cl = Class.forName("org.finos.legend.pure.generated.core_pure_protocol_" + version + "_transfers_valueSpecification"); + Method method = cl.getMethod("Root_meta_protocols_pure_" + version + "_transformation_fromPureGraph_transformLambda_FunctionDefinition_1__Extension_MANY__Lambda_1_", FunctionDefinition.class, RichIterable.class, org.finos.legend.pure.m3.execution.ExecutionSupport.class); + return method.invoke(null, lambda, extensions, executionSupport); + } + catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) + { + throw new RuntimeException(e); + } + } +} diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/execute/SqlExecute.java b/legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/execute/SqlExecute.java index 220aba69785..9d463443298 100644 --- a/legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/execute/SqlExecute.java +++ b/legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/execute/SqlExecute.java @@ -21,74 +21,29 @@ import io.swagger.annotations.ApiParam; import org.eclipse.collections.api.RichIterable; import org.eclipse.collections.api.block.function.Function; -import org.eclipse.collections.api.block.function.Function3; -import org.eclipse.collections.api.collection.MutableCollection; -import org.eclipse.collections.api.factory.Lists; import org.eclipse.collections.api.factory.Maps; import org.eclipse.collections.api.list.MutableList; -import org.eclipse.collections.api.map.MutableMap; -import org.eclipse.collections.api.multimap.MutableMultimap; -import org.eclipse.collections.api.tuple.Pair; -import org.eclipse.collections.impl.map.mutable.UnifiedMap; -import org.eclipse.collections.impl.tuple.Tuples; -import org.eclipse.collections.impl.utility.Iterate; -import org.eclipse.collections.impl.utility.ListIterate; -import org.eclipse.collections.impl.utility.internal.IterableIterate; import org.finos.legend.engine.language.pure.compiler.toPureGraph.PureModel; import org.finos.legend.engine.language.pure.modelManager.ModelManager; import org.finos.legend.engine.language.sql.grammar.from.SQLGrammarParser; import org.finos.legend.engine.plan.execution.PlanExecutor; -import org.finos.legend.engine.plan.execution.result.ConstantResult; import org.finos.legend.engine.plan.execution.result.Result; import org.finos.legend.engine.plan.execution.result.serialization.SerializationFormat; import org.finos.legend.engine.plan.generation.transformers.PlanTransformer; -import org.finos.legend.engine.plan.platform.PlanPlatform; -import org.finos.legend.engine.protocol.pure.PureClientVersions; -import org.finos.legend.engine.protocol.pure.v1.model.context.PureModelContext; -import org.finos.legend.engine.protocol.pure.v1.model.context.PureModelContextData; -import org.finos.legend.engine.protocol.pure.v1.model.context.PureModelContextPointer; -import org.finos.legend.engine.protocol.pure.v1.model.executionPlan.SingleExecutionPlan; +import org.finos.legend.engine.protocol.pure.v1.model.executionPlan.ExecutionPlan; import org.finos.legend.engine.protocol.pure.v1.model.valueSpecification.raw.Lambda; -import org.finos.legend.engine.protocol.sql.metamodel.ProtocolToMetamodelTranslator; import org.finos.legend.engine.protocol.sql.metamodel.Query; -import org.finos.legend.engine.protocol.sql.metamodel.Statement; +import org.finos.legend.engine.protocol.sql.schema.metamodel.Schema; +import org.finos.legend.engine.query.sql.api.SQLExecutor; import org.finos.legend.engine.query.sql.api.sources.SQLContext; -import org.finos.legend.engine.query.sql.api.sources.SQLSource; import org.finos.legend.engine.query.sql.api.sources.SQLSourceProvider; -import org.finos.legend.engine.query.sql.api.sources.SQLSourceResolvedContext; -import org.finos.legend.engine.query.sql.api.sources.SQLSourceTranslator; -import org.finos.legend.engine.query.sql.api.sources.TableSource; -import org.finos.legend.engine.query.sql.api.sources.TableSourceExtractor; -import org.finos.legend.engine.shared.core.ObjectMapperFactory; import org.finos.legend.engine.shared.core.kerberos.ProfileManagerHelper; -import org.finos.legend.engine.shared.core.operational.errorManagement.EngineException; -import org.finos.legend.engine.shared.core.operational.logs.LogInfo; import org.finos.legend.engine.shared.core.operational.logs.LoggingEventType; -import org.finos.legend.engine.shared.core.operational.prometheus.MetricsHandler; -import org.finos.legend.pure.generated.Root_meta_external_query_sql_Schema; -import org.finos.legend.pure.generated.Root_meta_external_query_sql_metamodel_Node; -import org.finos.legend.pure.generated.Root_meta_external_query_sql_metamodel_Query; -import org.finos.legend.pure.generated.Root_meta_external_query_sql_transformation_queryToPure_PlanGenerationResult; -import org.finos.legend.pure.generated.Root_meta_external_query_sql_transformation_queryToPure_SQLSource; -import org.finos.legend.pure.generated.Root_meta_external_query_sql_transformation_queryToPure_SqlTransformContext; -import org.finos.legend.pure.generated.Root_meta_pure_executionPlan_ExecutionPlan; import org.finos.legend.pure.generated.Root_meta_pure_extension_Extension; -import org.finos.legend.pure.generated.core_external_format_json_toJSON; -import org.finos.legend.pure.generated.core_external_query_sql_binding_fromPure_fromPure; -import org.finos.legend.pure.generated.core_pure_tools_tools_extension; -import org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.function.FunctionDefinition; -import org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.function.LambdaFunction; -import org.finos.legend.pure.m3.execution.ExecutionSupport; import org.pac4j.core.profile.CommonProfile; import org.pac4j.core.profile.ProfileManager; import org.pac4j.jax.rs.annotations.Pac4JProfileManager; -import org.slf4j.Logger; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.List; -import java.util.Map; -import java.util.Set; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.Consumes; import javax.ws.rs.DefaultValue; @@ -100,34 +55,31 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; +import java.util.List; import static org.finos.legend.engine.plan.execution.api.result.ResultManager.manageResult; -import static org.finos.legend.engine.plan.generation.PlanGenerator.transformExecutionPlan; @Api(tags = "SQL - Execution") @Path("sql/v1/execution") @Produces(MediaType.APPLICATION_JSON) public class SqlExecute { + private static final SQLGrammarParser PARSER = SQLGrammarParser.newInstance(); - private static final Logger LOGGER = org.slf4j.LoggerFactory.getLogger(SqlExecute.class); - private static final SQLGrammarParser parser = SQLGrammarParser.newInstance(); - private final ModelManager modelManager; - private final PlanExecutor planExecutor; - private final Function> routerExtensions; - private final Iterable transformers; - private final MutableMap providers; + private final SQLExecutor executor; + @Deprecated public SqlExecute(ModelManager modelManager, PlanExecutor planExecutor, Function> routerExtensions, List providers, Iterable transformers) { - this.modelManager = modelManager; - this.planExecutor = planExecutor; - this.routerExtensions = routerExtensions; - this.transformers = transformers; - this.providers = ListIterate.groupByUniqueKey(providers, SQLSourceProvider::getType); + this(new SQLExecutor(modelManager, planExecutor, routerExtensions, providers, transformers)); + } + + public SqlExecute(SQLExecutor executor) + { + this.executor = executor; } @POST @@ -137,7 +89,7 @@ public SqlExecute(ModelManager modelManager, PlanExecutor planExecutor, public Response executeSql(@Context HttpServletRequest request, String sql, @DefaultValue(SerializationFormat.defaultFormatString) @QueryParam("serializationFormat")SerializationFormat format, @ApiParam(hidden = true) @Pac4JProfileManager ProfileManager pm, @Context UriInfo uriInfo) { - Query query = (Query) parser.parseStatement(sql); + Query query = parseSQL(sql); return executeSql(request, query, format, pm, uriInfo); } @@ -149,14 +101,10 @@ public Response executeSql(@Context HttpServletRequest request, Query query, @De @ApiParam(hidden = true) @Pac4JProfileManager ProfileManager pm, @Context UriInfo uriInfo) { MutableList profiles = ProfileManagerHelper.extractProfiles(pm); - SQLContext context = new SQLContext(query, Maps.mutable.of()); - long start = System.currentTimeMillis(); - LOGGER.info(new LogInfo(profiles, LoggingEventType.EXECUTE_INTERACTIVE_STOP, (double) System.currentTimeMillis() - start).toString()); - Result result = execute(query, request.getRemoteUser(), context, profiles); + Result result = this.executor.execute(query, request.getRemoteUser(), context, profiles); - MetricsHandler.observe("execute", start, System.currentTimeMillis()); try (Scope ignored = GlobalTracer.get().buildSpan("Manage Results").startActive(true)) { return manageResult(profiles, result, format, LoggingEventType.EXECUTE_INTERACTIVE_ERROR); @@ -169,7 +117,7 @@ public Response executeSql(@Context HttpServletRequest request, Query query, @De @Consumes({MediaType.TEXT_PLAIN}) public Lambda generateLambda(@Context HttpServletRequest request, String sql, @ApiParam(hidden = true) @Pac4JProfileManager ProfileManager pm, @Context UriInfo uriInfo) { - Query query = (Query) parser.parseStatement(sql); + Query query = parseSQL(sql); return generateLambda(request, query, pm, uriInfo); } @@ -182,16 +130,16 @@ public Lambda generateLambda(@Context HttpServletRequest request, Query query, @ MutableList profiles = ProfileManagerHelper.extractProfiles(pm); SQLContext context = new SQLContext(query, Maps.mutable.of()); - return lambda(query, context, profiles); + return executor.lambda(query, context, profiles); } @POST @ApiOperation(value = "Generate plans for a SQL query using sql string") @Path("generatePlanQueryString") @Consumes({MediaType.TEXT_PLAIN}) - public Response generatePlan(@Context HttpServletRequest request, String sql, @ApiParam(hidden = true) @Pac4JProfileManager ProfileManager pm, @Context UriInfo uriInfo) + public ExecutionPlan generatePlan(@Context HttpServletRequest request, String sql, @ApiParam(hidden = true) @Pac4JProfileManager ProfileManager pm, @Context UriInfo uriInfo) { - Query query = (Query) parser.parseStatement(sql); + Query query = parseSQL(sql); return generatePlan(request, query, pm, uriInfo); } @@ -199,210 +147,45 @@ public Response generatePlan(@Context HttpServletRequest request, String sql, @A @ApiOperation(value = "Generate plans for a SQL query using protocol model") @Path("generatePlanQuery") @Consumes({MediaType.APPLICATION_JSON}) - public Response generatePlan(@Context HttpServletRequest request, Query query, @ApiParam(hidden = true) @Pac4JProfileManager ProfileManager pm, @Context UriInfo uriInfo) + public ExecutionPlan generatePlan(@Context HttpServletRequest request, Query query, @ApiParam(hidden = true) @Pac4JProfileManager ProfileManager pm, @Context UriInfo uriInfo) { MutableList profiles = ProfileManagerHelper.extractProfiles(pm); SQLContext context = new SQLContext(query, Maps.mutable.of()); - SingleExecutionPlan singleExecutionPlan = plan(query, context, profiles); - return Response.ok().type(MediaType.APPLICATION_JSON_TYPE).entity(singleExecutionPlan).build(); + return this.executor.plan(query, context, profiles); } @POST - @ApiOperation(value = "Get schema for a SQL query in the context of a Mapping and a Runtime from a SDLC project") + @ApiOperation(value = "Get schema for a SQL query") @Path("getSchemaFromQueryString") @Consumes({MediaType.TEXT_PLAIN}) - public Response getSchema(@Context HttpServletRequest request, String sql, @ApiParam(hidden = true) @Pac4JProfileManager ProfileManager pm, @Context UriInfo uriInfo) + public Schema getSchema(@Context HttpServletRequest request, String sql, @ApiParam(hidden = true) @Pac4JProfileManager ProfileManager pm, @Context UriInfo uriInfo) { - Query query = (Query) parser.parseStatement(sql); + Query query = parseSQL(sql); return getSchema(request, query, pm, uriInfo); } @POST - @ApiOperation(value = "Get schema for a SQL query in the context of a Mapping and a Runtime from a SDLC project") + @ApiOperation(value = "Get schema for a SQL query") @Path("getSchemaFromQuery") @Consumes({MediaType.APPLICATION_JSON}) - public Response getSchema(@Context HttpServletRequest request, Query query, @ApiParam(hidden = true) @Pac4JProfileManager ProfileManager pm, @Context UriInfo uriInfo) + public Schema getSchema(@Context HttpServletRequest request, Query query, @ApiParam(hidden = true) @Pac4JProfileManager ProfileManager pm, @Context UriInfo uriInfo) { MutableList profiles = ProfileManagerHelper.extractProfiles(pm); - - String schema = schema(query, profiles); - return Response.ok().type(MediaType.APPLICATION_JSON_TYPE).entity(schema).build(); - } - - private Lambda lambda(Query query, SQLContext context, MutableList profiles) - { - return process(query, - (transformedContext, pureModel, sources) -> transformLambda(transformedContext.lambda(pureModel.getExecutionSupport()), pureModel), - (sources, extensions, pureModel) -> core_external_query_sql_binding_fromPure_fromPure.Root_meta_external_query_sql_transformation_queryToPure_rootContext_SQLSource_MANY__Extension_MANY__SqlTransformContext_1_(sources, extensions, pureModel.getExecutionSupport())._scopeWithFrom(false), - context, profiles); + return this.executor.schema(query, profiles); } - private String schema(Query query, MutableList profiles) - { - SQLContext context = new SQLContext(query, Maps.mutable.of()); - return process(query, (t, pm, sources) -> - { - Root_meta_external_query_sql_Schema schema = core_external_query_sql_binding_fromPure_fromPure.Root_meta_external_query_sql_transformation_queryToPure_getSchema_SqlTransformContext_1__Schema_1_(t, pm.getExecutionSupport()); - return serializeToJSON(schema, pm, false); - }, context, profiles); - } - - private Root_meta_external_query_sql_transformation_queryToPure_PlanGenerationResult planResult(Root_meta_external_query_sql_transformation_queryToPure_SqlTransformContext transformedContext, PureModel pureModel, RichIterable sources) - { - return core_external_query_sql_binding_fromPure_fromPure.Root_meta_external_query_sql_transformation_queryToPure_getPlanResult_SqlTransformContext_1__SQLSource_MANY__Extension_MANY__PlanGenerationResult_1_(transformedContext, sources, routerExtensions.apply(pureModel), pureModel.getExecutionSupport()); - } - - private SingleExecutionPlan plan(Query query, SQLContext context, MutableList profiles) - { - return process(query, (transformedContext, pureModel, sources) -> transformExecutionPlan(planResult(transformedContext, pureModel, sources)._plan(), pureModel, PureClientVersions.production, profiles, routerExtensions.apply(pureModel), transformers), context, profiles); - } - - private Result execute(Query query, String user, SQLContext context, MutableList profiles) - { - return process(query, (transformedContext, pureModel, sources) -> - { - Root_meta_external_query_sql_transformation_queryToPure_PlanGenerationResult plans = planResult(transformedContext, pureModel, sources); - - Map arguments = UnifiedMap.newMapWith(IterableIterate.collectIf(plans._arguments(), p -> p._value() != null || p._plan() != null, p -> - { - Result result; - - if (p._value() != null) - { - Object value = p._value() instanceof org.finos.legend.pure.m3.coreinstance.meta.pure.functions.collection.List - ? ((org.finos.legend.pure.m3.coreinstance.meta.pure.functions.collection.List) p._value())._values() - : p._value(); - result = new ConstantResult(value); - } - else - { - Root_meta_pure_executionPlan_ExecutionPlan l = PlanPlatform.JAVA.bindPlan(p._plan(), null, pureModel, routerExtensions.apply(pureModel)); - SingleExecutionPlan m = transformExecutionPlan(l, pureModel, PureClientVersions.production, profiles, routerExtensions.apply(pureModel), transformers); - result = planExecutor.execute(m, Maps.mutable.empty(), "pentej", profiles); - } - - return Tuples.pair(p._name(), result); - })); - - Root_meta_pure_executionPlan_ExecutionPlan plan = plans._plan(); - plan = PlanPlatform.JAVA.bindPlan(plan, null, pureModel, routerExtensions.apply(pureModel)); - SingleExecutionPlan transformedPlan = transformExecutionPlan(plan, pureModel, PureClientVersions.production, profiles, routerExtensions.apply(pureModel), transformers); - - return planExecutor.execute(transformedPlan, arguments, user, profiles); - }, context, profiles); - } - - private T process(Query query, Function3, T> func, SQLContext context, MutableList profiles) - { - return process(query, func, (sources, extensions, pureModel) -> core_external_query_sql_binding_fromPure_fromPure.Root_meta_external_query_sql_transformation_queryToPure_rootContext_SQLSource_MANY__Extension_MANY__SqlTransformContext_1_(sources, extensions, pureModel.getExecutionSupport()), context, profiles); - } - - private T process(Query query, - Function3, T> func, - Function3, RichIterable, PureModel, Root_meta_external_query_sql_transformation_queryToPure_SqlTransformContext> transformContextFunc, - SQLContext context, - MutableList profiles) - { - Pair, PureModelContext> sqlSourcesAndPureModel = getSourcesAndModel(query, context, profiles); - RichIterable sources = sqlSourcesAndPureModel.getOne(); - PureModelContext pureModelContext = sqlSourcesAndPureModel.getTwo(); - - PureModel pureModel = modelManager.loadModel(pureModelContext, PureClientVersions.production, profiles, ""); - - Root_meta_external_query_sql_metamodel_Query compiledQuery = new ProtocolToMetamodelTranslator().translate(query, pureModel); - - RichIterable compiledSources = new SQLSourceTranslator().translate(sources, pureModel); - LOGGER.info("{}", new LogInfo(profiles, LoggingEventType.GENERATE_PLAN_START)); - - Root_meta_external_query_sql_transformation_queryToPure_SqlTransformContext transformedContext = core_external_query_sql_binding_fromPure_fromPure.Root_meta_external_query_sql_transformation_queryToPure_processRootQuery_Query_1__SqlTransformContext_1__SqlTransformContext_1_( - compiledQuery, transformContextFunc.value(compiledSources, routerExtensions.apply(pureModel), pureModel), pureModel.getExecutionSupport()); - - return func.value(transformedContext, pureModel, compiledSources); - } - - private Pair, PureModelContext> getSourcesAndModel(Query query, SQLContext context, MutableList profiles) - { - Set tables = new TableSourceExtractor().visit(query); - - MutableMultimap grouped = Iterate.groupBy(tables, TableSource::getType); - - boolean schemasValid = Iterate.allSatisfy(grouped.keySet(), providers::containsKey); - - if (!schemasValid) - { - throw new IllegalArgumentException("Unsupported schema types " + String.join(", ", grouped.keySet().select(k -> !providers.containsKey(k)))); - } - - RichIterable resolved = grouped.keySet().collect(k -> resolve(grouped.get(k), context, providers.get(k), profiles)); - - MutableList allContexts = IterableIterate.flatCollect(resolved, SQLSourceResolvedContext::getPureModelContexts); - boolean allCompatiblePointers = allContexts.allSatisfy(p -> p instanceof PureModelContextPointer && allContexts.allSatisfy(p2 -> ((PureModelContextPointer) p).safeEqual(p, p2))); - - PureModelContext pureModelContext; - - //this means all pointers are from same source, so we can combine to utilise model cache. - if (allCompatiblePointers) - { - pureModelContext = allContexts.collectIf(p -> p instanceof PureModelContextPointer, p -> ((PureModelContextPointer) p)) - .injectInto(null, (d, e) -> e.combine((PureModelContextPointer) d)); - } - else - { - pureModelContext = resolved.injectInto(PureModelContextData.newPureModelContextData(), (p, p2) -> PureModelContextData.combine(p, PureModelContextData.newPureModelContextData(), ListIterate.collect(p2.getPureModelContexts(), c -> modelManager.loadData(c, PureClientVersions.production, profiles)).toArray(new PureModelContextData[]{}))); - } - RichIterable sources = resolved.flatCollect(SQLSourceResolvedContext::getSources); - return Tuples.pair(sources, pureModelContext); - } - - - private SQLSourceResolvedContext resolve(MutableCollection tables, SQLContext context, SQLSourceProvider extension, MutableList profiles) - { - return extension.resolve(tables.toList(), context, profiles); - } - - static String serializeToJSON(Object pureObject, PureModel pureModel, Boolean alloyJSON) - { - return alloyJSON - ? org.finos.legend.pure.generated.core_pure_protocol_protocol.Root_meta_alloy_metadataServer_alloyToJSON_Any_1__String_1_(pureObject, pureModel.getExecutionSupport()) - : core_external_format_json_toJSON.Root_meta_json_toJSON_Any_MANY__Integer_$0_1$__Config_1__String_1_( - Lists.mutable.with(pureObject), - 1000L, - core_external_format_json_toJSON.Root_meta_json_config_Boolean_1__Boolean_1__Boolean_1__Boolean_1__Config_1_(true, false, false, false, pureModel.getExecutionSupport()), - pureModel.getExecutionSupport() - ); - } - - private Lambda transformLambda(LambdaFunction lambda, PureModel pureModel) - { - Object protocol = transformToVersionedModel(lambda, PureClientVersions.production, routerExtensions.apply(pureModel), pureModel.getExecutionSupport()); - return transform(protocol, Lambda.class, pureModel); - } - - private T transform(Object object, java.lang.Class clazz, PureModel pureModel) + @POST + @ApiOperation(value = "Parse SQL to metamodel") + @Path("parseToMetamodel") + @Consumes({MediaType.TEXT_PLAIN}) + public Query parseToMetamodel(@Context HttpServletRequest request, String sql, @ApiParam(hidden = true) @Pac4JProfileManager ProfileManager pm, @Context UriInfo uriInfo) { - String json = serializeToJSON(object, pureModel, true); - try - { - return ObjectMapperFactory.getNewStandardObjectMapperWithPureProtocolExtensionSupports().readValue(json, clazz); - } - catch (Exception e) - { - throw new EngineException(e.getMessage()); - } + return parseSQL(sql); } - public Object transformToVersionedModel(FunctionDefinition lambda, String version, RichIterable extensions, ExecutionSupport executionSupport) + private Query parseSQL(String sql) { - try - { - Class cl = Class.forName("org.finos.legend.pure.generated.core_pure_protocol_" + version + "_transfers_valueSpecification"); - Method method = cl.getMethod("Root_meta_protocols_pure_" + version + "_transformation_fromPureGraph_transformLambda_FunctionDefinition_1__Extension_MANY__Lambda_1_", FunctionDefinition.class, RichIterable.class, org.finos.legend.pure.m3.execution.ExecutionSupport.class); - return method.invoke(null, lambda, extensions, executionSupport); - } - catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) - { - throw new RuntimeException(e); - } + return (Query) PARSER.parseStatement(sql); } } \ No newline at end of file diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-query/src/test/java/org/finos/legend/engine/query/sql/api/execute/SqlExecuteTest.java b/legend-engine-xts-sql/legend-engine-xt-sql-query/src/test/java/org/finos/legend/engine/query/sql/api/execute/SqlExecuteTest.java index 4dc4d7f0815..408501ec7c3 100644 --- a/legend-engine-xts-sql/legend-engine-xt-sql-query/src/test/java/org/finos/legend/engine/query/sql/api/execute/SqlExecuteTest.java +++ b/legend-engine-xts-sql/legend-engine-xt-sql-query/src/test/java/org/finos/legend/engine/query/sql/api/execute/SqlExecuteTest.java @@ -30,21 +30,18 @@ import org.finos.legend.engine.plan.execution.result.serialization.SerializationFormat; import org.finos.legend.engine.plan.generation.extension.PlanGeneratorExtension; import org.finos.legend.engine.protocol.pure.PureClientVersions; -import org.finos.legend.engine.pure.code.core.PureCoreExtensionLoader; import org.finos.legend.engine.protocol.pure.v1.model.valueSpecification.raw.Lambda; +import org.finos.legend.engine.protocol.sql.schema.metamodel.Enum; +import org.finos.legend.engine.protocol.sql.schema.metamodel.EnumSchemaColumn; +import org.finos.legend.engine.protocol.sql.schema.metamodel.PrimitiveSchemaColumn; +import org.finos.legend.engine.protocol.sql.schema.metamodel.PrimitiveType; +import org.finos.legend.engine.protocol.sql.schema.metamodel.Schema; +import org.finos.legend.engine.pure.code.core.PureCoreExtensionLoader; import org.finos.legend.engine.query.sql.api.CatchAllExceptionMapper; import org.finos.legend.engine.query.sql.api.MockPac4jFeature; import org.finos.legend.engine.query.sql.api.sources.TestSQLSourceProvider; import org.finos.legend.engine.shared.core.api.grammar.RenderStyle; import org.finos.legend.engine.shared.core.deployment.DeploymentMode; -import org.finos.legend.pure.generated.Root_meta_external_query_sql_Enum; -import org.finos.legend.pure.generated.Root_meta_external_query_sql_EnumValueSchemaColumn_Impl; -import org.finos.legend.pure.generated.Root_meta_external_query_sql_Enum_Impl; -import org.finos.legend.pure.generated.Root_meta_external_query_sql_PrimitiveValueSchemaColumn_Impl; -import org.finos.legend.pure.generated.Root_meta_external_query_sql_Schema; -import org.finos.legend.pure.generated.Root_meta_external_query_sql_SchemaColumn; -import org.finos.legend.pure.generated.Root_meta_external_query_sql_Schema_Impl; -import org.finos.legend.pure.generated.Root_meta_pure_metamodel_type_Enum_Impl; import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory; import org.junit.Assert; import org.junit.ClassRule; @@ -201,52 +198,88 @@ public void testExecuteWithCSVFormat() } @Test - public void getSchemaForQueryWithDuplicateSources() + public void getSchemaForQueryWithDuplicateSources() throws JsonProcessingException { String actualSchema = resources.target("sql/v1/execution/getSchemaFromQueryString") .request() .post(Entity.text("SELECT Id FROM service.\"/testService\" UNION SELECT Id FROM service.\"/testService\"")).readEntity(String.class); - org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.type.Enum integerType = new Root_meta_pure_metamodel_type_Enum_Impl("Integer"); - Root_meta_external_query_sql_SchemaColumn idColumn = new Root_meta_external_query_sql_PrimitiveValueSchemaColumn_Impl((String) null)._name("Id")._type(integerType); - Root_meta_external_query_sql_Schema schema = new Root_meta_external_query_sql_Schema_Impl((String) null)._columnsAdd(idColumn); - String expectedSchema = SqlExecute.serializeToJSON(schema, pureModel, false); - Assert.assertEquals(expectedSchema, actualSchema); + Schema schema = new Schema(); + schema.columns = FastList.newListWith( + primitiveColumn("Id", PrimitiveType.Integer) + ); + + Assert.assertEquals(new ObjectMapper().writeValueAsString(schema), actualSchema); } @Test - public void getSchemaFromQueryString() + public void getSchemaFromQueryString() throws JsonProcessingException { String actualSchema = resources.target("sql/v1/execution/getSchemaFromQueryString") .request() .post(Entity.text("SELECT * FROM service.\"/testService\"")).readEntity(String.class); - org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.type.Enum integerType = new Root_meta_pure_metamodel_type_Enum_Impl("Integer"); - org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.type.Enum stringType = new Root_meta_pure_metamodel_type_Enum_Impl("String"); - Root_meta_external_query_sql_SchemaColumn idColumn = new Root_meta_external_query_sql_PrimitiveValueSchemaColumn_Impl((String) null)._name("Id")._type(integerType); - Root_meta_external_query_sql_SchemaColumn nameColumn = new Root_meta_external_query_sql_PrimitiveValueSchemaColumn_Impl((String) null)._name("Name")._type(stringType); - Root_meta_external_query_sql_SchemaColumn typeColumn = new Root_meta_external_query_sql_EnumValueSchemaColumn_Impl((String) null)._name("Employee Type")._type("demo::employeeType"); - Root_meta_external_query_sql_Enum employeeType = new Root_meta_external_query_sql_Enum_Impl((String) null)._type("demo::employeeType")._valuesAdd("Type1")._valuesAdd("Type2"); - Root_meta_external_query_sql_Schema schema = new Root_meta_external_query_sql_Schema_Impl((String) null)._columnsAdd(idColumn)._columnsAdd(nameColumn)._columnsAdd(typeColumn)._enumsAdd(employeeType); - String expectedSchema = SqlExecute.serializeToJSON(schema, pureModel, false); - Assert.assertEquals(expectedSchema, actualSchema); + + Schema schema = new Schema(); + schema.columns = FastList.newListWith( + primitiveColumn("Id", PrimitiveType.Integer), + primitiveColumn("Name", PrimitiveType.String), + enumColumn("Employee Type", "demo::employeeType") + ); + + schema.enums = FastList.newListWith( + enumValue("demo::employeeType", "Type1", "Type2") + ); + + Assert.assertEquals(new ObjectMapper().writeValueAsString(schema), actualSchema); } @Test - public void getSchemaFromQuery() + public void getSchemaFromQuery() throws JsonProcessingException { String actualSchema = resources.target("sql/v1/execution/getSchemaFromQuery") .request() .post(Entity.json("{ \"_type\": \"query\", \"orderBy\": [], \"queryBody\": { \"_type\": \"querySpecification\", \"from\": [ { \"_type\": \"table\", \"name\": { \"parts\": [ \"service\", \"/testService\" ] } } ], \"groupBy\": [], \"orderBy\": [], \"select\": { \"_type\": \"select\", \"distinct\": false, \"selectItems\": [ { \"_type\": \"allColumns\" } ] } } }")).readEntity(String.class); - org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.type.Enum integerType = new Root_meta_pure_metamodel_type_Enum_Impl("Integer"); - org.finos.legend.pure.m3.coreinstance.meta.pure.metamodel.type.Enum stringType = new Root_meta_pure_metamodel_type_Enum_Impl("String"); - Root_meta_external_query_sql_SchemaColumn idColumn = new Root_meta_external_query_sql_PrimitiveValueSchemaColumn_Impl((String) null)._name("Id")._type(integerType); - Root_meta_external_query_sql_SchemaColumn nameColumn = new Root_meta_external_query_sql_PrimitiveValueSchemaColumn_Impl((String) null)._name("Name")._type(stringType); - Root_meta_external_query_sql_SchemaColumn typeColumn = new Root_meta_external_query_sql_EnumValueSchemaColumn_Impl((String) null)._name("Employee Type")._type("demo::employeeType"); - Root_meta_external_query_sql_Enum employeeType = new Root_meta_external_query_sql_Enum_Impl((String) null)._type("demo::employeeType")._valuesAdd("Type1")._valuesAdd("Type2"); - Root_meta_external_query_sql_Schema schema = new Root_meta_external_query_sql_Schema_Impl((String) null)._columnsAdd(idColumn)._columnsAdd(nameColumn)._columnsAdd(typeColumn)._enumsAdd(employeeType); - String expectedSchema = SqlExecute.serializeToJSON(schema, pureModel, false); - Assert.assertEquals(expectedSchema, actualSchema); + + Schema schema = new Schema(); + schema.columns = FastList.newListWith( + primitiveColumn("Id", PrimitiveType.Integer), + primitiveColumn("Name", PrimitiveType.String), + enumColumn("Employee Type", "demo::employeeType") + ); + + schema.enums = FastList.newListWith( + enumValue("demo::employeeType", "Type1", "Type2") + ); + + Assert.assertEquals(new ObjectMapper().writeValueAsString(schema), actualSchema); + } + + private static PrimitiveSchemaColumn primitiveColumn(String name, PrimitiveType type) + { + PrimitiveSchemaColumn c = new PrimitiveSchemaColumn(); + c.name = name; + c.type = type; + + return c; + } + + private static EnumSchemaColumn enumColumn(String name, String type) + { + EnumSchemaColumn c = new EnumSchemaColumn(); + c.name = name; + c.type = type;; + + return c; + } + + private static Enum enumValue(String type, String... values) + { + Enum e = new Enum(); + e.type = type; + e.values = FastList.newListWith(values); + + return e; } }