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 ec5bd2c906c..0c15c50e26e 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 @@ -286,7 +286,7 @@ function <> meta::external::query::sql::transformation::queryToP | ^$query(expression = processRestrict($expected.alias, $renamed)), | $renamed); - let aliases = $final.columns.name->map(c | ^SQLColumnAlias(name=$c)); + let aliases = $final.columns.name->map(c | ^SQLColumnAlias(name = $c)); ^$final(aliases = $aliases); } @@ -815,7 +815,9 @@ function <> meta::external::query::sql::transformation::queryToP let right = wrapWithFrom($rightRenamed); //all of the child contexts are in scope for joining on so the new context needs to know about them - let newContext = ^$context(contexts = $left->concatenate($left.contexts)->concatenate($right)->concatenate($right.contexts)); + let leftContexts = if ($join.left->instanceOf(Join), | $left.contexts, | $left); + let rightContexts = if ($join.right->instanceOf(Join), | $right.contexts, | $right); + let newContext = ^$context(contexts = $leftContexts->concatenate($rightContexts)); let row1 = ^VariableExpression(multiplicity = PureOne, name = 'row1', genericType = ^GenericType(rawType = TDSRow)); let row2 = ^VariableExpression(multiplicity = PureOne, name = 'row2', genericType = ^GenericType(rawType = TDSRow)); @@ -2349,7 +2351,7 @@ function <> meta::external::query::sql::transformation::queryToP assert($simplified.type == ArithmeticType.ADD || $simplified.type == ArithmeticType.SUBTRACT, | 'arithmetic type ' + $simplified.type.name + ' not currently supported for dates'); //note we are making assumption here that Any is fine. This results from a function call that is generic return type (e.g. max) - assert($leftTypeNormalized == Date || $leftTypeNormalized == Any, | 'left side of date arithmetic must be non interval date'); + assert($leftTypeNormalized == Date || $leftTypeNormalized == String || $leftTypeNormalized == Any, | 'left side of date arithmetic must be non interval date'); assert($rightTypeNormalized == Number || $rightTypeNormalized == String || $simplified.right->instanceOf(IntervalLiteralWrapper) || $simplified.right->instanceOf(NullLiteral) || ($rightTypeNormalized == Date && $simplified.type == ArithmeticType.SUBTRACT), | 'right side of date arithmetic must be numeric or interval'); @@ -2365,7 +2367,7 @@ function <> meta::external::query::sql::transformation::queryToP let cast = ^Cast(expression = ^NullLiteral(), type = ^ColumnType(name = 'DATE')); processCastAsCast($cast, processExpression($cast, $expContext, $context), $expContext, $context); }), - pair($leftTypeNormalized == Date && ($rightTypeNormalized == Date || $rightTypeNormalized == String) && $simplified.type == ArithmeticType.SUBTRACT && !$simplified.right->instanceOf(IntervalLiteralWrapper), {| + pair(($leftTypeNormalized == Date || $leftTypeNormalized == String) && ($rightTypeNormalized == Date || $rightTypeNormalized == String) && $simplified.type == ArithmeticType.SUBTRACT && !$simplified.right->instanceOf(IntervalLiteralWrapper), {| let left = $simplified.left->processExpression($expContext, $context); let right = $simplified.right->processExpression($expContext, $context); @@ -2855,6 +2857,7 @@ Class meta::external::query::sql::transformation::queryToPure::SqlTransformConte let foundContext = $this.contexts->filter(c | $c.name == $contextName); let context = if ($contextName->isEmpty() || $foundContext->isEmpty(), | $this, | $foundContext->toOne()); + $context.columnByName($name, $failIfNotFound); }:TDSColumn[0..1]; columnByName(name:String[1], failIfNotFound:Boolean[1]){ @@ -3000,4 +3003,4 @@ function meta::external::query::sql::transformation::queryToPure::debug(a:String function meta::external::query::sql::transformation::queryToPure::debug(f:FunctionDefinition<{->String[1]}>[1], debug:DebugContext[1]):Any[0] { if ($debug.debug, | println($debug.space + $f->eval()), | []); -} +} \ 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/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 4dcbce199cd..a2dbd301beb 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 @@ -1324,6 +1324,49 @@ function <> meta::external::query::sql::transformation::queryToPure:: ) } +function <> meta::external::query::sql::transformation::queryToPure::tests::testJoinWithMultiCommonTableAliases():Boolean[1] +{ + test( + 'SELECT "t0"."int" AS "int",' + + ' "t1"."Integer" AS "Integer"' + + 'FROM service."/service/service1" "t1"' + + 'INNER JOIN (' + + ' SELECT "t1"."Integer" AS "int",' + + ' "t1"."String" AS "str"' + + ' FROM service."/service/service2" "t1"' + + ') "t0" ON (("t1"."Integer" = "t0"."int") AND ("t1"."String" = "t0"."str"))', + + {|meta::external::query::sql::transformation::queryToPure::tests::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_t1'), + pair('Integer', 'Integer_t1'), + pair('Float', 'Float_t1'), + pair('Decimal', 'Decimal_t1'), + pair('StrictDate', 'StrictDate_t1'), + pair('DateTime', 'DateTime_t1'), + pair('String', 'String_t1')]) + ->join(meta::external::query::sql::transformation::queryToPure::tests::FlatInput.all() + ->project([x|$x.idIn, x|$x.integerIn, x|$x.stringIn], ['ID', 'Integer', 'String']) + ->restrict(['Integer', 'String']) + ->renameColumns([ + pair('Integer', 'int'), + pair('String', 'str')]) + ->renameColumns([ + pair('int', 'int_t0'), + pair('str', 'str_t0')]), + meta::relational::metamodel::join::JoinType->extractEnumValue('INNER'), + {row1, row2|(($row2.getInteger('Integer_t1') == $row2.getInteger('int_t0')) && ($row2.getString('String_t1') == $row2.getString('str_t0')))}) + ->restrict(['int_t0', 'Integer_t1']) + ->renameColumns([ + pair('int_t0', 'int'), + pair('Integer_t1', 'Integer')]) + }, false + ) +} + + //UNION function <> meta::external::query::sql::transformation::queryToPure::tests::testUnion():Boolean[1] @@ -1502,7 +1545,8 @@ function <> meta::external::query::sql::transformation::queryToPure:: 'CAST((DATE_TRUNC( \'DAY\', CAST("StrictDate" AS DATE) ) + (EXTRACT(DOW FROM "StrictDate") * INTERVAL \'1 DAY\')) AS DATE) AS "INTERVAL_MIX3", ' + 'StrictDate - DateTime AS "DATE_SUBTRACT", ' + 'StrictDate - INTERVAL \'1 DAY\' AS "INTERVAL_SUBTRACT", ' + - 'StrictDate - \'2023-01-01\' AS "STRING_SUBSTRACT"' + + 'StrictDate - \'2023-01-01\' AS "STRING_SUBSTRACT", ' + + '\'2023-01-01\' - StrictDate AS "STRING_SUBSTRACT2" ' + 'FROM service."/service/service1"', {| @@ -1522,7 +1566,8 @@ function <> meta::external::query::sql::transformation::queryToPure:: col(row:TDSRow[1] | $row.getStrictDate('StrictDate')->firstHourOfDay()->adjust(($row.getStrictDate('StrictDate')->dayOfWeekNumber() * 1), DurationUnit.DAYS), 'INTERVAL_MIX3'), col(row:TDSRow[1] | dateDiff($row.getStrictDate('StrictDate'), $row.getDateTime('DateTime'), DurationUnit.DAYS), 'DATE_SUBTRACT'), col(row:TDSRow[1] | adjust($row.getStrictDate('StrictDate'), -1, DurationUnit.DAYS), 'INTERVAL_SUBTRACT'), - col(row:TDSRow[1] | dateDiff($row.getStrictDate('StrictDate'), parseDate('2023-01-01'), DurationUnit.DAYS), 'STRING_SUBSTRACT') + col(row:TDSRow[1] | dateDiff($row.getStrictDate('StrictDate'), parseDate('2023-01-01'), DurationUnit.DAYS), 'STRING_SUBSTRACT'), + col(row:TDSRow[1] | dateDiff(parseDate('2023-01-01'), $row.getStrictDate('StrictDate'), DurationUnit.DAYS), 'STRING_SUBSTRACT2') ]) }) }