Skip to content

Commit 40c5944

Browse files
dai-chenSwiddisnoChargerMebsina
authored
[Backport 2.19-dev] Publish internal modules separately for downstream reuse (#4723)
* Publish internal modules separately for downstream reuse (#4484) Co-authored-by: Louis Chu <[email protected]> Co-authored-by: Chen Dai <[email protected]> Co-authored-by: Mebsina <[email protected]> Signed-off-by: Chen Dai <[email protected]> * Add test branch and downgrade JDK in maven publish workflow Signed-off-by: Chen Dai <[email protected]> * Add 2.19-dev branch to workflow and bump Calcite version in api module Signed-off-by: Chen Dai <[email protected]> --------- Signed-off-by: Chen Dai <[email protected]> Co-authored-by: Simeon Widdis <[email protected]> Co-authored-by: Louis Chu <[email protected]> Co-authored-by: Mebsina <[email protected]>
1 parent aa0635d commit 40c5944

File tree

8 files changed

+703
-0
lines changed

8 files changed

+703
-0
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
name: Publish unified query modules to maven
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
branches:
7+
- main
8+
- '[0-9]+.[0-9]+'
9+
- '[0-9]+.x'
10+
- 2.19-dev
11+
12+
env:
13+
SNAPSHOT_REPO_URL: https://ci.opensearch.org/ci/dbc/snapshots/maven/
14+
15+
jobs:
16+
publish-unified-query-modules:
17+
strategy:
18+
fail-fast: false
19+
if: github.repository == 'opensearch-project/sql'
20+
runs-on: ubuntu-latest
21+
22+
permissions:
23+
id-token: write
24+
contents: write
25+
26+
steps:
27+
- uses: actions/setup-java@v3
28+
with:
29+
distribution: temurin # Temurin is a distribution of adoptium
30+
java-version: 11
31+
- uses: actions/checkout@v3
32+
- name: Load secret
33+
uses: 1password/load-secrets-action@v2
34+
with:
35+
# Export loaded secrets as environment variables
36+
export-env: true
37+
env:
38+
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
39+
MAVEN_SNAPSHOTS_S3_REPO: op://opensearch-infra-secrets/maven-snapshots-s3/repo
40+
MAVEN_SNAPSHOTS_S3_ROLE: op://opensearch-infra-secrets/maven-snapshots-s3/role
41+
- name: Configure AWS credentials
42+
uses: aws-actions/configure-aws-credentials@v5
43+
with:
44+
role-to-assume: ${{ env.MAVEN_SNAPSHOTS_S3_ROLE }}
45+
aws-region: us-east-1
46+
- name: publish snapshots to maven
47+
run: |
48+
./gradlew publishUnifiedQueryPublicationToSnapshotsRepository

api/README.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Unified Query API
2+
3+
This module provides a high-level integration layer for the Calcite-based query engine, enabling external systems such as Apache Spark or command-line tools to parse and analyze queries without exposing low-level internals.
4+
5+
## Overview
6+
7+
The `UnifiedQueryPlanner` serves as the primary entry point for external consumers. It accepts PPL (Piped Processing Language) queries and returns Calcite `RelNode` logical plans as intermediate representation.
8+
9+
## Usage
10+
11+
Use the declarative, fluent builder API to initialize the `UnifiedQueryPlanner`.
12+
13+
```java
14+
UnifiedQueryPlanner planner = UnifiedQueryPlanner.builder()
15+
.language(QueryType.PPL)
16+
.catalog("opensearch", schema)
17+
.defaultNamespace("opensearch")
18+
.cacheMetadata(true)
19+
.build();
20+
21+
RelNode plan = planner.plan("source = opensearch.test");
22+
```
23+
24+
## Development & Testing
25+
26+
A set of unit tests is provided to validate planner behavior.
27+
28+
To run tests:
29+
30+
```
31+
./gradlew :api:test
32+
```
33+
34+
## Integration Guide
35+
36+
This guide walks through how to integrate unified query planner into your application.
37+
38+
### Step 1: Add Dependency
39+
40+
The module is currently published as a snapshot to the AWS Sonatype Snapshots repository. To include it as a dependency in your project, add the following to your `pom.xml` or `build.gradle`:
41+
42+
```xml
43+
<dependency>
44+
<groupId>org.opensearch.query</groupId>
45+
<artifactId>unified-query-api</artifactId>
46+
<version>YOUR_VERSION_HERE</version>
47+
</dependency>
48+
```
49+
50+
### Step 2: Implement a Calcite Schema
51+
52+
You must implement the Calcite `Schema` interface and register them using the fluent `catalog()` method on the builder.
53+
54+
```java
55+
public class MySchema extends AbstractSchema {
56+
@Override
57+
protected Map<String, Table> getTableMap() {
58+
return Map.of(
59+
"test_table",
60+
new AbstractTable() {
61+
@Override
62+
public RelDataType getRowType(RelDataTypeFactory typeFactory) {
63+
return typeFactory.createStructType(
64+
List.of(typeFactory.createSqlType(SqlTypeName.INTEGER)),
65+
List.of("id"));
66+
}
67+
});
68+
}
69+
}
70+
```
71+
72+
## Future Work
73+
74+
- Expand support to SQL language.
75+
- Extend planner to generate optimized physical plans using Calcite's optimization frameworks.

api/build.gradle

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
plugins {
7+
id 'java-library'
8+
id 'jacoco'
9+
id 'com.diffplug.spotless'
10+
}
11+
12+
dependencies {
13+
api project(':ppl')
14+
15+
testImplementation group: 'junit', name: 'junit', version: '4.13.2'
16+
testImplementation group: 'org.hamcrest', name: 'hamcrest-library', version: "${hamcrest_version}"
17+
testImplementation group: 'org.mockito', name: 'mockito-core', version: "${mockito_version}"
18+
testImplementation group: 'org.apache.calcite', name: 'calcite-testkit', version: '1.41.0'
19+
}
20+
21+
spotless {
22+
java {
23+
target fileTree('.') {
24+
include '**/*.java'
25+
exclude '**/build/**', '**/build-*/**', 'src/main/gen/**'
26+
}
27+
importOrder()
28+
removeUnusedImports()
29+
trimTrailingWhitespace()
30+
endWithNewline()
31+
googleJavaFormat('1.17.0').reflowLongStrings().groupArtifact('com.google.googlejavaformat:google-java-format')
32+
}
33+
}
34+
35+
test {
36+
testLogging {
37+
events "passed", "skipped", "failed"
38+
exceptionFormat "full"
39+
}
40+
}
41+
42+
jacocoTestReport {
43+
reports {
44+
html.required = true
45+
xml.required = true
46+
}
47+
afterEvaluate {
48+
classDirectories.setFrom(files(classDirectories.files.collect {
49+
fileTree(dir: it,
50+
exclude: ['**/antlr/parser/**'])
51+
}))
52+
}
53+
}
54+
test.finalizedBy(project.tasks.jacocoTestReport)
55+
jacocoTestCoverageVerification {
56+
violationRules {
57+
rule {
58+
limit {
59+
minimum = 0.9
60+
}
61+
62+
}
63+
}
64+
afterEvaluate {
65+
classDirectories.setFrom(files(classDirectories.files.collect {
66+
fileTree(dir: it,
67+
exclude: ['**/antlr/parser/**'])
68+
}))
69+
}
70+
}
71+
check.dependsOn jacocoTestCoverageVerification
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package org.opensearch.sql.api;
2+
3+
import java.util.Map;
4+
import java.util.Set;
5+
import org.opensearch.sql.datasource.DataSourceService;
6+
import org.opensearch.sql.datasource.RequestContext;
7+
import org.opensearch.sql.datasource.model.DataSource;
8+
import org.opensearch.sql.datasource.model.DataSourceMetadata;
9+
10+
/** A DataSourceService that assumes no access to data sources */
11+
public class EmptyDataSourceService implements DataSourceService {
12+
public EmptyDataSourceService() {}
13+
14+
@Override
15+
public DataSource getDataSource(String dataSourceName) {
16+
return null;
17+
}
18+
19+
@Override
20+
public Set<DataSourceMetadata> getDataSourceMetadata(boolean isDefaultDataSourceRequired) {
21+
return Set.of();
22+
}
23+
24+
@Override
25+
public DataSourceMetadata getDataSourceMetadata(String name) {
26+
return null;
27+
}
28+
29+
@Override
30+
public void createDataSource(DataSourceMetadata metadata) {}
31+
32+
@Override
33+
public void updateDataSource(DataSourceMetadata dataSourceMetadata) {}
34+
35+
@Override
36+
public void patchDataSource(Map<String, Object> dataSourceData) {}
37+
38+
@Override
39+
public void deleteDataSource(String dataSourceName) {}
40+
41+
@Override
42+
public Boolean dataSourceExists(String dataSourceName) {
43+
return false;
44+
}
45+
46+
@Override
47+
public DataSourceMetadata verifyDataSourceAccessAndGetRawMetadata(
48+
String dataSourceName, RequestContext context) {
49+
return null;
50+
}
51+
}

0 commit comments

Comments
 (0)