Skip to content

Commit

Permalink
Rule Based SQL Optimizer - Init Commit with filter push down rules (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
gs-ssh16 authored Dec 17, 2024
1 parent 59e691b commit 171cfae
Show file tree
Hide file tree
Showing 10 changed files with 2,037 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@
<artifactId>legend-engine-pure-runtime-java-extension-compiled-functions-relationalStore-postgresSql-parser</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.finos.legend.engine</groupId>
<artifactId>legend-engine-xt-relationalStore-sqlDialectTranslation-pure</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
Expand Down Expand Up @@ -114,6 +119,11 @@
<artifactId>legend-engine-pure-runtime-java-extension-compiled-functions-relationalStore-postgresSql-parser</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.finos.legend.engine</groupId>
<artifactId>legend-engine-xt-relationalStore-sqlDialectTranslation-pure</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
Expand All @@ -140,6 +150,10 @@
<!-- PURE -->

<!-- ENGINE -->
<dependency>
<groupId>org.finos.legend.engine</groupId>
<artifactId>legend-engine-pure-runtime-java-extension-compiled-functions-unclassified</artifactId>
</dependency>
<dependency>
<groupId>org.finos.legend.engine</groupId>
<artifactId>legend-engine-pure-code-compiled-core</artifactId>
Expand All @@ -151,7 +165,10 @@
<dependency>
<groupId>org.finos.legend.engine</groupId>
<artifactId>legend-engine-pure-runtime-java-extension-compiled-functions-relationalStore-postgresSql-parser</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.finos.legend.engine</groupId>
<artifactId>legend-engine-xt-relationalStore-sqlDialectTranslation-pure</artifactId>
</dependency>
<!-- ENGINE -->

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,18 @@
"pattern": "(meta::external::store::relational::sqlPlanning)(::.*)?",
"dependencies": [
"platform",
"platform_dsl_store",
"platform_dsl_mapping",
"platform_dsl_path",
"platform_dsl_graph",
"platform_dsl_diagram",
"platform_store_relational",
"core_functions_standard",
"core_functions_unclassified",
"core_functions_json",
"core",
"core_external_store_relational_postgres_sql_model",
"core_external_store_relational_postgres_sql_parser"
"core_external_store_relational_postgres_sql_parser",
"core_external_store_relational_sql_dialect_translation"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Copyright 2024 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.

import meta::external::query::sql::metamodel::*;
import meta::external::store::relational::sqlPlanning::*;
import meta::external::store::relational::sqlPlanning::ruleBasedTransformation::*;
import meta::pure::extension::*;



function <<test.Test>> meta::external::store::relational::sqlPlanning::ruleBasedTransformation::multiRuleTests::testJoinAndSubQueryFilterPushDown(): Boolean[1]
{
runSqlRuleBasedTransformationTest(
'SELECT\n' +
' tradetable_0.TradeID AS TradeID,\n' +
' tradetable_0.TradeDate AS TradeDate,\n' +
' tradetable_0.ProductName AS ProductName\n' +
'FROM\n' +
' (\n' +
' SELECT\n' +
' tradetable_1.TradeID AS TradeID,\n' +
' tradetable_1.TradeDate AS TradeDate,\n' +
' tradetable_3.ProductName AS ProductName\n' +
' FROM\n' +
' (\n' +
' SELECT\n' +
' root.ID AS TradeID,\n' +
' root.tradeDate AS TradeDate\n' +
' FROM\n' +
' tradeTable AS root\n' +
' ) AS tradetable_1\n' +
' INNER JOIN\n' +
' (\n' +
' SELECT\n' +
' root.ID AS TradeID,\n' +
' producttable_0.NAME AS ProductName\n' +
' FROM\n' +
' tradeTable AS root\n' +
' LEFT OUTER JOIN\n' +
' productSchema.productTable AS producttable_0\n' +
' ON (root.prodId = producttable_0.ID)\n' +
' ) AS tradetable_3\n' +
' ON (tradetable_1.TradeID = tradetable_3.TradeID)\n' +
' ) AS tradetable_0\n' +
'WHERE\n' +
' tradetable_0.TradeID = 1',

'SELECT\n' +
' tradetable_0.TradeID AS TradeID,\n' +
' tradetable_0.TradeDate AS TradeDate,\n' +
' tradetable_0.ProductName AS ProductName\n' +
'FROM\n' +
' (\n' +
' SELECT\n' +
' tradetable_1.TradeID AS TradeID,\n' +
' tradetable_1.TradeDate AS TradeDate,\n' +
' tradetable_3.ProductName AS ProductName\n' +
' FROM\n' +
' (\n' +
' SELECT\n' +
' root.ID AS TradeID,\n' +
' root.tradeDate AS TradeDate\n' +
' FROM\n' +
' tradeTable AS root\n' +
' WHERE\n' +
' root.ID = 1\n' +
' ) AS tradetable_1\n' +
' INNER JOIN\n' +
' (\n' +
' SELECT\n' +
' root.ID AS TradeID,\n' +
' producttable_0.NAME AS ProductName\n' +
' FROM\n' +
' tradeTable AS root\n' +
' LEFT OUTER JOIN\n' +
' productSchema.productTable AS producttable_0\n' +
' ON (root.prodId = producttable_0.ID)\n' +
' WHERE\n' +
' root.ID = 1\n' +
' ) AS tradetable_3\n' +
' ON (tradetable_1.TradeID = tradetable_3.TradeID AND tradetable_3.TradeID = 1)\n' +
' WHERE\n' +
' tradetable_1.TradeID = 1\n' +
' ) AS tradetable_0\n' +
'WHERE\n' +
' tradetable_0.TradeID = 1',

[
meta::external::store::relational::sqlPlanning::ruleBasedTransformation::subQueryFilterPushDown::subQueryFilterPushDownRule(),
meta::external::store::relational::sqlPlanning::ruleBasedTransformation::joinFilterPushDown::joinFilterPushDownRule()
]
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// Copyright 2024 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.

import meta::external::query::sql::metamodel::*;
import meta::external::store::relational::postgresSql::parser::*;
import meta::external::store::relational::sqlDialectTranslation::*;
import meta::external::store::relational::sqlDialectTranslation::postgres::*;
import meta::external::store::relational::sqlPlanning::*;
import meta::external::store::relational::sqlPlanning::ruleBasedTransformation::*;
import meta::external::store::relational::sqlPlanning::utils::*;
import meta::pure::extension::*;

Class meta::external::store::relational::sqlPlanning::ruleBasedTransformation::TransformedQuery
{
hasChanged: Boolean[1];
query: Query[1];
}

Class <<typemodifiers.abstract>> meta::external::store::relational::sqlPlanning::ruleBasedTransformation::RuleBasedSqlTransformer
{
name : String[1];
enabledByDefault : Boolean[1];
databaseSupport : DatabaseSupport[1];

transformSqlQuery(query: Query[1], config: SqlPlanningConfig[1], debug: DebugContext[1], extensions: Extension[*])
{
fail('Needs to be implemented in sub classes'); ^TransformedQuery(hasChanged = false, query = $query);
}: TransformedQuery[1];
}

Class <<typemodifiers.abstract>> meta::external::store::relational::sqlPlanning::ruleBasedTransformation::DatabaseSupport
{
isDatabaseTypeSupported(dbType: String[1]) { fail('Needs to be implemented in sub classes'); false; }: Boolean[1];
}

Class meta::external::store::relational::sqlPlanning::ruleBasedTransformation::AllDatabaseSupport extends DatabaseSupport
{
isDatabaseTypeSupported(dbType: String[1]) { true }: Boolean[1];
}

Class meta::external::store::relational::sqlPlanning::ruleBasedTransformation::LimitedDatabaseSupport extends DatabaseSupport
{
supportedDatabaseTypes: String[*];
isDatabaseTypeSupported(dbType: String[1]) { $dbType->in($this.supportedDatabaseTypes) }: Boolean[1];
}

function meta::external::store::relational::sqlPlanning::ruleBasedTransformation::executeRuleBasedTransformersOnQuery(query: Query[1], config: SqlPlanningConfig[1], debug: DebugContext[1], extensions: Extension[*]): Query[1]
{
let ruleBasedTransformers = $config->fetchInScopeRuleBasedTransformers($extensions);
$query->executeRuleBasedTransformersOnQuery($config, $ruleBasedTransformers, $debug, $extensions);
}

function meta::external::store::relational::sqlPlanning::ruleBasedTransformation::executeRuleBasedTransformersOnQuery(query: Query[1], config: SqlPlanningConfig[1], rules: RuleBasedSqlTransformer[*], debug: DebugContext[1], extensions: Extension[*]): Query[1]
{
$query->executeRuleBasedTransformersOnQueryTillFixedPointOrMaxIterations($config, $rules, 1, 10, $debug, $extensions).query
}

function <<access.private>> meta::external::store::relational::sqlPlanning::ruleBasedTransformation::executeRuleBasedTransformersOnQueryTillFixedPointOrMaxIterations(query: Query[1], config: SqlPlanningConfig[1], rules: RuleBasedSqlTransformer[*], currentIteration: Integer[1], maxIterations: Integer[1], debug: DebugContext[1], extensions: Extension[*]): TransformedQuery[1]
{
if ($currentIteration > $maxIterations,
| ^TransformedQuery(hasChanged = false, query = $query),
|
let debugPrefix = '[RuleBasedTransformation] Iteration (' + $currentIteration->toString() + '/' + $maxIterations->toString() + ')';
print(if(!$debug.debug, |'', | $debug.space + range(40)->map(x | '-')->joinStrings() + $debugPrefix + format(' (%t{yyyy-MM-dd HH:mm::ss.SSS})', now()) + range(40)->map(x | '-')->joinStrings() + '\n'));
print(if(!$debug.debug, |'', | $debug.space + 'Rules: ' + $rules.name->joinStrings('[', ', ', ']') + '\n'));
print(if(!$debug.debug, |'', | $debug.space + 'Starting Query: ' + $query->printDebugQuery($config, $extensions) + '\n'));

let transformed = $rules->fold({t, agg |
print(if(!$debug.debug, |'', | $debug->indent().space + range(50)->map(x | '-')->joinStrings() + '\n'));
print(if(!$debug.debug, |'', | $debug->indent().space + 'Running rule: ' + $t.name + '\n'));
let res = $t->executeSubTypeQualifierAndCast('transformSqlQuery', [list($agg.query), list($config), list($debug->indent()), list($extensions)], @TransformedQuery);
if ($res.hasChanged,
| print(if(!$debug.debug, |'', | $debug->indent().space + 'Rule Result: Query Changed\n'));
print(if(!$debug.debug, |'', | $debug->indent().space + 'Updated Query: ' + $res.query->printDebugQuery($config, $extensions) + '\n'));,
| print(if(!$debug.debug, |'', | $debug->indent().space + 'Rule Result: Query Unchanged\n'));
);
^TransformedQuery(hasChanged = $res.hasChanged || $agg.hasChanged, query = $res.query);
}, ^TransformedQuery(hasChanged = false, query = $query));

print(if(!$debug.debug, |'', | $debug->indent().space + range(50)->map(x | '-')->joinStrings() + '\n'));
if ($transformed.hasChanged,
| print(if(!$debug.debug, |'', | $debug.space + 'Iteration Result: Query Changed\n'));
print(if(!$debug.debug, |'', | $debug.space + 'Updated Query: ' + $transformed.query->printDebugQuery($config, $extensions) + '\n'));,
| print(if(!$debug.debug, |'', | $debug.space + 'Iteration Result: Query Unchanged\n'));
);
print(if(!$debug.debug, |'', | $debug.space + range(40)->map(x | '-')->joinStrings() + $debugPrefix + format(' (%t{yyyy-MM-dd HH:mm::ss.SSS})', now()) + range(40)->map(x | '-')->joinStrings() + '\n\n'));

if ($transformed.hasChanged,
| $transformed.query->executeRuleBasedTransformersOnQueryTillFixedPointOrMaxIterations($config, $rules, $currentIteration + 1, $maxIterations, $debug, $extensions),
| $transformed
);
)
}

function <<access.private>> meta::external::store::relational::sqlPlanning::ruleBasedTransformation::fetchInScopeRuleBasedTransformers(config: SqlPlanningConfig[1], extensions: Extension[*]): RuleBasedSqlTransformer[*]
{
let defaultTransformers = meta::external::store::relational::sqlPlanning::ruleBasedTransformation::defaultRuleBasedSqlTransformers();
let extensionTransformers = $extensions->map(e | $e.moduleExtension('SqlPlanning')->cast(@SqlPlanningModuleExtension).sqlPlanning_ruleBasedTransformation_extraRuleBasedSqlTransformers);

let allTransformers = $defaultTransformers->concatenate($extensionTransformers);

let filteredTransformers = $allTransformers->filter({t |
if ($t.databaseSupport->meta::external::store::relational::sqlPlanning::utils::executeSubTypeQualifierAndCast('isDatabaseTypeSupported', list($config.dbType), @Boolean) == true,
| // Transformer supported for database type
if ($t.enabledByDefault,
| // TODO: Check if excluded in connection
true,
| // TODO: Check if included in connection
false
),
| false
)
});
}

function meta::external::store::relational::sqlPlanning::ruleBasedTransformation::defaultRuleBasedSqlTransformers(): RuleBasedSqlTransformer[*]
{
[
// meta::external::store::relational::sqlPlanning::ruleBasedTransformation::filterPushDown::filterPushDownSqlTransformer()
]
}

function meta::external::store::relational::sqlPlanning::ruleBasedTransformation::runSqlRuleBasedTransformationTest(originalQuery: String[1], expectedQuery: String[1], rules: RuleBasedSqlTransformer[*]): Boolean[1]
{
runSqlRuleBasedTransformationTest($originalQuery, $expectedQuery, $rules, ^SqlPlanningConfig(dbType = 'Postgres'), postgresSqlDialectExtension())
}

function meta::external::store::relational::sqlPlanning::ruleBasedTransformation::runSqlRuleBasedTransformationTest(originalQuery: String[1], expectedQuery: String[1], rules: RuleBasedSqlTransformer[*], config: SqlPlanningConfig[1], extensions: Extension[*]): Boolean[1]
{
let debug = noDebug();

let parsedQuery = parseSqlStatement($originalQuery)->cast(@Query);
print(if(!$debug.debug, |'', | $debug.space + '>[RuleBasedTransformationTest] Original Query: \n' + $parsedQuery->printDebugQuery($config, true, $extensions) + '\n'));

let transformedQuery = $parsedQuery->executeRuleBasedTransformersOnQuery($config, $rules, $debug, $extensions);
let resultSqlString = $transformedQuery->printDebugQuery($config, true, $extensions);
print(if(!$debug.debug, |'', | $debug.space + '>[RuleBasedTransformationTest] Transformed Query: \n' + $resultSqlString + '\n'));

assertEquals($expectedQuery, $resultSqlString);
}
Loading

0 comments on commit 171cfae

Please sign in to comment.