diff --git a/legend-engine-config/legend-engine-server/pom.xml b/legend-engine-config/legend-engine-server/pom.xml index 6e94374f2f3..b2fac3d4e69 100644 --- a/legend-engine-config/legend-engine-server/pom.xml +++ b/legend-engine-config/legend-engine-server/pom.xml @@ -610,6 +610,22 @@ org.finos.legend.engine legend-engine-xt-sql-query + + org.finos.legend.engine + legend-engine-xt-sql-providers-relationalStore + + + org.finos.legend.engine + legend-engine-xt-sql-providers-service + + + org.finos.legend.engine + legend-engine-xt-sql-providers-core + + + org.finos.legend.engine + legend-engine-xt-sql-providers-shared + org.finos.legend.engine legend-engine-test-data-generation diff --git a/legend-engine-config/legend-engine-server/src/main/java/org/finos/legend/engine/server/Server.java b/legend-engine-config/legend-engine-server/src/main/java/org/finos/legend/engine/server/Server.java index 1568eefd55f..932921641d7 100644 --- a/legend-engine-config/legend-engine-server/src/main/java/org/finos/legend/engine/server/Server.java +++ b/legend-engine-config/legend-engine-server/src/main/java/org/finos/legend/engine/server/Server.java @@ -82,6 +82,8 @@ import org.finos.legend.engine.external.shared.format.model.api.ExternalFormats; import org.finos.legend.engine.functionActivator.api.FunctionActivatorAPI; import org.finos.legend.engine.generation.artifact.api.ArtifactGenerationExtensionApi; +import org.finos.legend.engine.language.hostedService.api.HostedServiceService; +import org.finos.legend.engine.language.hostedService.deployment.HostedServiceDeploymentConfiguration; import org.finos.legend.engine.language.pure.compiler.api.Compile; import org.finos.legend.engine.language.pure.compiler.toPureGraph.PureModel; import org.finos.legend.engine.language.pure.grammar.api.grammarToJson.GrammarToJson; @@ -95,6 +97,8 @@ import org.finos.legend.engine.language.pure.modelManager.ModelManager; import org.finos.legend.engine.language.pure.modelManager.sdlc.SDLCLoader; import org.finos.legend.engine.language.pure.relational.api.relationalElement.RelationalElementAPI; +import org.finos.legend.engine.language.snowflakeApp.api.SnowflakeAppService; +import org.finos.legend.engine.language.snowflakeApp.deployment.SnowflakeAppDeploymentConfiguration; import org.finos.legend.engine.plan.execution.PlanExecutor; import org.finos.legend.engine.plan.execution.api.ExecutePlanLegacy; import org.finos.legend.engine.plan.execution.api.ExecutePlanStrategic; @@ -120,19 +124,20 @@ import org.finos.legend.engine.plan.execution.stores.service.plugin.ServiceStoreExecutor; import org.finos.legend.engine.plan.execution.stores.service.plugin.ServiceStoreExecutorBuilder; import org.finos.legend.engine.plan.generation.extension.PlanGeneratorExtension; -import org.finos.legend.engine.language.hostedService.api.HostedServiceService; -import org.finos.legend.engine.language.hostedService.deployment.HostedServiceDeploymentConfiguration; import org.finos.legend.engine.protocol.pure.v1.PureProtocolObjectMapperFactory; import org.finos.legend.engine.protocol.pure.v1.model.PureProtocol; -import org.finos.legend.engine.language.snowflakeApp.api.SnowflakeAppService; -import org.finos.legend.engine.language.snowflakeApp.deployment.SnowflakeAppDeploymentConfiguration; import org.finos.legend.engine.pure.code.core.PureCoreExtensionLoader; import org.finos.legend.engine.query.graphQL.api.debug.GraphQLDebug; import org.finos.legend.engine.query.graphQL.api.execute.GraphQLExecute; import org.finos.legend.engine.query.graphQL.api.grammar.GraphQLGrammar; import org.finos.legend.engine.query.pure.api.Execute; +import org.finos.legend.engine.query.sql.api.SQLExecutor; import org.finos.legend.engine.query.sql.api.execute.SqlExecute; import org.finos.legend.engine.query.sql.api.grammar.SqlGrammar; +import org.finos.legend.engine.query.sql.providers.LegendServiceSQLSourceProvider; +import org.finos.legend.engine.query.sql.providers.RelationalStoreSQLSourceProvider; +import org.finos.legend.engine.query.sql.providers.shared.FunctionSQLSourceProvider; +import org.finos.legend.engine.query.sql.providers.shared.project.ProjectCoordinateLoader; import org.finos.legend.engine.server.core.ServerShared; import org.finos.legend.engine.server.core.api.CurrentUser; import org.finos.legend.engine.server.core.api.Info; @@ -388,7 +393,12 @@ public void run(T serverConfiguration, Environment environment) environment.jersey().register(new GraphQLDebug(modelManager, serverConfiguration.metadataserver, routerExtensions)); // SQL - environment.jersey().register(new SqlExecute(modelManager, planExecutor, routerExtensions, FastList.newListWith(), generatorExtensions.flatCollect(PlanGeneratorExtension::getExtraPlanTransformers))); + ProjectCoordinateLoader projectCoordinateLoader = new ProjectCoordinateLoader(modelManager, serverConfiguration.metadataserver.getSdlc()); + environment.jersey().register(new SqlExecute(new SQLExecutor(modelManager, planExecutor, routerExtensions, FastList.newListWith( + new RelationalStoreSQLSourceProvider(projectCoordinateLoader), + new FunctionSQLSourceProvider(projectCoordinateLoader), + new LegendServiceSQLSourceProvider(projectCoordinateLoader)), + generatorExtensions.flatCollect(PlanGeneratorExtension::getExtraPlanTransformers)))); environment.jersey().register(new SqlGrammar()); // Service diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/pom.xml b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/pom.xml new file mode 100644 index 00000000000..93b8286a1f9 --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/pom.xml @@ -0,0 +1,74 @@ + + + + + + org.finos.legend.engine + legend-engine-xt-sql-providers + 4.32.1-SNAPSHOT + + 4.0.0 + + legend-engine-xt-sql-providers-core + jar + Legend Engine - XT - SQL - Providers - Core + + + + + + org.finos.legend.engine + legend-engine-protocol-pure + + + org.finos.legend.engine + legend-engine-xt-sql-protocol + + + + + + org.eclipse.collections + eclipse-collections-api + + + org.eclipse.collections + eclipse-collections + + + + + + org.pac4j + pac4j-core + + + + + org.apache.commons + commons-lang3 + + + + + junit + junit + test + + + + \ No newline at end of file diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/api/sources/SQLContext.java b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/api/sources/SQLContext.java new file mode 100644 index 00000000000..5f87c8476d0 --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/api/sources/SQLContext.java @@ -0,0 +1,31 @@ +// 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.sources; + +import org.finos.legend.engine.protocol.sql.metamodel.Node; + +/** + * @deprecated + * Use {@link org.finos.legend.engine.query.sql.providers.core.SQLContext} + */ +@Deprecated +public class SQLContext extends org.finos.legend.engine.query.sql.providers.core.SQLContext +{ + public SQLContext(Node query) + { + super(query); + } +} diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/api/sources/SQLSource.java b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/api/sources/SQLSource.java new file mode 100644 index 00000000000..005acd21d37 --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/api/sources/SQLSource.java @@ -0,0 +1,42 @@ +// 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.sources; + +import org.eclipse.collections.impl.utility.ListIterate; +import org.finos.legend.engine.protocol.pure.v1.model.executionOption.ExecutionOption; +import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.runtime.Runtime; +import org.finos.legend.engine.protocol.pure.v1.model.valueSpecification.raw.Lambda; +import org.finos.legend.engine.protocol.pure.v1.model.valueSpecification.raw.executionContext.ExecutionContext; + +import java.util.List; + +/** + * @deprecated + * Use {@link org.finos.legend.engine.query.sql.providers.core.SQLSource} + */ +@Deprecated +public class SQLSource extends org.finos.legend.engine.query.sql.providers.core.SQLSource +{ + public SQLSource(String type, Lambda func, String mapping, Runtime runtime, List executionOptions, List key) + { + this(type, func, mapping, runtime, executionOptions, null, key); + } + + public SQLSource(String type, Lambda func, String mapping, Runtime runtime, List executionOptions, ExecutionContext executionContext, List key) + { + super(type, func, mapping, runtime, executionOptions, executionContext, ListIterate.collect(key, k -> new org.finos.legend.engine.query.sql.providers.core.SQLSourceArgument(k.getName(), k.getIndex(), k.getValue()))); + } +} \ No newline at end of file diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/api/sources/SQLSourceArgument.java b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/api/sources/SQLSourceArgument.java new file mode 100644 index 00000000000..d0caf9a8470 --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/api/sources/SQLSourceArgument.java @@ -0,0 +1,29 @@ +// 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.sources; + +/** + * @deprecated + * Use {@link org.finos.legend.engine.query.sql.providers.core.SQLSourceArgument} + */ +@Deprecated +public class SQLSourceArgument extends org.finos.legend.engine.query.sql.providers.core.SQLSourceArgument +{ + public SQLSourceArgument(String name, Integer index, Object value) + { + super(name, index, value); + } +} \ No newline at end of file diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/sources/SQLSourceProvider.java b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/api/sources/SQLSourceProvider.java similarity index 68% rename from legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/sources/SQLSourceProvider.java rename to legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/api/sources/SQLSourceProvider.java index 325a88a3caf..398618954b2 100644 --- a/legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/sources/SQLSourceProvider.java +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/api/sources/SQLSourceProvider.java @@ -15,14 +15,11 @@ package org.finos.legend.engine.query.sql.api.sources; -import org.eclipse.collections.api.list.MutableList; -import org.pac4j.core.profile.CommonProfile; - -import java.util.List; - -public interface SQLSourceProvider +/** + * @deprecated + * Use {@link org.finos.legend.engine.query.sql.providers.core.SQLSourceProvider} + */ +@Deprecated +public interface SQLSourceProvider extends org.finos.legend.engine.query.sql.providers.core.SQLSourceProvider { - String getType(); - - SQLSourceResolvedContext resolve(List sources, SQLContext context, MutableList profiles); } \ No newline at end of file diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/api/sources/SQLSourceResolvedContext.java b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/api/sources/SQLSourceResolvedContext.java new file mode 100644 index 00000000000..97b82595f39 --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/api/sources/SQLSourceResolvedContext.java @@ -0,0 +1,59 @@ +// 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.sources; + +import org.eclipse.collections.impl.list.mutable.FastList; +import org.finos.legend.engine.protocol.pure.v1.model.context.PureModelContext; + +import java.util.List; + +/** + * @deprecated + * Use {@link org.finos.legend.engine.query.sql.providers.core.SQLSourceResolvedContext} + */ +@Deprecated +public class SQLSourceResolvedContext +{ + private final List pureModelContexts; + private final List sources; + + public SQLSourceResolvedContext(PureModelContext pureModelContext, List sources) + { + this(FastList.newListWith(pureModelContext), sources); + } + + public SQLSourceResolvedContext(List pureModelContexts, List sources) + { + this.pureModelContexts = pureModelContexts != null ? pureModelContexts : FastList.newList(); + this.sources = sources; + } + + @Deprecated + public PureModelContext getPureModelContext() + { + return pureModelContexts.get(0); + } + + public List getPureModelContexts() + { + return pureModelContexts; + } + + public List getSources() + { + return sources; + } +} diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/sources/TableSource.java b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/api/sources/TableSource.java similarity index 95% rename from legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/sources/TableSource.java rename to legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/api/sources/TableSource.java index 80dcc2ff5ea..ecad910e973 100644 --- a/legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/sources/TableSource.java +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/api/sources/TableSource.java @@ -24,6 +24,11 @@ import java.util.List; import java.util.Optional; +/** + * @deprecated + * Use {@link org.finos.legend.engine.query.sql.providers.core.TableSource} + */ +@Deprecated public class TableSource { private final String type; @@ -81,4 +86,4 @@ public String toString() { return ToStringBuilder.reflectionToString(this); } -} +} \ No newline at end of file diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/api/sources/TableSourceArgument.java b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/api/sources/TableSourceArgument.java new file mode 100644 index 00000000000..2fb04368443 --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/api/sources/TableSourceArgument.java @@ -0,0 +1,72 @@ +// 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.sources; + +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.commons.lang3.builder.ToStringBuilder; + +/** + * @deprecated + * Use {@link org.finos.legend.engine.query.sql.providers.core.TableSourceArgument} + */ +@Deprecated +public class TableSourceArgument +{ + private final String name; + private final Object value; + private final Integer index; + + public TableSourceArgument(String name, Integer index, Object value) + { + this.name = name; + this.value = value; + this.index = index; + } + + public String getName() + { + return name; + } + + public Integer getIndex() + { + return index; + } + + public Object getValue() + { + return value; + } + + @Override + public boolean equals(Object o) + { + return EqualsBuilder.reflectionEquals(this, o); + } + + @Override + public int hashCode() + { + return HashCodeBuilder.reflectionHashCode(this); + } + + @Override + public String toString() + { + return ToStringBuilder.reflectionToString(this); + } +} \ No newline at end of file diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/sources/SQLContext.java b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/providers/core/SQLContext.java similarity index 72% rename from legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/sources/SQLContext.java rename to legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/providers/core/SQLContext.java index b7588f5b78c..e75c93dee21 100644 --- a/legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/sources/SQLContext.java +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/providers/core/SQLContext.java @@ -13,23 +13,21 @@ // limitations under the License. // -package org.finos.legend.engine.query.sql.api.sources; +package org.finos.legend.engine.query.sql.providers.core; import org.finos.legend.engine.protocol.sql.metamodel.Node; -import java.util.Map; - public class SQLContext { private final Node query; - private final Map> arguments; - public SQLContext( - Node query, - Map> arguments - ) + public SQLContext(Node query) { this.query = query; - this.arguments = arguments; + } + + public Node getQuery() + { + return query; } } diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/sources/SQLSource.java b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/providers/core/SQLSource.java similarity index 88% rename from legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/sources/SQLSource.java rename to legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/providers/core/SQLSource.java index 44a9d8e6e56..3fc08a71e1c 100644 --- a/legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/sources/SQLSource.java +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/providers/core/SQLSource.java @@ -13,7 +13,7 @@ // limitations under the License. // -package org.finos.legend.engine.query.sql.api.sources; +package org.finos.legend.engine.query.sql.providers.core; import org.finos.legend.engine.protocol.pure.v1.model.executionOption.ExecutionOption; import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.runtime.Runtime; @@ -33,12 +33,6 @@ public class SQLSource private final ExecutionContext executionContext; private final List key; - @Deprecated - public SQLSource(String type, Lambda func, String mapping, Runtime runtime, List executionOptions, List key) - { - this(type, func, mapping, runtime, executionOptions, null, key); - } - public SQLSource(String type, Lambda func, String mapping, Runtime runtime, List executionOptions, ExecutionContext executionContext, List key) { this.type = type; diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/sources/SQLSourceArgument.java b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/providers/core/SQLSourceArgument.java similarity index 94% rename from legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/sources/SQLSourceArgument.java rename to legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/providers/core/SQLSourceArgument.java index 41a952f2e83..60f94fc0db1 100644 --- a/legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/sources/SQLSourceArgument.java +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/providers/core/SQLSourceArgument.java @@ -13,7 +13,7 @@ // limitations under the License. // -package org.finos.legend.engine.query.sql.api.sources; +package org.finos.legend.engine.query.sql.providers.core; public class SQLSourceArgument { diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/providers/core/SQLSourceProvider.java b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/providers/core/SQLSourceProvider.java new file mode 100644 index 00000000000..9bc416ef803 --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/providers/core/SQLSourceProvider.java @@ -0,0 +1,39 @@ +// 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.providers.core; + +import org.eclipse.collections.api.list.MutableList; +import org.pac4j.core.profile.CommonProfile; + +import java.util.List; + +public interface SQLSourceProvider +{ + String getType(); + + //TODO remove default impl + default SQLSourceResolvedContext resolve(List sources, SQLContext context, MutableList profiles) + { + return null; + } + + //TODO remove default impl + @Deprecated + default org.finos.legend.engine.query.sql.api.sources.SQLSourceResolvedContext resolve(List sources, org.finos.legend.engine.query.sql.api.sources.SQLContext context, MutableList profiles) + { + return null; + } +} \ No newline at end of file diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/sources/SQLSourceResolvedContext.java b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/providers/core/SQLSourceResolvedContext.java similarity index 96% rename from legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/sources/SQLSourceResolvedContext.java rename to legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/providers/core/SQLSourceResolvedContext.java index 959bd70ce1f..f1852c7e2b1 100644 --- a/legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/sources/SQLSourceResolvedContext.java +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/providers/core/SQLSourceResolvedContext.java @@ -13,7 +13,7 @@ // limitations under the License. // -package org.finos.legend.engine.query.sql.api.sources; +package org.finos.legend.engine.query.sql.providers.core; import org.eclipse.collections.impl.list.mutable.FastList; import org.finos.legend.engine.protocol.pure.v1.model.context.PureModelContext; diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/providers/core/TableSource.java b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/providers/core/TableSource.java new file mode 100644 index 00000000000..880982335dc --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/providers/core/TableSource.java @@ -0,0 +1,114 @@ +// 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.providers.core; + +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.eclipse.collections.impl.utility.ListIterate; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +public class TableSource +{ + private final String type; + private final List arguments; + + public TableSource(String type, List arguments) + { + this.type = type; + this.arguments = arguments == null ? Collections.emptyList() : arguments; + } + + public String getType() + { + return type; + } + + public List getArguments() + { + return this.arguments; + } + + //get named argument, or default to index + public TableSourceArgument getArgument(String name, int index) + { + return getArgument(name, index, true); + } + + public TableSourceArgument getArgument(String name, int index, boolean required) + { + Optional found = getNamedArgument(name); + return found.orElseGet(() -> + { + if (this.arguments.size() > index && index >= 0) + { + return this.arguments.get(index); + } + if (required) + { + throw new IllegalArgumentException("'" + name + "' parameter is required"); + } + else + { + return null; + } + }); + } + + public Optional getNamedArgument(String name) + { + return ListIterate.select(this.arguments, a -> name.equals(a.getName())).getFirstOptional(); + } + + public T getArgumentValueAs(String name, int index, Class type, boolean required) + { + TableSourceArgument argument = getArgument(name, index, required); + + if (!required && argument == null) + { + return null; + } + Object value = argument.getValue(); + + if (type.isInstance(value)) + { + return (T) value; + } + + throw new IllegalArgumentException("Argument of name '" + name + "' or index '" + index + "' is not of type " + type.getName()); + } + + @Override + public boolean equals(Object o) + { + return EqualsBuilder.reflectionEquals(this, o); + } + + @Override + public int hashCode() + { + return HashCodeBuilder.reflectionHashCode(this); + } + + @Override + public String toString() + { + return ToStringBuilder.reflectionToString(this); + } +} diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/sources/TableSourceArgument.java b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/providers/core/TableSourceArgument.java similarity index 96% rename from legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/sources/TableSourceArgument.java rename to legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/providers/core/TableSourceArgument.java index 58bbe1bd904..28f4e3b86c2 100644 --- a/legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/sources/TableSourceArgument.java +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-core/src/main/java/org/finos/legend/engine/query/sql/providers/core/TableSourceArgument.java @@ -13,7 +13,7 @@ // limitations under the License. // -package org.finos.legend.engine.query.sql.api.sources; +package org.finos.legend.engine.query.sql.providers.core; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-relationalStore/pom.xml b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-relationalStore/pom.xml new file mode 100644 index 00000000000..1651e8868f1 --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-relationalStore/pom.xml @@ -0,0 +1,84 @@ + + + + + + org.finos.legend.engine + legend-engine-xt-sql-providers + 4.32.1-SNAPSHOT + + 4.0.0 + + legend-engine-xt-sql-providers-relationalStore + jar + Legend Engine - XT - SQL - Providers - Relational Store + + + + + org.finos.legend.engine + legend-engine-xt-sql-providers-core + + + org.finos.legend.engine + legend-engine-xt-sql-providers-shared + + + org.finos.legend.engine + legend-engine-xt-relationalStore-protocol + + + org.finos.legend.engine + legend-engine-protocol-pure + + + + + + org.eclipse.collections + eclipse-collections-api + + + org.eclipse.collections + eclipse-collections + + + + + + junit + junit + test + + + org.mockito + mockito-core + + + org.finos.legend.engine + legend-engine-xt-sql-providers-shared + test-jar + test + + + org.finos.legend.engine + legend-engine-xt-relationalStore-grammar + test + + + + \ No newline at end of file diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-relationalStore/src/main/java/org/finos/legend/engine/query/sql/providers/RelationalStoreSQLSourceProvider.java b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-relationalStore/src/main/java/org/finos/legend/engine/query/sql/providers/RelationalStoreSQLSourceProvider.java new file mode 100644 index 00000000000..38b7d14d0ec --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-relationalStore/src/main/java/org/finos/legend/engine/query/sql/providers/RelationalStoreSQLSourceProvider.java @@ -0,0 +1,89 @@ +// 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.providers; + +import org.eclipse.collections.impl.list.mutable.FastList; +import org.finos.legend.engine.protocol.pure.v1.model.context.PureModelContextData; +import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.connection.ConnectionPointer; +import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.connection.PackageableConnection; +import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.runtime.LegacyRuntime; +import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.store.relational.model.Database; +import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.store.relational.model.Schema; +import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.store.relational.model.Table; +import org.finos.legend.engine.protocol.pure.v1.model.valueSpecification.raw.Lambda; +import org.finos.legend.engine.query.sql.providers.core.SQLSource; +import org.finos.legend.engine.query.sql.providers.core.SQLSourceArgument; +import org.finos.legend.engine.query.sql.providers.core.TableSource; +import org.finos.legend.engine.query.sql.providers.shared.AbstractLegendStoreSQLSourceProvider; +import org.finos.legend.engine.query.sql.providers.shared.project.ProjectCoordinateLoader; +import org.finos.legend.engine.query.sql.providers.shared.utils.SQLProviderUtils; + +import java.util.Collections; +import java.util.List; + +/** + * This class serves for handling the **relationalStore** source type + *

+ * Sample Select statement + * select * from relationalStore(connection => 'my::Connection', store => 'my::Store', schema => 'schema1', table => 'table1', coordinates => 'com.gs:proj1:1.0.0') + * select * from relationalStore(connection => 'my::Connection', store => 'my::Store', schema => 'schema1', table => 'table1', project => 'PROD-12345', workspace => 'myworkspace') + * select * from relationalStore(connection => 'my::Connection', store => 'my::Store', schema => 'schema1', table => 'table1', project => 'PROD-12345', groupWorkspace => 'myworkspace') + */ +public class RelationalStoreSQLSourceProvider extends AbstractLegendStoreSQLSourceProvider +{ + + private static final String TYPE = "relationalStore"; + private static final String ARG_SCHEMA = "schema"; + private static final String ARG_TABLE = "table"; + + public RelationalStoreSQLSourceProvider(ProjectCoordinateLoader projectCoordinateLoader) + { + super(Database.class, projectCoordinateLoader); + } + + @Override + public String getType() + { + return TYPE; + } + + @Override + protected SQLSource createSource(TableSource source, Database store, PackageableConnection connection, List keys, PureModelContextData pmcd) + { + String schemaName = source.getArgumentValueAs(ARG_SCHEMA, -1, String.class, true); + String tableName = source.getArgumentValueAs(ARG_TABLE, -1, String.class, true); + + Lambda lambda = tableToTDS(store, schemaName, tableName); + + ConnectionPointer connectionPtr = new ConnectionPointer(); + connectionPtr.connection = connection.getPath(); + + LegacyRuntime runtime = new LegacyRuntime(); + runtime.connections = FastList.newListWith(connectionPtr); + + Collections.addAll(keys, new SQLSourceArgument(ARG_SCHEMA, null, schemaName), new SQLSourceArgument(ARG_TABLE, null, tableName)); + + return new SQLSource(TYPE, lambda, null, runtime, null, null, keys); + } + + public static Lambda tableToTDS(Database database, String schemaName, String tableName) + { + Schema schema = SQLProviderUtils.extractElement("schema", database.schemas, s -> SQLProviderUtils.equalsEscaped(s.name, schemaName)); + Table table = SQLProviderUtils.extractElement("table", schema.tables, t -> SQLProviderUtils.equalsEscaped(t.name, tableName)); + + return SQLProviderUtils.tableToTDS(database.getPath(), schema.name, table.name); + } +} \ No newline at end of file diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-relationalStore/src/test/java/org/finos/legend/engine/query/sql/providers/TestRelationalStoreSQLSourceProvider.java b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-relationalStore/src/test/java/org/finos/legend/engine/query/sql/providers/TestRelationalStoreSQLSourceProvider.java new file mode 100644 index 00000000000..c99585700bb --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-relationalStore/src/test/java/org/finos/legend/engine/query/sql/providers/TestRelationalStoreSQLSourceProvider.java @@ -0,0 +1,227 @@ +// 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.providers; + +import org.eclipse.collections.impl.list.mutable.FastList; +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.packageableElement.connection.ConnectionPointer; +import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.runtime.LegacyRuntime; +import org.finos.legend.engine.protocol.pure.v1.model.valueSpecification.raw.Lambda; +import org.finos.legend.engine.query.sql.providers.core.SQLSource; +import org.finos.legend.engine.query.sql.providers.core.SQLSourceArgument; +import org.finos.legend.engine.query.sql.providers.core.SQLSourceProvider; +import org.finos.legend.engine.query.sql.providers.core.SQLSourceResolvedContext; +import org.finos.legend.engine.query.sql.providers.core.TableSource; +import org.finos.legend.engine.query.sql.providers.core.TableSourceArgument; +import org.finos.legend.engine.query.sql.providers.shared.AbstractTestLegendStoreSQLSourceProvider; +import org.finos.legend.engine.query.sql.providers.shared.SQLSourceProviderTestUtils; +import org.finos.legend.engine.query.sql.providers.shared.project.ProjectCoordinateLoader; +import org.finos.legend.engine.query.sql.providers.shared.project.ProjectCoordinateWrapper; +import org.finos.legend.engine.query.sql.providers.shared.project.ProjectResolvedContext; +import org.finos.legend.engine.query.sql.providers.shared.utils.SQLProviderUtils; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.List; + +import static org.finos.legend.engine.query.sql.providers.shared.SQLSourceProviderTestUtils.loadPureModelContextFromResource; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class TestRelationalStoreSQLSourceProvider extends AbstractTestLegendStoreSQLSourceProvider +{ + private static final String CONNECTION_NAME = "simple::store::DB::H2Connection"; + + @Mock + private ProjectCoordinateLoader projectCoordinateLoader; + + private RelationalStoreSQLSourceProvider provider; + + @Before + public void setup() + { + provider = new RelationalStoreSQLSourceProvider(projectCoordinateLoader); + } + + @Override + protected SQLSourceProvider getProvider() + { + return provider; + } + + @Override + protected ProjectCoordinateLoader getProjectCoordinateLoader() + { + return projectCoordinateLoader; + } + + @Test + public void testType() + { + Assert.assertEquals("relationalStore", provider.getType()); + } + + @Test + public void testMissingSchema() + { + TableSource tableSource = new TableSource("relationalStore", FastList.newListWith( + new TableSourceArgument("store", null, "simple::store::DBForSQL"), + new TableSourceArgument("coordinates", null, "group:artifact:version"), + new TableSourceArgument("connection", null, CONNECTION_NAME))); + + testError(tableSource, ProjectCoordinateWrapper.coordinates("group:artifact:version"), "'schema' parameter is required"); + } + + @Test + public void testMissingTable() + { + TableSource tableSource = new TableSource("relationalStore", FastList.newListWith( + new TableSourceArgument("store", null, "simple::store::DBForSQL"), + new TableSourceArgument("schema", null, "nonexistent"), + new TableSourceArgument("connection", null, CONNECTION_NAME), + new TableSourceArgument("coordinates", null, "group:artifact:version"))); + + testError(tableSource, ProjectCoordinateWrapper.coordinates("group:artifact:version"), "'table' parameter is required"); + } + + @Test + public void testDatabaseFoundSchemaNotFound() + { + testNotFound("nonexistent", "nonexistent", "No element found for 'schema'"); + } + + @Test + public void testDatabaseFoundSchemaFoundTableNotFound() + { + testNotFound("DBSchema", "nonexistent", "No element found for 'table'"); + } + + @Test + public void testSingleFromCoordinates() + { + testSuccess( + ProjectCoordinateWrapper.coordinates("group:artifact:version"), + new PureModelContextPointer(), + FastList.newListWith(new TableSourceArgument("coordinates", null, "group:artifact:version")), + FastList.newListWith(new SQLSourceArgument("coordinates", null, "group:artifact:version"))); + + } + + @Test + public void testSingleFromProjectWorkspace() + { + testSuccess( + ProjectCoordinateWrapper.workspace("proj1", "ws1"), + new PureModelContextPointer(), + FastList.newListWith( + new TableSourceArgument("project", null, "proj1"), + new TableSourceArgument("workspace", null, "ws1")), + FastList.newListWith( + new SQLSourceArgument("project", null, "proj1"), + new SQLSourceArgument("workspace", null, "ws1") + ) + ); + } + + @Test + public void testSingleFromProjectGroupWorkspace() + { + testSuccess( + ProjectCoordinateWrapper.groupWorkspace("proj1", "ws1"), + new PureModelContextPointer(), + FastList.newListWith( + new TableSourceArgument("project", null, "proj1"), + new TableSourceArgument("groupWorkspace", null, "ws1")), + FastList.newListWith( + new SQLSourceArgument("project", null, "proj1"), + new SQLSourceArgument("groupWorkspace", null, "ws1") + ) + ); + } + + private void testNotFound(String schema, String table, String error) + { + PureModelContextData pmcd = loadPureModelContextFromResource("pmcd.pure", this.getClass()); + when(projectCoordinateLoader.resolve(eq(ProjectCoordinateWrapper.coordinates("group:artifact:version")), any())).thenReturn(new ProjectResolvedContext(pmcd, pmcd)); + + TableSource tableSource = new TableSource("relationalStore", FastList.newListWith( + new TableSourceArgument("store", null, "simple::store::DBForSQL"), + new TableSourceArgument("connection", null, CONNECTION_NAME), + new TableSourceArgument("schema", null, schema), + new TableSourceArgument("table", null, table), + new TableSourceArgument("coordinates", null, "group:artifact:version"))); + + testError(tableSource, error); + } + + private void testError(TableSource tableSource, ProjectCoordinateWrapper projectCoordinateWrapper, String error) + { + PureModelContextData pmcd = loadPureModelContextFromResource("pmcd.pure", this.getClass()); + when(projectCoordinateLoader.resolve(eq(projectCoordinateWrapper), any())).thenReturn(new ProjectResolvedContext(pmcd, pmcd)); + + testError(tableSource, error); + } + + private void testSuccess(ProjectCoordinateWrapper projectCoordinateWrapper, PureModelContext expectedContext, List tableSourceKeys, List sourceKeys) + { + PureModelContextData pmcd = loadPureModelContextFromResource("pmcd.pure", this.getClass()); + when(projectCoordinateLoader.resolve(eq(projectCoordinateWrapper), any())).thenReturn(new ProjectResolvedContext(expectedContext, pmcd)); + + String databaseName = "simple::store::DBForSQL"; + String schemaName = "DBSchema"; + String tableName = "FIRM_TABLE"; + + TableSource tablesource = new TableSource("relationalStore", FastList.newListWith( + new TableSourceArgument("store", null, databaseName), + new TableSourceArgument("schema", null, schemaName), + new TableSourceArgument("table", null, tableName), + new TableSourceArgument("connection", null, CONNECTION_NAME)).withAll(tableSourceKeys) + ); + + List keys = FastList.newListWith( + new SQLSourceArgument("store", null, databaseName), + new SQLSourceArgument("connection", null, CONNECTION_NAME)) + .withAll(sourceKeys) + .with(new SQLSourceArgument("schema", null, schemaName)) + .with(new SQLSourceArgument("table", null, tableName)); + + SQLSourceResolvedContext result = provider.resolve(FastList.newListWith(tablesource), null, FastList.newList()); + + Lambda lambda = SQLProviderUtils.tableToTDS(databaseName, schemaName, tableName); + + ConnectionPointer connectionPtr = new ConnectionPointer(); + connectionPtr.connection = CONNECTION_NAME; + + LegacyRuntime runtime = new LegacyRuntime(); + runtime.connections = FastList.newListWith(connectionPtr); + + SQLSource expected = new SQLSource("relationalStore", lambda, null, runtime, null, null, keys); + + //ASSERT + Assert.assertEquals(FastList.newListWith(expectedContext), result.getPureModelContexts()); + Assert.assertEquals(1, result.getSources().size()); + + SQLSourceProviderTestUtils.assertLogicalEquality(expected, result.getSources().get(0)); + } +} \ No newline at end of file diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-relationalStore/src/test/resources/META-INF/services/org.finos.legend.engine.language.pure.grammar.from.extension.PureGrammarParserExtension b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-relationalStore/src/test/resources/META-INF/services/org.finos.legend.engine.language.pure.grammar.from.extension.PureGrammarParserExtension new file mode 100644 index 00000000000..14ee6d447b1 --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-relationalStore/src/test/resources/META-INF/services/org.finos.legend.engine.language.pure.grammar.from.extension.PureGrammarParserExtension @@ -0,0 +1 @@ +org.finos.legend.engine.language.pure.grammar.from.RelationalGrammarParserExtension \ No newline at end of file diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-relationalStore/src/test/resources/pmcd.pure b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-relationalStore/src/test/resources/pmcd.pure new file mode 100644 index 00000000000..948018fa779 --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-relationalStore/src/test/resources/pmcd.pure @@ -0,0 +1,45 @@ +###Relational +Database simple::store::DBForSQL +( + Schema DBSchema + ( + Table FIRM_TABLE + ( + ID INTEGER PRIMARY KEY, + LEGAL_NAME VARCHAR(100) + ) + + Table PERSON_TABLE + ( + ID INTEGER PRIMARY KEY, + FIRST_NAME VARCHAR(100), + LAST_NAME VARCHAR(100), + FIRM_ID INTEGER + ) + ) +) + +###Connection +RelationalDatabaseConnection simple::store::DB::H2Connection{ + store: simple::store::DB; + type: H2; + specification: LocalH2{ + testDataSetupSqls: [ + 'DROP TABLE IF EXISTS PERSON_TABLE;', + 'CREATE TABLE PERSON_TABLE(ID INT PRIMARY KEY, FIRST_NAME VARCHAR(100), LAST_NAME VARCHAR(100), FIRM_ID INT);', + 'INSERT INTO PERSON_TABLE(ID,FIRST_NAME,LAST_NAME,FIRM_ID) VALUES (1,\'Peter\',\'Smith\',1);', + 'INSERT INTO PERSON_TABLE(ID,FIRST_NAME,LAST_NAME,FIRM_ID) VALUES (2,\'John\',\'Johnson\',1);', + 'INSERT INTO PERSON_TABLE(ID,FIRST_NAME,LAST_NAME,FIRM_ID) VALUES (3,\'John\',\'Hill\',1);', + 'INSERT INTO PERSON_TABLE(ID,FIRST_NAME,LAST_NAME,FIRM_ID) VALUES (4,\'Anthony\',\'Allen\',1)', + 'INSERT INTO PERSON_TABLE(ID,FIRST_NAME,LAST_NAME,FIRM_ID) VALUES (5,\'Fabrice\',\'Roberts\',2)', + 'INSERT INTO PERSON_TABLE(ID,FIRST_NAME,LAST_NAME,FIRM_ID) VALUES (6,\'Oliver\',\'Hill\',3)', + 'INSERT INTO PERSON_TABLE(ID,FIRST_NAME,LAST_NAME,FIRM_ID) VALUES (7,\'David\',\'Harris\',3)', + 'DROP TABLE IF EXISTS FIRM_TABLE;', + 'CREATE TABLE FIRM_TABLE(ID INT PRIMARY KEY, LEGAL_NAME VARCHAR(100));', + 'INSERT INTO FIRM_TABLE(ID,LEGAL_NAME) VALUES (1,\'Firm X\');', + 'INSERT INTO FIRM_TABLE(ID,LEGAL_NAME) VALUES (2,\'Firm A\');', + 'INSERT INTO FIRM_TABLE(ID,LEGAL_NAME) VALUES (3,\'Firm B\');' + ]; + }; + auth: DefaultH2; +} \ No newline at end of file diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-service/pom.xml b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-service/pom.xml new file mode 100644 index 00000000000..f34c9ce12a6 --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-service/pom.xml @@ -0,0 +1,97 @@ + + + + + + org.finos.legend.engine + legend-engine-xt-sql-providers + 4.32.1-SNAPSHOT + + 4.0.0 + + legend-engine-xt-sql-providers-service + jar + Legend Engine - XT - SQL - Providers - Service + + + + + + org.finos.legend.engine + legend-engine-xt-sql-providers-core + + + org.finos.legend.engine + legend-engine-xt-sql-providers-shared + + + org.finos.legend.engine + legend-engine-language-pure-dsl-service + + + org.finos.legend.engine + legend-engine-protocol-pure + + + org.finos.legend.engine + legend-engine-shared-core + + + + + + org.eclipse.collections + eclipse-collections-api + + + org.eclipse.collections + eclipse-collections + + + + + + org.pac4j + pac4j-core + + + + + + junit + junit + test + + + org.mockito + mockito-core + + + org.finos.legend.engine + legend-engine-xt-sql-providers-shared + test-jar + test + + + org.finos.legend.engine + legend-engine-xt-relationalStore-grammar + test + + + + + \ No newline at end of file diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-service/src/main/java/org/finos/legend/engine/query/sql/providers/LegendServiceSQLSourceProvider.java b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-service/src/main/java/org/finos/legend/engine/query/sql/providers/LegendServiceSQLSourceProvider.java new file mode 100644 index 00000000000..ada0258cda0 --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-service/src/main/java/org/finos/legend/engine/query/sql/providers/LegendServiceSQLSourceProvider.java @@ -0,0 +1,120 @@ +// 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.providers; + +import org.eclipse.collections.api.list.MutableList; +import org.eclipse.collections.api.tuple.Pair; +import org.eclipse.collections.impl.list.mutable.FastList; +import org.eclipse.collections.impl.tuple.Tuples; +import org.eclipse.collections.impl.utility.ListIterate; +import org.finos.legend.engine.protocol.pure.v1.model.context.PureModelContext; +import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.service.KeyedExecutionParameter; +import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.service.PureMultiExecution; +import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.service.PureSingleExecution; +import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.service.Service; +import org.finos.legend.engine.query.sql.providers.core.SQLContext; +import org.finos.legend.engine.query.sql.providers.core.SQLSource; +import org.finos.legend.engine.query.sql.providers.core.SQLSourceArgument; +import org.finos.legend.engine.query.sql.providers.core.SQLSourceProvider; +import org.finos.legend.engine.query.sql.providers.core.SQLSourceResolvedContext; +import org.finos.legend.engine.query.sql.providers.core.TableSource; +import org.finos.legend.engine.query.sql.providers.shared.project.ProjectCoordinateLoader; +import org.finos.legend.engine.query.sql.providers.shared.project.ProjectCoordinateWrapper; +import org.finos.legend.engine.query.sql.providers.shared.project.ProjectResolvedContext; +import org.finos.legend.engine.query.sql.providers.shared.utils.SQLProviderUtils; +import org.finos.legend.engine.shared.core.operational.errorManagement.EngineException; +import org.pac4j.core.profile.CommonProfile; + +import java.util.List; +import java.util.Optional; + +/** + * This class serves for handling the **service** source type + *

+ * Sample Select statement + * select * from service('/my/service', coordinates => 'com.gs:proj1:1.0.0') + * select * from service('/my/service', project => 'PROD-12345', workspace => 'myWorkspace') + * select * from service('/my/service', project => 'PROD-12345', groupWorkspace => 'myGroupWorkspace') + */ +public class LegendServiceSQLSourceProvider implements SQLSourceProvider +{ + private static final String PATTERN = "pattern"; + private static final String SERVICE = "service"; + + private final ProjectCoordinateLoader projectCoordinateLoader; + + public LegendServiceSQLSourceProvider(ProjectCoordinateLoader projectCoordinateLoader) + { + this.projectCoordinateLoader = projectCoordinateLoader; + } + + @Override + public String getType() + { + return SERVICE; + } + + @Override + public SQLSourceResolvedContext resolve(List sources, SQLContext context, MutableList profiles) + { + MutableList> resolved = ListIterate.collect(sources, source -> + { + String pattern = source.getArgumentValueAs(PATTERN, 0, String.class, true); + ProjectCoordinateWrapper projectCoordinateWrapper = ProjectCoordinateWrapper.extractFromTableSource(source); + + ProjectResolvedContext resolvedProject = projectCoordinateLoader.resolve(projectCoordinateWrapper, profiles); + + Service service = SQLProviderUtils.extractElement("service", Service.class, resolvedProject.getData(), s -> pattern.equals(s.pattern)); + FastList keys = FastList.newListWith(new SQLSourceArgument(PATTERN, 0, pattern)); + projectCoordinateWrapper.addProjectCoordinatesAsSQLSourceArguments(keys); + SQLSource resolvedSource; + + if (service.execution instanceof PureSingleExecution) + { + resolvedSource = from((PureSingleExecution) service.execution, keys); + } + else if (service.execution instanceof PureMultiExecution) + { + resolvedSource = from((PureMultiExecution) service.execution, source, keys); + } + else + { + throw new EngineException("Execution Type Unsupported"); + } + + return Tuples.pair(resolvedSource, resolvedProject.getContext()); + }); + + return new SQLSourceResolvedContext(resolved.collect(Pair::getTwo), resolved.collect(Pair::getOne)); + } + + private SQLSource from(PureSingleExecution pse, List keys) + { + return new SQLSource(SERVICE, pse.func, pse.mapping, pse.runtime, pse.executionOptions, null, keys); + } + + private SQLSource from(PureMultiExecution pme, TableSource source, List keys) + { + String key = (String) source.getArgument(pme.executionKey, -1).getValue(); + Optional optional = ListIterate.select(pme.executionParameters, e -> e.key.equals(key)).getFirstOptional(); + + KeyedExecutionParameter execution = optional.orElseThrow(() -> new IllegalArgumentException("No execution found for key " + key)); + + keys.add(new SQLSourceArgument(pme.executionKey, null, key)); + + return new SQLSource(SERVICE, pme.func, execution.mapping, execution.runtime, execution.executionOptions, null, keys); + } +} \ No newline at end of file diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-service/src/test/java/org/finos/legend/engine/query/sql/providers/TestLegendServiceSQLSourceProvider.java b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-service/src/test/java/org/finos/legend/engine/query/sql/providers/TestLegendServiceSQLSourceProvider.java new file mode 100644 index 00000000000..3a6a8759df3 --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-service/src/test/java/org/finos/legend/engine/query/sql/providers/TestLegendServiceSQLSourceProvider.java @@ -0,0 +1,194 @@ +// 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.providers; + +import org.eclipse.collections.api.block.function.Function; +import org.eclipse.collections.api.block.procedure.Procedure; +import org.eclipse.collections.impl.list.mutable.FastList; +import org.eclipse.collections.impl.utility.ListIterate; +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.packageableElement.service.KeyedExecutionParameter; +import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.service.PureMultiExecution; +import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.service.PureSingleExecution; +import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.service.Service; +import org.finos.legend.engine.query.sql.providers.core.SQLSource; +import org.finos.legend.engine.query.sql.providers.core.SQLSourceArgument; +import org.finos.legend.engine.query.sql.providers.core.SQLSourceResolvedContext; +import org.finos.legend.engine.query.sql.providers.core.TableSource; +import org.finos.legend.engine.query.sql.providers.core.TableSourceArgument; +import org.finos.legend.engine.query.sql.providers.shared.project.ProjectCoordinateLoader; +import org.finos.legend.engine.query.sql.providers.shared.project.ProjectCoordinateWrapper; +import org.finos.legend.engine.query.sql.providers.shared.project.ProjectResolvedContext; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import static org.finos.legend.engine.query.sql.providers.shared.SQLSourceProviderTestUtils.assertLogicalEquality; +import static org.finos.legend.engine.query.sql.providers.shared.SQLSourceProviderTestUtils.loadPureModelContextFromResource; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class TestLegendServiceSQLSourceProvider +{ + @Mock + private ProjectCoordinateLoader projectCoordinateLoader; + + private LegendServiceSQLSourceProvider provider; + + @Before + public void setup() + { + provider = new LegendServiceSQLSourceProvider(projectCoordinateLoader); + } + + @Test + public void testType() + { + Assert.assertEquals("service", provider.getType()); + } + + public void testSingleService(String pattern, TableSource table, Procedure pmcdSupplier, Function expectedFunc) + { + PureModelContextData pmcd = loadPureModelContextFromResource("pmcd.pure", this.getClass()); + + Service service = ListIterate.select(pmcd.getElementsOfType(Service.class), s -> s.pattern.equals(pattern)).getOnly(); + PureSingleExecution execution = (PureSingleExecution) service.execution; + + pmcdSupplier.accept(pmcd); + + SQLSource expected = expectedFunc.apply(execution); + + SQLSourceResolvedContext resolved = provider.resolve(FastList.newListWith(table), null, FastList.newList()); + Assert.assertNotNull(resolved.getPureModelContext()); + Assert.assertEquals(1, resolved.getSources().size()); + + assertLogicalEquality(expected, resolved.getSources().get(0)); + } + + @Test + public void testSingleServicePatternAndCoordinates() + { + String pattern = "/people"; + TableSource table = new TableSource("service", FastList.newListWith( + new TableSourceArgument("pattern", 0, pattern), + new TableSourceArgument("coordinates", null, "group:artifact:version") + )); + + testSingleService(pattern, table, pmcd -> + { + PureModelContextPointer pointer = new PureModelContextPointer(); + when(projectCoordinateLoader.resolve(eq(ProjectCoordinateWrapper.coordinates("group:artifact:version")), any())).thenReturn(new ProjectResolvedContext(pointer, pmcd)); + }, execution -> new SQLSource("service", execution.func, execution.mapping, execution.runtime, execution.executionOptions, null, FastList.newListWith( + new SQLSourceArgument("pattern", 0, pattern), + new SQLSourceArgument("coordinates", null, "group:artifact:version") + ))); + } + + @Test + public void testMultiServicePatternAndCoordinates() + { + String pattern = "/people/{key}"; + PureModelContextData pmcd = loadPureModelContextFromResource("pmcd.pure", this.getClass()); + + + PureModelContextPointer pointer = new PureModelContextPointer(); + when(projectCoordinateLoader.resolve(eq(ProjectCoordinateWrapper.coordinates("group:artifact:version")), any())).thenReturn(new ProjectResolvedContext(pointer, pmcd)); + + Service service = ListIterate.select(pmcd.getElementsOfType(Service.class), s -> s.pattern.equals(pattern)).getOnly(); + PureMultiExecution multi = (PureMultiExecution) service.execution; + KeyedExecutionParameter execution = multi.executionParameters.get(1); + + + TableSource table = new TableSource("service", FastList.newListWith( + new TableSourceArgument("pattern", 0, pattern), + new TableSourceArgument("key", null, "k2"), + new TableSourceArgument("coordinates", null, "group:artifact:version") + )); + + SQLSource expected = new SQLSource("service", multi.func, execution.mapping, execution.runtime, execution.executionOptions, null, FastList.newListWith( + new SQLSourceArgument("pattern", 0, pattern), + new SQLSourceArgument("coordinates", null, "group:artifact:version"), + new SQLSourceArgument("key", null, "k2") + )); + + SQLSourceResolvedContext resolved = provider.resolve(FastList.newListWith(table), null, FastList.newList()); + Assert.assertEquals(FastList.newListWith(pointer), resolved.getPureModelContexts()); + Assert.assertEquals(1, resolved.getSources().size()); + + assertLogicalEquality(expected, resolved.getSources().get(0)); + } + + @Test + public void testSingleServicePatternPatternAndWorkspace() + { + String pattern = "/people"; + TableSource table = new TableSource("service", FastList.newListWith( + new TableSourceArgument("pattern", 0, pattern), + new TableSourceArgument("project", null, "p1"), + new TableSourceArgument("workspace", null, "ws") + )); + + testSingleService(pattern, table, pmcd -> + when(projectCoordinateLoader.resolve(eq(ProjectCoordinateWrapper.workspace("p1", "ws")), any())).thenReturn(new ProjectResolvedContext(pmcd, pmcd)), + execution -> new SQLSource("service", execution.func, execution.mapping, execution.runtime, execution.executionOptions, null, FastList.newListWith( + new SQLSourceArgument("pattern", 0, pattern), + new SQLSourceArgument("project", null, "p1"), + new SQLSourceArgument("workspace", null, "ws") + ))); + + } + + @Test + public void testSingleServicePatternPatternAndGroupWorkspace() + { + String pattern = "/people"; + TableSource table = new TableSource("service", FastList.newListWith( + new TableSourceArgument("pattern", 0, pattern), + new TableSourceArgument("project", null, "p1"), + new TableSourceArgument("groupWorkspace", null, "gws") + )); + + testSingleService(pattern, table, pmcd -> + when(projectCoordinateLoader.resolve(eq(ProjectCoordinateWrapper.groupWorkspace("p1", "gws")), any())).thenReturn(new ProjectResolvedContext(pmcd, pmcd)), + execution -> new SQLSource("service", execution.func, execution.mapping, execution.runtime, execution.executionOptions, null, FastList.newListWith( + new SQLSourceArgument("pattern", 0, pattern), + new SQLSourceArgument("project", null, "p1"), + new SQLSourceArgument("groupWorkspace", null, "gws") + ))); + + } + + @Test + public void testNoServiceFound() + { + PureModelContextData pmcd = loadPureModelContextFromResource("pmcd.pure", this.getClass()); + when(projectCoordinateLoader.resolve(eq(ProjectCoordinateWrapper.workspace("p1", "ws")), any())).thenReturn(new ProjectResolvedContext(pmcd, pmcd)); + + TableSource table = new TableSource("service", FastList.newListWith( + new TableSourceArgument("pattern", 0, "notfound"), + new TableSourceArgument("project", null, "p1"), + new TableSourceArgument("workspace", null, "ws") + )); + IllegalArgumentException exception = Assert.assertThrows("Should throw given no service found", IllegalArgumentException.class, () -> provider.resolve(FastList.newListWith(table), null, FastList.newList())); + Assert.assertEquals("No element found for 'service'", exception.getMessage()); + } +} \ No newline at end of file diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-service/src/test/resources/META-INF/services/org.finos.legend.engine.language.pure.grammar.from.extension.PureGrammarParserExtension b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-service/src/test/resources/META-INF/services/org.finos.legend.engine.language.pure.grammar.from.extension.PureGrammarParserExtension new file mode 100644 index 00000000000..14ee6d447b1 --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-service/src/test/resources/META-INF/services/org.finos.legend.engine.language.pure.grammar.from.extension.PureGrammarParserExtension @@ -0,0 +1 @@ +org.finos.legend.engine.language.pure.grammar.from.RelationalGrammarParserExtension \ No newline at end of file diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-service/src/test/resources/pmcd.pure b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-service/src/test/resources/pmcd.pure new file mode 100644 index 00000000000..fa96a3b66fb --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-service/src/test/resources/pmcd.pure @@ -0,0 +1,125 @@ +###Pure +import simple::model::*; + +Class simple::model::Person +{ + firstName: String[1]; + lastName: String[1]; +} + +###Relational +Database simple::store::DB +( + Table PERSON_TABLE + ( + FIRST_NAME VARCHAR(100), + LAST_NAME VARCHAR(100) + ) +) + +###Mapping +import simple::model::*; +import simple::store::*; + +Mapping simple::mapping::Mapping +( + Person : Relational + { + firstName: [DB]PERSON_TABLE.FIRST_NAME, + lastName: [DB]PERSON_TABLE.LAST_NAME + } +) + +Mapping simple::mapping::Mapping2 +( + Person : Relational + { + firstName: [DB]PERSON_TABLE.FIRST_NAME, + lastName: [DB]PERSON_TABLE.LAST_NAME + } +) + +###Runtime +Runtime simple::runtime::Runtime +{ + mappings : + [ + simple::mapping::Mapping + ]; + connections : + [ + simple::store::DB : + [ + connection_1 : #{ + RelationalDatabaseConnection { + store: simple::store::DB; + type: H2; + specification: LocalH2{ + testDataSetupSqls: [ + 'DROP TABLE IF EXISTS PERSON_TABLE;', + 'CREATE TABLE PERSON_TABLE(ID INT PRIMARY KEY, FIRST_NAME VARCHAR(100), LAST_NAME VARCHAR(100), FIRM_ID INT);', + 'INSERT INTO PERSON_TABLE(ID,FIRST_NAME,LAST_NAME,FIRM_ID) VALUES (1,\'Peter\',\'Smith\',1);' + ]; + }; + auth: DefaultH2; + } + }# + ] + ]; +} + +###Service +Service simple::service::PeopleService +{ + pattern: '/people'; + owners: + [ + 'person1', + 'person2' + ]; + documentation: ''; + autoActivateUpdates: true; + execution: Single + { + query: {| + simple::model::Person.all()->project([ + col(x | $x.firstName, 'first name'), + col(x | $x.lastName, 'last name') + ]) + }; + mapping: simple::mapping::Mapping; + runtime: simple::runtime::Runtime; + } +} + +Service simple::service::MultiExecutionService +{ + pattern: '/people/{key}'; + owners: + [ + 'person1', + 'person2' + ]; + documentation: ''; + autoActivateUpdates: true; + execution: Multi + { + query: {| + simple::model::Person.all()->project([ + col(x | $x.firstName, 'first name'), + col(x | $x.lastName, 'last name') + ]) + }; + key: 'key'; + executions['k1']: + { + mapping: simple::mapping::Mapping; + runtime: simple::runtime::Runtime; + } + executions['k2']: + { + mapping: simple::mapping::Mapping2; + runtime: simple::runtime::Runtime; + } + } +} \ No newline at end of file diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/pom.xml b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/pom.xml new file mode 100644 index 00000000000..25e0e30f21e --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/pom.xml @@ -0,0 +1,159 @@ + + + + + + org.finos.legend.engine + legend-engine-xt-sql-providers + 4.32.1-SNAPSHOT + + 4.0.0 + + legend-engine-xt-sql-providers-shared + jar + Legend Engine - XT - SQL - Providers - Shared + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + test-jar + + + + + + + + + + + javax.ws.rs + javax.ws.rs-api + + + + + org.finos.legend.engine + legend-engine-protocol-pure + + + org.finos.legend.engine + legend-engine-xt-sql-providers-core + + + org.finos.legend.engine + legend-engine-language-pure-modelManager-sdlc + + + org.finos.legend.engine + legend-engine-language-pure-grammar + + + org.finos.legend.engine + legend-engine-language-pure-modelManager + + + org.finos.legend.engine + legend-engine-shared-core + + + + + + + org.eclipse.collections + eclipse-collections-api + + + org.eclipse.collections + eclipse-collections + + + + + + org.apache.httpcomponents + httpclient + + + org.apache.httpcomponents + httpcore + + + + + + org.pac4j + pac4j-core + + + + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.core + jackson-annotations + + + + + + io.opentracing + opentracing-util + + + io.opentracing + opentracing-api + + + + + org.apache.commons + commons-lang3 + + + + + junit + junit + test + + + org.mockito + mockito-core + test + + + commons-io + commons-io + test + + + + \ No newline at end of file diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/src/main/java/org/finos/legend/engine/query/sql/providers/shared/AbstractLegendStoreSQLSourceProvider.java b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/src/main/java/org/finos/legend/engine/query/sql/providers/shared/AbstractLegendStoreSQLSourceProvider.java new file mode 100644 index 00000000000..c55a8bfad34 --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/src/main/java/org/finos/legend/engine/query/sql/providers/shared/AbstractLegendStoreSQLSourceProvider.java @@ -0,0 +1,83 @@ +// 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.providers.shared; + +import org.eclipse.collections.api.list.MutableList; +import org.eclipse.collections.impl.list.mutable.FastList; +import org.eclipse.collections.impl.utility.ListIterate; +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.packageableElement.connection.PackageableConnection; +import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.store.Store; +import org.finos.legend.engine.query.sql.providers.core.SQLContext; +import org.finos.legend.engine.query.sql.providers.core.SQLSource; +import org.finos.legend.engine.query.sql.providers.core.SQLSourceArgument; +import org.finos.legend.engine.query.sql.providers.core.SQLSourceProvider; +import org.finos.legend.engine.query.sql.providers.core.SQLSourceResolvedContext; +import org.finos.legend.engine.query.sql.providers.core.TableSource; +import org.finos.legend.engine.query.sql.providers.shared.project.ProjectCoordinateLoader; +import org.finos.legend.engine.query.sql.providers.shared.project.ProjectCoordinateWrapper; +import org.finos.legend.engine.query.sql.providers.shared.project.ProjectResolvedContext; +import org.finos.legend.engine.query.sql.providers.shared.utils.SQLProviderUtils; +import org.pac4j.core.profile.CommonProfile; + +import java.util.List; + +public abstract class AbstractLegendStoreSQLSourceProvider implements SQLSourceProvider +{ + + private static final String ARG_CONNECTION = "connection"; + private static final String ARG_STORE = "store"; + + private final Class storeType; + private final ProjectCoordinateLoader projectCoordinateLoader; + + public AbstractLegendStoreSQLSourceProvider(Class storeType, ProjectCoordinateLoader projectCoordinateLoader) + { + this.storeType = storeType; + this.projectCoordinateLoader = projectCoordinateLoader; + } + + protected abstract SQLSource createSource(TableSource source, T store, PackageableConnection connection, List keys, PureModelContextData pmcd); + + @Override + public SQLSourceResolvedContext resolve(List sources, SQLContext context, MutableList profiles) + { + List contexts = FastList.newList(); + List sqlSources = FastList.newList(); + + ListIterate.forEach(sources, source -> + { + ProjectCoordinateWrapper projectCoordinateWrapper = ProjectCoordinateWrapper.extractFromTableSource(source); + ProjectResolvedContext resolved = projectCoordinateLoader.resolve(projectCoordinateWrapper, profiles); + + String storeName = source.getArgumentValueAs(ARG_STORE, -1, String.class, true); + String connectionName = source.getArgumentValueAs(ARG_CONNECTION, -1, String.class, true); + + T store = SQLProviderUtils.extractElement(ARG_STORE, this.storeType, resolved.getData(), s -> storeName.equals(s.getPath())); + PackageableConnection connection = SQLProviderUtils.extractElement(ARG_CONNECTION, PackageableConnection.class, resolved.getData(), c -> connectionName.equals(c.getPath())); + + List keys = FastList.newListWith(new SQLSourceArgument(ARG_STORE, null, storeName), new SQLSourceArgument(ARG_CONNECTION, null, connectionName)); + projectCoordinateWrapper.addProjectCoordinatesAsSQLSourceArguments(keys); + + sqlSources.add(createSource(source, store, connection, keys, resolved.getData())); + + contexts.add(resolved.getContext()); + }); + + return new SQLSourceResolvedContext(contexts, sqlSources); + } +} \ No newline at end of file diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/src/main/java/org/finos/legend/engine/query/sql/providers/shared/FunctionSQLSourceProvider.java b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/src/main/java/org/finos/legend/engine/query/sql/providers/shared/FunctionSQLSourceProvider.java new file mode 100644 index 00000000000..7c2a3312601 --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/src/main/java/org/finos/legend/engine/query/sql/providers/shared/FunctionSQLSourceProvider.java @@ -0,0 +1,104 @@ +// 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.providers.shared; + +import org.eclipse.collections.api.factory.Sets; +import org.eclipse.collections.api.list.MutableList; +import org.eclipse.collections.api.set.ImmutableSet; +import org.eclipse.collections.api.tuple.Pair; +import org.eclipse.collections.impl.list.mutable.FastList; +import org.eclipse.collections.impl.tuple.Tuples; +import org.eclipse.collections.impl.utility.ListIterate; +import org.finos.legend.engine.protocol.pure.v1.model.context.PureModelContext; +import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.domain.Function; +import org.finos.legend.engine.protocol.pure.v1.model.valueSpecification.raw.Lambda; +import org.finos.legend.engine.query.sql.providers.core.SQLContext; +import org.finos.legend.engine.query.sql.providers.core.SQLSource; +import org.finos.legend.engine.query.sql.providers.core.SQLSourceArgument; +import org.finos.legend.engine.query.sql.providers.core.SQLSourceProvider; +import org.finos.legend.engine.query.sql.providers.core.SQLSourceResolvedContext; +import org.finos.legend.engine.query.sql.providers.core.TableSource; +import org.finos.legend.engine.query.sql.providers.shared.project.ProjectCoordinateLoader; +import org.finos.legend.engine.query.sql.providers.shared.project.ProjectCoordinateWrapper; +import org.finos.legend.engine.query.sql.providers.shared.project.ProjectResolvedContext; +import org.finos.legend.engine.query.sql.providers.shared.utils.SQLProviderUtils; +import org.finos.legend.engine.shared.core.operational.errorManagement.EngineException; +import org.pac4j.core.profile.CommonProfile; + +import java.util.List; + +/** + * This class serves for handling the **function** source type + *

+ * Sample Select statement + * select * from func('my::func__TabularDataSet_1_', coordinates => 'com.gs:proj1:1.0.0') + * select * from func('my::func__TabularDataSet_1_', project => 'PROD-12345', workspace => 'myWorkspace') + * select * from func('my::func__TabularDataSet_1_', project => 'PROD-12345', groupWorkspace => 'myGroupWorkspace') + * select * from func('my::func_String_1__TabularDataSet_1_', project => 'PROD-12345', groupWorkspace => 'myGroupWorkspace', myParam => 'abc') + */ +public class FunctionSQLSourceProvider implements SQLSourceProvider +{ + + private static final String FUNCTION = "func"; + private static final String PATH = "path"; + + private static final ImmutableSet TABULAR_TYPES = Sets.immutable.of( + "meta::pure::tds::TabularDataSet" + ); + + private final ProjectCoordinateLoader projectCoordinateLoader; + + public FunctionSQLSourceProvider(ProjectCoordinateLoader projectCoordinateLoader) + { + this.projectCoordinateLoader = projectCoordinateLoader; + } + + @Override + public String getType() + { + return FUNCTION; + } + + @Override + public SQLSourceResolvedContext resolve(List sources, SQLContext context, MutableList profiles) + { + MutableList> resolved = ListIterate.collect(sources, source -> + { + String path = source.getArgumentValueAs(PATH, 0, String.class, true); + ProjectCoordinateWrapper projectCoordinateWrapper = ProjectCoordinateWrapper.extractFromTableSource(source); + + ProjectResolvedContext resolvedProject = projectCoordinateLoader.resolve(projectCoordinateWrapper, profiles); + + Function function = SQLProviderUtils.extractElement("function", Function.class, resolvedProject.getData(), f -> path.equals(f.getPath())); + + if (!TABULAR_TYPES.contains(function.returnType)) + { + throw new EngineException("Function " + path + " does not return Tabular data type"); + } + + Lambda lambda = new Lambda(); + lambda.parameters = function.parameters; + lambda.body = function.body; + + List keys = FastList.newListWith(new SQLSourceArgument(PATH, 0, path)); + projectCoordinateWrapper.addProjectCoordinatesAsSQLSourceArguments(keys); + + return Tuples.pair(new SQLSource(getType(), lambda, null, null, FastList.newList(), null, keys), resolvedProject.getContext()); + }); + + return new SQLSourceResolvedContext(resolved.collect(Pair::getTwo), resolved.collect(Pair::getOne)); + } +} \ No newline at end of file diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/src/main/java/org/finos/legend/engine/query/sql/providers/shared/project/ProjectCoordinateLoader.java b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/src/main/java/org/finos/legend/engine/query/sql/providers/shared/project/ProjectCoordinateLoader.java new file mode 100644 index 00000000000..95aa2f04a02 --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/src/main/java/org/finos/legend/engine/query/sql/providers/shared/project/ProjectCoordinateLoader.java @@ -0,0 +1,252 @@ +// 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.providers.shared.project; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.http.StatusLine; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.BasicCookieStore; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.util.EntityUtils; +import org.eclipse.collections.api.block.function.Function; +import org.eclipse.collections.api.list.MutableList; +import org.finos.legend.engine.language.pure.modelManager.ModelManager; +import org.finos.legend.engine.language.pure.modelManager.sdlc.SDLCLoader; +import org.finos.legend.engine.language.pure.modelManager.sdlc.configuration.ServerConnectionConfiguration; +import org.finos.legend.engine.protocol.pure.PureClientVersions; +import org.finos.legend.engine.protocol.pure.v1.model.context.AlloySDLC; +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.query.sql.providers.shared.utils.TraceUtils; +import org.finos.legend.engine.shared.core.ObjectMapperFactory; +import org.finos.legend.engine.shared.core.kerberos.HttpClientBuilder; +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.LoggingEventType; +import org.pac4j.core.profile.CommonProfile; + +import javax.security.auth.Subject; +import javax.ws.rs.core.Response; +import java.security.PrivilegedAction; +import java.util.List; +import java.util.Optional; + +public class ProjectCoordinateLoader +{ + private static final ObjectMapper OBJECT_MAPPER = ObjectMapperFactory.getNewStandardObjectMapperWithPureProtocolExtensionSupports(); + + private final ModelManager modelManager; + private final ServerConnectionConfiguration sdlcServerConfig; + private final Function, CloseableHttpClient> httpClientProvider; + + public ProjectCoordinateLoader(ModelManager modelManager, ServerConnectionConfiguration sdlcServerConfig) + { + this(modelManager, sdlcServerConfig, profiles -> (CloseableHttpClient) HttpClientBuilder.getHttpClient(new BasicCookieStore())); + } + + public ProjectCoordinateLoader(ModelManager modelManager, ServerConnectionConfiguration sdlcServerConfig, Function, CloseableHttpClient> httpClientProvider) + { + this.modelManager = modelManager; + this.sdlcServerConfig = sdlcServerConfig; + this.httpClientProvider = httpClientProvider; + } + + public ProjectResolvedContext resolve(ProjectCoordinateWrapper projectCoordinateWrapper, MutableList profiles) + { + return resolve(projectCoordinateWrapper, true, profiles); + } + + public ProjectResolvedContext resolve(ProjectCoordinateWrapper projectCoordinateWrapper, boolean required, MutableList profiles) + { + Optional coordinates = projectCoordinateWrapper.getCoordinates(); + if (coordinates.isPresent()) + { + PureModelContextPointer pointer = pointerFromCoordinates(coordinates.get()); + + PureModelContextData pmcd = modelManager.loadData(pointer, PureClientVersions.production, profiles); + + return new ProjectResolvedContext(pointer, pmcd); + } + Optional project = projectCoordinateWrapper.getProject(); + if (project.isPresent()) + { + Optional workspace = projectCoordinateWrapper.getWorkspace(); + Optional groupWorkspace = projectCoordinateWrapper.getGroupWorkspace(); + String workspaceId = workspace.orElseGet(groupWorkspace::get); + boolean isGroup = groupWorkspace.isPresent(); + String projectId = project.get(); + + PureModelContextData pmcd = loadProjectPureModelContextData(projectId, workspaceId, isGroup, profiles); + + return new ProjectResolvedContext(pmcd, pmcd); + } + + if (required) + { + throw new EngineException("project/workspace or coordinates must be supplied"); + } + + return null; + } + + private PureModelContextPointer pointerFromCoordinates(String coordinates) + { + AlloySDLC sdlc = new AlloySDLC(); + enrichCoordinates(sdlc, coordinates); + PureModelContextPointer pointer = new PureModelContextPointer(); + pointer.sdlcInfo = sdlc; + return pointer; + } + + private void enrichCoordinates(AlloySDLC alloySDLC, String coordinates) + { + String[] parts = coordinates.split(":"); + if (parts.length != 3) + { + throw new IllegalArgumentException("Invalid coordinates on service " + coordinates); + } + + alloySDLC.groupId = parts[0]; + alloySDLC.artifactId = parts[1]; + alloySDLC.version = parts[2]; + } + + private PureModelContextData loadProjectPureModelContextData(String project, String workspace, boolean isGroup, MutableList profiles) + { + return doAs(ProfileManagerHelper.extractSubject(profiles), () -> + { + String url = String.format("%s/api/projects/%s/%s/%s/pureModelContextData", sdlcServerConfig.getBaseUrl(), project, isGroup ? "groupWorkspaces" : "workspaces", workspace); + PureModelContextData projectPMCD = SDLCLoader.loadMetadataFromHTTPURL(profiles, LoggingEventType.METADATA_REQUEST_ALLOY_PROJECT_START, LoggingEventType.METADATA_REQUEST_ALLOY_PROJECT_STOP, url, httpClientProvider); + PureModelContextData dependencyPMCD = getSDLCDependenciesPMCD(project, workspace, isGroup, profiles); + + return projectPMCD.combine(dependencyPMCD); + }); + } + + private PureModelContextData getSDLCDependenciesPMCD(String project, String workspace, boolean isGroup, MutableList profiles) + { + return TraceUtils.trace("Get SDLC Dependencies", span -> + { + + span.setTag("project", project); + span.setTag("workspace", workspace); + span.setTag("group", isGroup); + + if (sdlcServerConfig == null) + { + throw new EngineException("SDLC Server configuration must be supplied"); + } + try (CloseableHttpClient client = this.httpClientProvider.apply(profiles)) + { + String url = String.format("%s/api/projects/%s/%s/%s/revisions/HEAD/upstreamProjects", + sdlcServerConfig.getBaseUrl(), + project, + isGroup ? "groupWorkspaces" : "workspaces", + workspace); + + try (CloseableHttpResponse response = client.execute(new HttpGet(url))) + { + StatusLine status = response.getStatusLine(); + if (!Response.Status.Family.familyOf(status.getStatusCode()).equals(Response.Status.Family.SUCCESSFUL)) + { + throw new RuntimeException(String.format("Status Code: %s\nReason: %s\nMessage: %s", + status.getStatusCode(), status.getReasonPhrase(), "Error fetching from " + url)); + } + + List dependencies = OBJECT_MAPPER.readValue(EntityUtils.toString(response.getEntity()), new TypeReference>() + { + }); + PureModelContextData.Builder builder = PureModelContextData.newBuilder(); + dependencies.forEach(dependency -> + { + try + { + builder.addPureModelContextData(loadProjectData(profiles, dependency.getGroupId(), dependency.getArtifactId(), dependency.versionId)); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + }); + builder.removeDuplicates(); + return builder.build(); + } + } + catch (Exception e) + { + throw new RuntimeException(e); + } + }); + } + + private PureModelContextData loadProjectData(MutableList profiles, String groupId, String artifactId, String versionId) + { + return TraceUtils.trace("Loading Project Data", span -> + { + + span.setTag("groupId", groupId); + span.setTag("artifactId", artifactId); + span.setTag("versionId", versionId); + + Subject subject = ProfileManagerHelper.extractSubject(profiles); + PureModelContextPointer pointer = new PureModelContextPointer(); + AlloySDLC sdlcInfo = new AlloySDLC(); + sdlcInfo.groupId = groupId; + sdlcInfo.artifactId = artifactId; + sdlcInfo.version = versionId; + pointer.sdlcInfo = sdlcInfo; + + return doAs(subject, () -> this.modelManager.loadData(pointer, PureClientVersions.production, profiles)); + }); + } + + private T doAs(Subject subject, PrivilegedAction action) + { + return subject != null ? Subject.doAs(subject, action) : action.run(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + private static class SDLCProjectDependency + { + private final String projectId; + private final String versionId; + + public SDLCProjectDependency(@JsonProperty("projectId") String projectId, @JsonProperty("versionId") String versionId) + { + this.projectId = projectId; + this.versionId = versionId; + } + + public String getGroupId() + { + return projectId.split(":")[0]; + } + + public String getArtifactId() + { + return projectId.split(":")[1]; + } + + public String getVersionId() + { + return versionId; + } + } +} \ No newline at end of file diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/src/main/java/org/finos/legend/engine/query/sql/providers/shared/project/ProjectCoordinateWrapper.java b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/src/main/java/org/finos/legend/engine/query/sql/providers/shared/project/ProjectCoordinateWrapper.java new file mode 100644 index 00000000000..76e453291c0 --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/src/main/java/org/finos/legend/engine/query/sql/providers/shared/project/ProjectCoordinateWrapper.java @@ -0,0 +1,137 @@ +// 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.providers.shared.project; + +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.finos.legend.engine.query.sql.providers.core.SQLSourceArgument; +import org.finos.legend.engine.query.sql.providers.core.TableSource; + +import java.util.List; +import java.util.Optional; + +public class ProjectCoordinateWrapper +{ + + private static final String ARG_COORDINATES = "coordinates"; + private static final String ARG_PROJECT = "project"; + private static final String ARG_WORKSPACE = "workspace"; + private static final String ARG_GROUP_WORKSPACE = "groupWorkspace"; + + private final Optional coordinates; + private final Optional project; + private final Optional workspace; + private final Optional groupWorkspace; + + private ProjectCoordinateWrapper(Optional coordinates, Optional project, Optional workspace, Optional groupWorkspace) + { + this.coordinates = coordinates; + this.project = project; + this.workspace = workspace; + this.groupWorkspace = groupWorkspace; + } + + public static ProjectCoordinateWrapper coordinates(String coordinates) + { + return new ProjectCoordinateWrapper(Optional.of(coordinates), Optional.empty(), Optional.empty(), Optional.empty()); + } + + public static ProjectCoordinateWrapper workspace(String project, String workspace) + { + return new ProjectCoordinateWrapper(Optional.empty(), Optional.of(project), Optional.of(workspace), Optional.empty()); + } + + public static ProjectCoordinateWrapper groupWorkspace(String project, String groupWorkspace) + { + return new ProjectCoordinateWrapper(Optional.empty(), Optional.of(project), Optional.empty(), Optional.of(groupWorkspace)); + } + + public static ProjectCoordinateWrapper extractFromTableSource(TableSource source) + { + return extractFromTableSource(source, true); + } + + public static ProjectCoordinateWrapper extractFromTableSource(TableSource source, boolean required) + { + Optional coordinates = Optional.ofNullable(source.getArgumentValueAs(ARG_COORDINATES, -1, String.class, false)); + Optional project = Optional.ofNullable(source.getArgumentValueAs(ARG_PROJECT, -1, String.class, false)); + Optional workspace = Optional.ofNullable(source.getArgumentValueAs(ARG_WORKSPACE, -1, String.class, false)); + Optional groupWorkspace = Optional.ofNullable(source.getArgumentValueAs(ARG_GROUP_WORKSPACE, -1, String.class, false)); + + validateArguments(coordinates, project, workspace, groupWorkspace, required); + + return new ProjectCoordinateWrapper(coordinates, project, workspace, groupWorkspace); + } + + public void addProjectCoordinatesAsSQLSourceArguments(List keys) + { + coordinates.ifPresent(value -> keys.add(new SQLSourceArgument(ARG_COORDINATES, null, value))); + project.ifPresent(value -> keys.add(new SQLSourceArgument(ARG_PROJECT, null, value))); + workspace.ifPresent(value -> keys.add(new SQLSourceArgument(ARG_WORKSPACE, null, value))); + groupWorkspace.ifPresent(value -> keys.add(new SQLSourceArgument(ARG_GROUP_WORKSPACE, null, value))); + } + + private static void validateArguments(Optional coordinates, Optional project, Optional workspace, Optional groupWorkspace, boolean required) + { + if (coordinates.isPresent() && (project.isPresent() || workspace.isPresent() || groupWorkspace.isPresent())) + { + throw new IllegalArgumentException("cannot mix coordinates with project/workspace"); + } + if (project.isPresent() && !(workspace.isPresent() || groupWorkspace.isPresent())) + { + throw new IllegalArgumentException("workspace/group workspace must be supplied if loading from project"); + } + + if (required && !(coordinates.isPresent() || project.isPresent())) + { + throw new IllegalArgumentException("coordinates or project/workspace must be supplied"); + } + } + + + public Optional getCoordinates() + { + return coordinates; + } + + public Optional getProject() + { + return project; + } + + public Optional getWorkspace() + { + return workspace; + } + + public Optional getGroupWorkspace() + { + return groupWorkspace; + } + + @Override + public boolean equals(Object o) + { + return EqualsBuilder.reflectionEquals(this, o); + } + + @Override + public int hashCode() + { + return HashCodeBuilder.reflectionHashCode(this); + } + +} \ No newline at end of file diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/src/main/java/org/finos/legend/engine/query/sql/providers/shared/project/ProjectResolvedContext.java b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/src/main/java/org/finos/legend/engine/query/sql/providers/shared/project/ProjectResolvedContext.java new file mode 100644 index 00000000000..8546465c896 --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/src/main/java/org/finos/legend/engine/query/sql/providers/shared/project/ProjectResolvedContext.java @@ -0,0 +1,45 @@ +// 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.providers.shared.project; + +import org.finos.legend.engine.protocol.pure.v1.model.context.PureModelContext; +import org.finos.legend.engine.protocol.pure.v1.model.context.PureModelContextData; + +/** + * This class acts as a holder for a ProjectCoordinateWrapper resolved data + */ +public class ProjectResolvedContext +{ + /** this will be the smallest unit possible, eg. a pointer instead of the full pmcd if available*/ + private final PureModelContext context; + private final PureModelContextData data; + + public ProjectResolvedContext(PureModelContext context, PureModelContextData data) + { + this.context = context; + this.data = data; + } + + public PureModelContext getContext() + { + return context; + } + + public PureModelContextData getData() + { + return data; + } +} diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/src/main/java/org/finos/legend/engine/query/sql/providers/shared/utils/SQLProviderUtils.java b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/src/main/java/org/finos/legend/engine/query/sql/providers/shared/utils/SQLProviderUtils.java new file mode 100644 index 00000000000..0e74abaf1ed --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/src/main/java/org/finos/legend/engine/query/sql/providers/shared/utils/SQLProviderUtils.java @@ -0,0 +1,83 @@ +// 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.providers.shared.utils; + +import org.eclipse.collections.api.list.MutableList; +import org.eclipse.collections.impl.list.mutable.FastList; +import org.eclipse.collections.impl.utility.ListIterate; +import org.finos.legend.engine.protocol.pure.v1.model.context.PureModelContextData; +import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.PackageableElement; +import org.finos.legend.engine.protocol.pure.v1.model.valueSpecification.application.AppliedFunction; +import org.finos.legend.engine.protocol.pure.v1.model.valueSpecification.raw.CString; +import org.finos.legend.engine.protocol.pure.v1.model.valueSpecification.raw.Lambda; +import org.finos.legend.engine.protocol.pure.v1.model.valueSpecification.raw.PackageableElementPtr; + +import java.util.Collections; +import java.util.List; +import java.util.function.Predicate; + +public class SQLProviderUtils +{ + + public static T extractElement(String argumentName, Class type, PureModelContextData pmcd, Predicate predicate) + { + return extractElement(argumentName, pmcd.getElementsOfType(type), predicate); + } + + public static T extractElement(String argumentName, List list, Predicate predicate) + { + MutableList elements = ListIterate.select(list, + element -> predicate.test(element)); + + if (elements.isEmpty()) + { + throw new IllegalArgumentException("No element found for '" + argumentName + "'"); + } + + if (elements.size() > 1) + { + throw new IllegalArgumentException("Multiple elements found for '" + argumentName + "'"); + } + + return elements.getOnly(); + } + + public static Lambda tableToTDS(String databasePath, String schemaName, String tableName) + { + PackageableElementPtr databasePtr = new PackageableElementPtr(); + databasePtr.fullPath = databasePath; + + AppliedFunction tableReferenceFunc = new AppliedFunction(); + tableReferenceFunc.function = "tableReference"; + tableReferenceFunc.fControl = "tableReference_Database_1__String_1__String_1__Table_1_"; + tableReferenceFunc.parameters = FastList.newListWith(databasePtr, new CString(schemaName), new CString(tableName)); + + AppliedFunction tableToTdsFunc = new AppliedFunction(); + tableToTdsFunc.function = "tableToTDS"; + tableToTdsFunc.fControl = "tableToTDS_Table_1__TableTDS_1_"; + tableToTdsFunc.parameters = Collections.singletonList(tableReferenceFunc); + + Lambda lambda = new Lambda(); + lambda.body = Collections.singletonList(tableToTdsFunc); + + return lambda; + } + + public static boolean equalsEscaped(String value, String toMatch) + { + return value.equals(toMatch) || value.equals("\"" + toMatch + "\""); + } +} \ No newline at end of file diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/src/main/java/org/finos/legend/engine/query/sql/providers/shared/utils/TraceUtils.java b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/src/main/java/org/finos/legend/engine/query/sql/providers/shared/utils/TraceUtils.java new file mode 100644 index 00000000000..baed8276557 --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/src/main/java/org/finos/legend/engine/query/sql/providers/shared/utils/TraceUtils.java @@ -0,0 +1,72 @@ +// 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.providers.shared.utils; + +import io.opentracing.Scope; +import io.opentracing.Span; +import io.opentracing.util.GlobalTracer; +import org.eclipse.collections.api.block.procedure.Procedure; + +import java.util.function.Function; +import java.util.function.Supplier; + +public class TraceUtils +{ + private static final String PREFIX = "Legend SQL: "; + + public static void trace(String name, Procedure procedure) + { + Span span = GlobalTracer.get().buildSpan(PREFIX + name).start(); + + try (Scope ignored = GlobalTracer.get().activateSpan(span)) + { + procedure.accept(span); + } + finally + { + span.finish(); + } + } + + public static T trace(String name, Supplier supplier) + { + + Span span = GlobalTracer.get().buildSpan(PREFIX + name).start(); + + try (Scope ignored = GlobalTracer.get().activateSpan(span)) + { + return supplier.get(); + } + finally + { + span.finish(); + } + } + + public static T trace(String name, Function supplier) + { + Span span = GlobalTracer.get().buildSpan(PREFIX + name).start(); + + try (Scope ignored = GlobalTracer.get().activateSpan(span)) + { + return supplier.apply(span); + } + finally + { + span.finish(); + } + } +} \ No newline at end of file diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/src/test/java/org/finos/legend/engine/query/sql/providers/shared/AbstractTestLegendStoreSQLSourceProvider.java b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/src/test/java/org/finos/legend/engine/query/sql/providers/shared/AbstractTestLegendStoreSQLSourceProvider.java new file mode 100644 index 00000000000..80d8c9e2104 --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/src/test/java/org/finos/legend/engine/query/sql/providers/shared/AbstractTestLegendStoreSQLSourceProvider.java @@ -0,0 +1,109 @@ +// 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.providers.shared; + +import org.eclipse.collections.impl.list.mutable.FastList; +import org.finos.legend.engine.protocol.pure.v1.model.context.PureModelContextData; +import org.finos.legend.engine.query.sql.providers.core.SQLSourceProvider; +import org.finos.legend.engine.query.sql.providers.core.TableSource; +import org.finos.legend.engine.query.sql.providers.core.TableSourceArgument; +import org.finos.legend.engine.query.sql.providers.shared.project.ProjectCoordinateLoader; +import org.finos.legend.engine.query.sql.providers.shared.project.ProjectResolvedContext; +import org.junit.Assert; +import org.junit.Test; + +import static org.finos.legend.engine.query.sql.providers.shared.SQLSourceProviderTestUtils.loadPureModelContextFromResource; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public abstract class AbstractTestLegendStoreSQLSourceProvider +{ + @Test + public void testNoProjectOrCoordindates() + { + TableSource tableSource = new TableSource("store", FastList.newListWith( + new TableSourceArgument("store", null, "notfound")) + ); + + testError(tableSource, "coordinates or project/workspace must be supplied"); + } + + @Test + public void testMissingWorkspace() + { + TableSource tableSource = new TableSource("store", FastList.newListWith( + new TableSourceArgument("store", null, "notfound"), + new TableSourceArgument("project", null, "proj1")) + ); + + testError(tableSource, "workspace/group workspace must be supplied if loading from project"); + } + + @Test + public void testMixedCoordinatesWorkspace() + { + TableSource tableSource = new TableSource("store", FastList.newListWith( + new TableSourceArgument("store", null, "notfound"), + new TableSourceArgument("project", null, "proj1"), + new TableSourceArgument("coordinates", null, "group:artifact:version")) + ); + + testError(tableSource, "cannot mix coordinates with project/workspace"); + } + + @Test + public void testMissingStoreParams() + { + String connectionName = "simple::store::DB::H2Connection"; + + PureModelContextData pmcd = loadPureModelContextFromResource("pmcd.pure", this.getClass()); + when(getProjectCoordinateLoader().resolve(any(), any())).thenReturn(new ProjectResolvedContext(pmcd, pmcd)); + + TableSource table = new TableSource("relationalStore", FastList.newListWith( + new TableSourceArgument("coordinates", null, "group:artifact:version"), + new TableSourceArgument("connection", null, connectionName))); + + IllegalArgumentException exception = Assert.assertThrows("Should throw given no store found", IllegalArgumentException.class, () -> getProvider().resolve(FastList.newListWith(table), null, FastList.newList())); + Assert.assertEquals("'store' parameter is required", exception.getMessage()); + } + + @Test + public void testStoreNotFound() + { + when(getProjectCoordinateLoader().resolve(any(), any())).thenReturn(new ProjectResolvedContext(mock(PureModelContextData.class), mock(PureModelContextData.class))); + String connectionName = "simple::store::DB::H2Connection"; + + TableSource table = new TableSource("store", FastList.newListWith( + new TableSourceArgument("store", null, "simple::store::DBForSQL"), + new TableSourceArgument("connection", null, connectionName), + new TableSourceArgument("coordinates", null, "group:artifact:version"))); + + IllegalArgumentException exception = Assert.assertThrows("Should throw given no store found", IllegalArgumentException.class, () -> getProvider().resolve(FastList.newListWith(table), null, FastList.newList())); + Assert.assertEquals("No element found for 'store'", exception.getMessage()); + } + + protected void testError(TableSource tableSource, String error) + { + IllegalArgumentException exception = Assert.assertThrows("Should throw error", IllegalArgumentException.class, () -> getProvider().resolve(FastList.newListWith(tableSource), null, FastList.newList())); + Assert.assertEquals(error, exception.getMessage()); + } + + protected abstract SQLSourceProvider getProvider(); + + protected abstract ProjectCoordinateLoader getProjectCoordinateLoader(); + +} diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/src/test/java/org/finos/legend/engine/query/sql/providers/shared/SQLSourceProviderTestUtils.java b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/src/test/java/org/finos/legend/engine/query/sql/providers/shared/SQLSourceProviderTestUtils.java new file mode 100644 index 00000000000..ad318655867 --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/src/test/java/org/finos/legend/engine/query/sql/providers/shared/SQLSourceProviderTestUtils.java @@ -0,0 +1,79 @@ +// 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.providers.shared; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.io.IOUtils; +import org.finos.legend.engine.language.pure.grammar.from.PureGrammarParser; +import org.finos.legend.engine.protocol.pure.v1.PureProtocolObjectMapperFactory; +import org.finos.legend.engine.protocol.pure.v1.model.context.PureModelContextData; +import org.junit.Assert; + +import java.io.IOException; +import java.util.Objects; + +public class SQLSourceProviderTestUtils +{ + + //TODO replace once equals method added in legend + public static void assertLogicalEquality(Object expected, Object actual) + { + try + { + ObjectMapper mapper = PureProtocolObjectMapperFactory.getNewObjectMapper(); + Assert.assertEquals( + mapper.writeValueAsString(expected), + mapper.writeValueAsString(actual)); + } + catch (JsonProcessingException e) + { + throw new RuntimeException(e); + } + } + + public static PureModelContextData loadPureModelContextFromResource(String resource, Class clazz) + { + String model = getResource(resource, clazz); + return PureModelContextData.newBuilder().withPureModelContextData(PureGrammarParser.newInstance().parseModel(model)).build(); + } + + public static String getResource(String resource, Class clazz) + { + try + { + return IOUtils.toString(Objects.requireNonNull(clazz.getClassLoader().getResourceAsStream(resource))); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } + + public static T loadFromResources(String resource, TypeReference typeReference, Class clazz) + { + String sources = getResource(resource, clazz); + try + { + return PureProtocolObjectMapperFactory.getNewObjectMapper().readValue(sources, typeReference); + } + catch (JsonProcessingException e) + { + throw new RuntimeException(e); + } + } +} \ No newline at end of file diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/src/test/java/org/finos/legend/engine/query/sql/providers/shared/TestFunctionSQLSourceProvider.java b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/src/test/java/org/finos/legend/engine/query/sql/providers/shared/TestFunctionSQLSourceProvider.java new file mode 100644 index 00000000000..7b3f4e168dd --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/src/test/java/org/finos/legend/engine/query/sql/providers/shared/TestFunctionSQLSourceProvider.java @@ -0,0 +1,181 @@ +// 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.providers.shared; + +import org.eclipse.collections.impl.list.mutable.FastList; +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.packageableElement.domain.Function; +import org.finos.legend.engine.protocol.pure.v1.model.valueSpecification.raw.Lambda; +import org.finos.legend.engine.query.sql.providers.core.SQLSource; +import org.finos.legend.engine.query.sql.providers.core.SQLSourceArgument; +import org.finos.legend.engine.query.sql.providers.core.SQLSourceResolvedContext; +import org.finos.legend.engine.query.sql.providers.core.TableSource; +import org.finos.legend.engine.query.sql.providers.core.TableSourceArgument; +import org.finos.legend.engine.query.sql.providers.shared.project.ProjectCoordinateLoader; +import org.finos.legend.engine.query.sql.providers.shared.project.ProjectCoordinateWrapper; +import org.finos.legend.engine.query.sql.providers.shared.project.ProjectResolvedContext; +import org.finos.legend.engine.query.sql.providers.shared.utils.SQLProviderUtils; +import org.finos.legend.engine.shared.core.operational.errorManagement.EngineException; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import static org.finos.legend.engine.query.sql.providers.shared.SQLSourceProviderTestUtils.loadPureModelContextFromResource; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class TestFunctionSQLSourceProvider +{ + @Mock + private ProjectCoordinateLoader projectCoordinateLoader; + + private FunctionSQLSourceProvider provider; + + @Before + public void setup() + { + this.provider = new FunctionSQLSourceProvider(projectCoordinateLoader); + } + + @Test + public void testType() + { + Assert.assertEquals("func", provider.getType()); + } + + @Test + public void testWorkspace() + { + String functionName = "simple::func::simpleFunction_String_MANY__TabularDataSet_1_"; + + ProjectCoordinateWrapper coordinates = ProjectCoordinateWrapper.workspace("proj1", "ws1"); + + PureModelContextData pmcd = loadPureModelContextFromResource("function-pmcd.pure", this.getClass()); + Function function = SQLProviderUtils.extractElement("function", pmcd.getElementsOfType(Function.class), f -> f.getPath().equals(functionName)); + + when(projectCoordinateLoader.resolve(eq(coordinates), any())).thenReturn(new ProjectResolvedContext(pmcd, pmcd)); + + TableSource tableSource = createTableSource(functionName, + new TableSourceArgument("project", null, "proj1"), + new TableSourceArgument("workspace", null, "ws1") + ); + + Lambda lambda = new Lambda(); + lambda.body = function.body; + lambda.parameters = function.parameters; + + SQLSource expected = new SQLSource("func", lambda, null, null, FastList.newList(), null, FastList.newListWith( + new SQLSourceArgument("path", 0, functionName), + new SQLSourceArgument("project", null, "proj1"), + new SQLSourceArgument("workspace", null, "ws1") + )); + + testSuccess(tableSource, pmcd, expected); + } + + @Test + public void testCoordinates() + { + String functionName = "simple::func::simpleFunction_String_MANY__TabularDataSet_1_"; + + ProjectCoordinateWrapper coordinates = ProjectCoordinateWrapper.coordinates("proj1:art:1.0.0"); + + PureModelContextData pmcd = loadPureModelContextFromResource("function-pmcd.pure", this.getClass()); + Function function = SQLProviderUtils.extractElement("function", pmcd.getElementsOfType(Function.class), f -> f.getPath().equals(functionName)); + PureModelContextPointer pointer = new PureModelContextPointer(); + + when(projectCoordinateLoader.resolve(eq(coordinates), any())).thenReturn(new ProjectResolvedContext(pointer, pmcd)); + + TableSource tableSource = createTableSource(functionName, + new TableSourceArgument("coordinates", null, "proj1:art:1.0.0") + ); + + Lambda lambda = new Lambda(); + lambda.body = function.body; + lambda.parameters = function.parameters; + + SQLSource expected = new SQLSource("func", lambda, null, null, FastList.newList(), null, FastList.newListWith( + new SQLSourceArgument("path", 0, functionName), + new SQLSourceArgument("coordinates", null, "proj1:art:1.0.0") + )); + + testSuccess(tableSource, pointer, expected); + } + + @Test + public void testNoProjectOrCoordinates() + { + TableSource tableSource = createTableSource("simple::func__TabularDataSet_1_"); + testException(tableSource, IllegalArgumentException.class, "coordinates or project/workspace must be supplied"); + } + + @Test + public void testNoWorkspaceWithProject() + { + TableSource tableSource = createTableSource("simple::func__TabularDataSet_1_", new TableSourceArgument("project", null, "proj1")); + testException(tableSource, IllegalArgumentException.class, "workspace/group workspace must be supplied if loading from project"); + } + + @Test + public void testNotTDSFunc() + { + String functionName = "simple::func::nonTdsFunction__String_1_"; + + ProjectCoordinateWrapper coordinates = ProjectCoordinateWrapper.coordinates("proj1:art:1.0.0"); + + PureModelContextData pmcd = loadPureModelContextFromResource("function-pmcd.pure", this.getClass()); + + when(projectCoordinateLoader.resolve(eq(coordinates), any())).thenReturn(new ProjectResolvedContext(pmcd, pmcd)); + + TableSource tableSource = createTableSource(functionName, + new TableSourceArgument("coordinates", null, "proj1:art:1.0.0") + ); + + testException(tableSource, EngineException.class, "Function " + functionName + " does not return Tabular data type"); + } + + private void testException(TableSource tableSource, Class throwable, String expected) + { + T exception = Assert.assertThrows("Should throw given no service found", throwable, () -> provider.resolve(FastList.newListWith(tableSource), null, FastList.newList())); + Assert.assertEquals(expected, exception.getMessage()); + } + + private void testSuccess(TableSource tableSource, PureModelContext expectedContext, SQLSource expected) + { + SQLSourceResolvedContext result = provider.resolve(FastList.newListWith(tableSource), null, FastList.newList()); + + //ASSERT + Assert.assertEquals(FastList.newListWith(expectedContext), result.getPureModelContexts()); + Assert.assertEquals(1, result.getSources().size()); + + SQLSourceProviderTestUtils.assertLogicalEquality(expected, result.getSources().get(0)); + } + + private final TableSource createTableSource(String func, TableSourceArgument... extraArguments) + { + return new TableSource("func", FastList.newListWith( + new TableSourceArgument(null, 0, func)).with(extraArguments) + ); + } +} + diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/src/test/resources/function-pmcd.pure b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/src/test/resources/function-pmcd.pure new file mode 100644 index 00000000000..fbeb18c1a2d --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/legend-engine-xt-sql-providers-shared/src/test/resources/function-pmcd.pure @@ -0,0 +1,22 @@ +###Pure +import simple::model::*; + +Class simple::model::Person +{ + firstName: String[1]; + lastName: String[1]; +} +function simple::func::simpleFunction(lastNames:String[*]):meta::pure::tds::TabularDataSet[1] +{ + simple::model::Person.all() + ->filter(p | $p.lastName->in($lastNames)) + ->project([ + col(x | $x.firstName, 'first name'), + col(x | $x.lastName, 'last name') + ]) +} + +function simple::func::nonTdsFunction():String[1] +{ + '' +} \ No newline at end of file diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-providers/pom.xml b/legend-engine-xts-sql/legend-engine-xt-sql-providers/pom.xml new file mode 100644 index 00000000000..a5498551eed --- /dev/null +++ b/legend-engine-xts-sql/legend-engine-xt-sql-providers/pom.xml @@ -0,0 +1,35 @@ + + + + + org.finos.legend.engine + legend-engine-xts-sql + 4.32.1-SNAPSHOT + + 4.0.0 + + legend-engine-xt-sql-providers + pom + Legend Engine - XTS - SQL - Providers + + + legend-engine-xt-sql-providers-core + legend-engine-xt-sql-providers-shared + legend-engine-xt-sql-providers-relationalStore + legend-engine-xt-sql-providers-service + + \ No newline at end of file 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 c1e0cd03af2..f4b79ad6c04 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 @@ -96,6 +96,10 @@ org.finos.legend.engine legend-engine-xt-sql-compiler + + org.finos.legend.engine + legend-engine-xt-sql-providers-core + 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 index 798afbdf56b..58c3430da52 100644 --- 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 @@ -47,13 +47,12 @@ 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.query.sql.providers.core.SQLContext; +import org.finos.legend.engine.query.sql.providers.core.SQLSource; +import org.finos.legend.engine.query.sql.providers.core.SQLSourceProvider; +import org.finos.legend.engine.query.sql.providers.core.SQLSourceResolvedContext; +import org.finos.legend.engine.query.sql.providers.core.TableSource; +import org.finos.legend.engine.query.sql.api.sources.TableSourceArgument; 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; @@ -91,7 +90,8 @@ public class SQLExecutor private final Iterable transformers; private final MutableMap providers; - public SQLExecutor(ModelManager modelManager, PlanExecutor planExecutor, + public SQLExecutor(ModelManager modelManager, + PlanExecutor planExecutor, Function> routerExtensions, List providers, Iterable transformers) @@ -165,7 +165,7 @@ public SingleExecutionPlan plan(Query query, SQLContext context, MutableList profiles) { - SQLContext context = new SQLContext(query, Maps.mutable.of()); + SQLContext context = new SQLContext(query); 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()); @@ -243,6 +243,15 @@ private Pair, PureModelContext> getSourcesAndModel(Query private SQLSourceResolvedContext resolve(MutableCollection tables, SQLContext context, SQLSourceProvider extension, MutableList profiles) { + //TODO remove deprecated flow + org.finos.legend.engine.query.sql.api.sources.SQLSourceResolvedContext resolved = extension.resolve(tables.collect(t -> new org.finos.legend.engine.query.sql.api.sources.TableSource(t.getType(), + ListIterate.collect(t.getArguments(), a -> new TableSourceArgument(a.getName(), a.getIndex(), a.getValue())))).toList(), new org.finos.legend.engine.query.sql.api.sources.SQLContext(context.getQuery()), profiles); + + if (resolved != null) + { + return new SQLSourceResolvedContext(resolved.getPureModelContexts(), ListIterate.collect(resolved.getSources(), s -> new SQLSource(s.getType(), s.getFunc(), s.getMapping(), s.getRuntime(), s.getExecutionOptions(), s.getExecutionContext(), s.getKey()))); + } + return extension.resolve(tables.toList(), context, profiles); } diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/sources/SQLSourceTranslator.java b/legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/SQLSourceTranslator.java similarity index 96% rename from legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/sources/SQLSourceTranslator.java rename to legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/SQLSourceTranslator.java index ede0ac21bba..64ae7757879 100644 --- a/legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/sources/SQLSourceTranslator.java +++ b/legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/SQLSourceTranslator.java @@ -13,7 +13,7 @@ // limitations under the License. // -package org.finos.legend.engine.query.sql.api.sources; +package org.finos.legend.engine.query.sql.api; import org.eclipse.collections.api.RichIterable; import org.eclipse.collections.impl.list.mutable.FastList; @@ -25,6 +25,9 @@ import org.finos.legend.engine.language.pure.compiler.toPureGraph.PureModel; import org.finos.legend.engine.protocol.pure.v1.model.executionOption.ExecutionOption; import org.finos.legend.engine.protocol.pure.v1.model.valueSpecification.raw.executionContext.ExecutionContext; +import org.finos.legend.engine.query.sql.providers.core.SQLSource; +import org.finos.legend.engine.query.sql.providers.core.SQLSourceArgument; + import org.finos.legend.pure.generated.*; import java.util.Objects; diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/sources/TableSourceExtractor.java b/legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/TableSourceExtractor.java similarity index 98% rename from legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/sources/TableSourceExtractor.java rename to legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/TableSourceExtractor.java index ad1dd23d4b4..5d05297574d 100644 --- a/legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/sources/TableSourceExtractor.java +++ b/legend-engine-xts-sql/legend-engine-xt-sql-query/src/main/java/org/finos/legend/engine/query/sql/api/TableSourceExtractor.java @@ -13,12 +13,14 @@ // limitations under the License. // -package org.finos.legend.engine.query.sql.api.sources; +package org.finos.legend.engine.query.sql.api; import org.eclipse.collections.api.factory.Lists; import org.eclipse.collections.api.factory.Sets; import org.eclipse.collections.impl.utility.ListIterate; import org.finos.legend.engine.protocol.sql.metamodel.*; +import org.finos.legend.engine.query.sql.providers.core.TableSource; +import org.finos.legend.engine.query.sql.providers.core.TableSourceArgument; import java.util.Collection; import java.util.Collections; 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 9d463443298..fca71827a75 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 @@ -35,8 +35,8 @@ import org.finos.legend.engine.protocol.sql.metamodel.Query; 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.SQLSourceProvider; +import org.finos.legend.engine.query.sql.providers.core.SQLContext; +import org.finos.legend.engine.query.sql.providers.core.SQLSourceProvider; import org.finos.legend.engine.shared.core.kerberos.ProfileManagerHelper; import org.finos.legend.engine.shared.core.operational.logs.LoggingEventType; import org.finos.legend.pure.generated.Root_meta_pure_extension_Extension; @@ -101,7 +101,7 @@ 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()); + SQLContext context = new SQLContext(query); Result result = this.executor.execute(query, request.getRemoteUser(), context, profiles); @@ -129,7 +129,7 @@ public Lambda generateLambda(@Context HttpServletRequest request, Query query, @ { MutableList profiles = ProfileManagerHelper.extractProfiles(pm); - SQLContext context = new SQLContext(query, Maps.mutable.of()); + SQLContext context = new SQLContext(query); return executor.lambda(query, context, profiles); } @@ -151,7 +151,7 @@ public ExecutionPlan generatePlan(@Context HttpServletRequest request, Query que { MutableList profiles = ProfileManagerHelper.extractProfiles(pm); - SQLContext context = new SQLContext(query, Maps.mutable.of()); + SQLContext context = new SQLContext(query); return this.executor.plan(query, context, profiles); } diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-query/src/test/java/org/finos/legend/engine/query/sql/api/sources/TableSourceExtractorTest.java b/legend-engine-xts-sql/legend-engine-xt-sql-query/src/test/java/org/finos/legend/engine/query/sql/api/TableSourceExtractorTest.java similarity index 94% rename from legend-engine-xts-sql/legend-engine-xt-sql-query/src/test/java/org/finos/legend/engine/query/sql/api/sources/TableSourceExtractorTest.java rename to legend-engine-xts-sql/legend-engine-xt-sql-query/src/test/java/org/finos/legend/engine/query/sql/api/TableSourceExtractorTest.java index 9df7972debf..b92133ae745 100644 --- a/legend-engine-xts-sql/legend-engine-xt-sql-query/src/test/java/org/finos/legend/engine/query/sql/api/sources/TableSourceExtractorTest.java +++ b/legend-engine-xts-sql/legend-engine-xt-sql-query/src/test/java/org/finos/legend/engine/query/sql/api/TableSourceExtractorTest.java @@ -13,12 +13,14 @@ // limitations under the License. // -package org.finos.legend.engine.query.sql.api.sources; +package org.finos.legend.engine.query.sql.api; import org.eclipse.collections.api.factory.Sets; import org.eclipse.collections.impl.list.mutable.FastList; import org.finos.legend.engine.language.sql.grammar.from.SQLGrammarParser; import org.finos.legend.engine.protocol.sql.metamodel.Statement; +import org.finos.legend.engine.query.sql.providers.core.TableSource; +import org.finos.legend.engine.query.sql.providers.core.TableSourceArgument; import org.junit.Assert; import org.junit.Test; @@ -26,7 +28,6 @@ public class TableSourceExtractorTest { - private static final TableSource TABLE_1 = new TableSource("service", FastList.newListWith(new TableSourceArgument(null, 0, "table1"))); private static final TableSource TABLE_2 = new TableSource("service", FastList.newListWith(new TableSourceArgument(null, 0, "table2"))); diff --git a/legend-engine-xts-sql/legend-engine-xt-sql-query/src/test/java/org/finos/legend/engine/query/sql/api/sources/TestSQLSourceProvider.java b/legend-engine-xts-sql/legend-engine-xt-sql-query/src/test/java/org/finos/legend/engine/query/sql/api/TestSQLSourceProvider.java similarity index 89% rename from legend-engine-xts-sql/legend-engine-xt-sql-query/src/test/java/org/finos/legend/engine/query/sql/api/sources/TestSQLSourceProvider.java rename to legend-engine-xts-sql/legend-engine-xt-sql-query/src/test/java/org/finos/legend/engine/query/sql/api/TestSQLSourceProvider.java index b09021383c9..eb5857cae0e 100644 --- a/legend-engine-xts-sql/legend-engine-xt-sql-query/src/test/java/org/finos/legend/engine/query/sql/api/sources/TestSQLSourceProvider.java +++ b/legend-engine-xts-sql/legend-engine-xt-sql-query/src/test/java/org/finos/legend/engine/query/sql/api/TestSQLSourceProvider.java @@ -13,7 +13,7 @@ // limitations under the License. // -package org.finos.legend.engine.query.sql.api.sources; +package org.finos.legend.engine.query.sql.api; import org.eclipse.collections.api.LazyIterable; import org.eclipse.collections.api.list.MutableList; @@ -24,6 +24,13 @@ import org.finos.legend.engine.protocol.pure.v1.model.context.PureModelContextData; import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.service.PureSingleExecution; import org.finos.legend.engine.protocol.pure.v1.model.packageableElement.service.Service; +import org.finos.legend.engine.query.sql.api.sources.SQLSourceProvider; +import org.finos.legend.engine.query.sql.providers.core.SQLContext; +import org.finos.legend.engine.query.sql.providers.core.SQLSource; +import org.finos.legend.engine.query.sql.providers.core.SQLSourceArgument; +import org.finos.legend.engine.query.sql.providers.core.SQLSourceResolvedContext; +import org.finos.legend.engine.query.sql.providers.core.TableSource; +import org.finos.legend.engine.query.sql.providers.core.TableSourceArgument; import org.pac4j.core.profile.CommonProfile; import java.io.BufferedReader; 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 408501ec7c3..c2f383ee6e5 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 @@ -39,7 +39,7 @@ 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.query.sql.api.TestSQLSourceProvider; import org.finos.legend.engine.shared.core.api.grammar.RenderStyle; import org.finos.legend.engine.shared.core.deployment.DeploymentMode; import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory; diff --git a/legend-engine-xts-sql/pom.xml b/legend-engine-xts-sql/pom.xml index b9297f87eee..6948a39c84a 100644 --- a/legend-engine-xts-sql/pom.xml +++ b/legend-engine-xts-sql/pom.xml @@ -34,6 +34,7 @@ legend-engine-xt-sql-grammar-integration legend-engine-xt-sql-postgres-server legend-engine-xt-sql-protocol + legend-engine-xt-sql-providers legend-engine-xt-sql-pure legend-engine-xt-sql-pure-metamodel legend-engine-xt-sql-query diff --git a/pom.xml b/pom.xml index 0c66a7c491d..306e50fa853 100644 --- a/pom.xml +++ b/pom.xml @@ -1995,6 +1995,32 @@ ${project.version} test-jar + + org.finos.legend.engine + legend-engine-xt-sql-providers-core + ${project.version} + + + org.finos.legend.engine + legend-engine-xt-sql-providers-shared + ${project.version} + + + org.finos.legend.engine + legend-engine-xt-sql-providers-shared + test-jar + ${project.version} + + + org.finos.legend.engine + legend-engine-xt-sql-providers-relationalStore + ${project.version} + + + org.finos.legend.engine + legend-engine-xt-sql-providers-service + ${project.version} + org.finos.legend.engine legend-engine-xt-sql-grammar-integration