Skip to content

Commit 2dde6f3

Browse files
committed
RelNode -> Substrait Plan -> JNI -> Substrait Plan
Signed-off-by: Lantao Jin <ltjin@amazon.com>
1 parent 55bef39 commit 2dde6f3

41 files changed

Lines changed: 3668 additions & 9 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

build-and-run-demo.sh

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#!/bin/bash
2+
3+
# Build and run the Java-C++ integration demo
4+
5+
set -e
6+
7+
echo "=== Building OpenSearch SQL Native Engine Demo ==="
8+
9+
# Check required tools
10+
if ! command -v cmake &> /dev/null; then
11+
echo "Error: cmake is required but not installed."
12+
exit 1
13+
fi
14+
15+
if ! command -v make &> /dev/null; then
16+
echo "Error: make is required but not installed."
17+
exit 1
18+
fi
19+
20+
# Build project
21+
echo "Building native module..."
22+
./gradlew :native:build
23+
24+
# Run demo
25+
echo ""
26+
echo "Running demo..."
27+
./gradlew :native:run -PmainClass=org.opensearch.sql.NativeEngineDemo
28+
29+
echo ""
30+
echo "=== Build and demo completed ==="

common/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ configurations.all {
6363
resolutionStrategy.force "com.fasterxml.jackson.core:jackson-databind:${versions.jackson_databind}"
6464
resolutionStrategy.force "org.apache.httpcomponents:httpcore:4.4.13"
6565
resolutionStrategy.force "joda-time:joda-time:2.10.12"
66-
resolutionStrategy.force "org.slf4j:slf4j-api:1.7.36"
66+
resolutionStrategy.force "org.slf4j:slf4j-api:2.0.17"
6767
}
6868

6969
spotless {

core/src/main/java/org/opensearch/sql/ast/statement/Explain.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ public enum ExplainFormat {
4141
EXTENDED,
4242
COST,
4343
/** Formats explain output in yaml format. */
44-
YAML
44+
YAML,
45+
/** Explain output to substrait plan. */
46+
SUBSTRAIT
4547
}
4648

4749
public static ExplainFormat format(String format) {

core/src/main/java/org/opensearch/sql/calcite/utils/PlanUtils.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@
2525
import org.apache.calcite.rel.RelHomogeneousShuttle;
2626
import org.apache.calcite.rel.RelNode;
2727
import org.apache.calcite.rel.RelShuttle;
28+
import org.apache.calcite.rel.RelVisitor;
2829
import org.apache.calcite.rel.core.Project;
2930
import org.apache.calcite.rel.core.Sort;
3031
import org.apache.calcite.rel.core.TableScan;
32+
import org.apache.calcite.rel.logical.LogicalAggregate;
3133
import org.apache.calcite.rel.logical.LogicalProject;
3234
import org.apache.calcite.rel.logical.LogicalSort;
3335
import org.apache.calcite.rel.type.RelDataType;
@@ -511,6 +513,24 @@ static boolean containsRexOver(LogicalProject project) {
511513
return project.getProjects().stream().anyMatch(RexOver::containsOver);
512514
}
513515

516+
static boolean containsAggregate(RelNode node) {
517+
try {
518+
(new RelVisitor() {
519+
public void visit(RelNode node, int ordinal, RelNode parent) {
520+
if (node instanceof LogicalAggregate) {
521+
throw Util.FoundOne.NULL;
522+
} else {
523+
super.visit(node, ordinal, parent);
524+
}
525+
}
526+
})
527+
.go(node);
528+
return false;
529+
} catch (Util.FoundOne var3) {
530+
return true;
531+
}
532+
}
533+
514534
/**
515535
* The LogicalSort is a LIMIT that should be pushed down when its fetch field is not null and its
516536
* collation is empty. For example: <code>sort name | head 5</code> should not be pushed down

cpp/CMakeLists.txt

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
cmake_minimum_required(VERSION 3.16)
2+
project(opensearch-sql-native)
3+
4+
set(CMAKE_CXX_STANDARD 20)
5+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
6+
7+
# Set output directories
8+
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
9+
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
10+
11+
# Find JNI
12+
find_package(JNI REQUIRED)
13+
if(JNI_FOUND)
14+
message(STATUS "JNI_INCLUDE_DIRS=${JNI_INCLUDE_DIRS}")
15+
message(STATUS "JNI_LIBRARIES=${JNI_LIBRARIES}")
16+
endif()
17+
18+
# Find Protobuf
19+
find_package(Protobuf REQUIRED)
20+
21+
# Generate protobuf files
22+
set(PROTO_DIR ${CMAKE_SOURCE_DIR}/proto)
23+
set(GENERATED_DIR ${CMAKE_BINARY_DIR}/generated)
24+
file(MAKE_DIRECTORY ${GENERATED_DIR})
25+
26+
# Generate protobuf C++ files
27+
execute_process(
28+
COMMAND ${Protobuf_PROTOC_EXECUTABLE}
29+
--proto_path=${PROTO_DIR}
30+
--cpp_out=${GENERATED_DIR}
31+
${PROTO_DIR}/substrait/plan.proto
32+
${PROTO_DIR}/substrait/algebra.proto
33+
${PROTO_DIR}/substrait/type.proto
34+
${PROTO_DIR}/substrait/extensions/extensions.proto
35+
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
36+
)
37+
38+
# Collect generated files
39+
file(GLOB PROTO_SRCS "${GENERATED_DIR}/substrait/*.pb.cc")
40+
file(GLOB PROTO_HDRS "${GENERATED_DIR}/substrait/*.pb.h")
41+
file(GLOB PROTO_EXT_SRCS "${GENERATED_DIR}/substrait/extensions/*.pb.cc")
42+
file(GLOB PROTO_EXT_HDRS "${GENERATED_DIR}/substrait/extensions/*.pb.h")
43+
44+
# Combine all proto sources
45+
set(ALL_PROTO_SRCS ${PROTO_SRCS} ${PROTO_EXT_SRCS})
46+
47+
# Include directories
48+
include_directories(${JNI_INCLUDE_DIRS})
49+
include_directories(${Protobuf_INCLUDE_DIRS})
50+
include_directories(${GENERATED_DIR})
51+
include_directories(src)
52+
53+
# Add subdirectories
54+
add_subdirectory(src)

cpp/README.md

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# OpenSearch SQL Native Engine POC
2+
3+
This is a proof-of-concept (POC) for a Java+C++ hybrid project, demonstrating how to integrate C++ native code into the OpenSearch SQL project.
4+
5+
## Project Structure
6+
7+
```
8+
cpp/
9+
├── CMakeLists.txt # Main CMake configuration file
10+
├── src/
11+
│ ├── CMakeLists.txt # Source CMake configuration
12+
│ ├── core/
13+
│ │ ├── Calculator.h # C++ core calculation class header
14+
│ │ └── Calculator.cpp # C++ core calculation class implementation
15+
│ └── jni/
16+
│ └── NativeEngine.cpp # JNI wrapper implementation
17+
└── build/ # CMake build directory (auto-generated)
18+
19+
native/
20+
└── src/main/java/org/opensearch/sql/native/
21+
├── NativeEngine.java # Java JNI wrapper
22+
└── NativeEngineDemo.java # Demo program
23+
```
24+
25+
## Features
26+
27+
### 1. Java Calling C++
28+
- `nativeAdd(int a, int b)` - Addition operation implemented in C++
29+
- `nativeMultiply(int a, int b)` - Multiplication operation implemented in C++
30+
31+
### 2. C++ Calling Java
32+
- `nativeCallJava()` - C++ calls Java callback method
33+
- `onNativeCallback(String message)` - Java callback method
34+
35+
## Build Requirements
36+
37+
- Java 11+
38+
- CMake 3.16+
39+
- C++17 compatible compiler (GCC/Clang/MSVC)
40+
- Make (Linux/macOS) or Visual Studio (Windows)
41+
42+
## Quick Start
43+
44+
### 1. Build Project
45+
```bash
46+
# Execute in project root directory
47+
./gradlew :native:build
48+
```
49+
50+
### 2. Run Demo
51+
```bash
52+
./gradlew :native:run
53+
```
54+
55+
### 3. Run Tests
56+
```bash
57+
./gradlew :native:test
58+
```
59+
60+
### 4. One-click Build and Run
61+
```bash
62+
./build-and-run-demo.sh
63+
```
64+
65+
## Build Process
66+
67+
1. **C++ Compilation**: Gradle calls CMake to compile C++ code and generate dynamic library
68+
2. **Library Copy**: Copy generated dynamic library to Java project's libs directory
69+
3. **Java Compilation**: Compile Java code including JNI interfaces
70+
4. **Runtime Loading**: Java runtime loads C++ library via `System.loadLibrary()`
71+
72+
## Extension Guide
73+
74+
### Adding New C++ Features
75+
1. Add new C++ classes in `cpp/src/core/`
76+
2. Add JNI wrapper functions in `cpp/src/jni/NativeEngine.cpp`
77+
3. Declare corresponding native methods in `native/src/main/java/.../NativeEngine.java`
78+
79+
### JNI Method Naming Convention
80+
```cpp
81+
JNIEXPORT <return_type> JNICALL
82+
Java_<package_name_with_underscores>_<class_name>_<method_name>(JNIEnv *env, ...)
83+
```
84+
85+
## Notes
86+
87+
- Ensure JNI method signatures match Java declarations exactly
88+
- Pay attention to memory management to avoid memory leaks
89+
- Exception handling needs to be considered on both C++ and Java sides
90+
- Cross-platform compatibility needs to be configured in CMakeLists.txt
91+
92+
## Troubleshooting
93+
94+
### Common Issues
95+
96+
1. **UnsatisfiedLinkError**:
97+
- Check if dynamic library is correctly generated and copied
98+
- Confirm `java.library.path` is set correctly
99+
100+
2. **CMake cannot find JNI**:
101+
- Ensure JAVA_HOME environment variable is set correctly
102+
- Check if JDK installation is complete
103+
104+
3. **Compilation errors**:
105+
- Confirm C++ compiler supports C++17
106+
- Check if CMake version meets requirements
107+
108+
## Performance Considerations
109+
110+
- JNI calls have overhead, suitable for compute-intensive tasks
111+
- Avoid frequent small data JNI calls
112+
- Consider batch processing to reduce JNI boundary crossings

cpp/proto/substrait

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../native/src/main/resources/proto/substrait

cpp/src/CMakeLists.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Create JNI library
2+
add_library(opensearch-sql-native SHARED
3+
jni/NativeEngine.cpp
4+
core/Calculator.cpp
5+
velox/SubstraitPlanProcessor.cpp
6+
${ALL_PROTO_SRCS}
7+
)
8+
9+
# Link JNI libraries and protobuf
10+
target_link_libraries(opensearch-sql-native ${JNI_LIBRARIES} ${Protobuf_LIBRARIES})
11+
12+
# Set library output name
13+
set_target_properties(opensearch-sql-native PROPERTIES
14+
OUTPUT_NAME "opensearch-sql-native"
15+
PREFIX ""
16+
)

cpp/src/core/Calculator.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#include "Calculator.h"
2+
#include <jni.h>
3+
#include <iostream>
4+
5+
int Calculator::add(int a, int b) {
6+
return a + b;
7+
}
8+
9+
int Calculator::multiply(int a, int b) {
10+
return a * b;
11+
}
12+
13+
void Calculator::callJavaMethod(void* env, void* obj) {
14+
JNIEnv* jenv = static_cast<JNIEnv*>(env);
15+
jobject jobj = static_cast<jobject>(obj);
16+
17+
jclass clazz = jenv->GetObjectClass(jobj);
18+
19+
jmethodID methodId = jenv->GetMethodID(clazz, "onNativeCallback", "(Ljava/lang/String;)V");
20+
21+
if (methodId != nullptr) {
22+
jstring message = jenv->NewStringUTF("Hello from C++!");
23+
24+
jenv->CallVoidMethod(jobj, methodId, message);
25+
26+
jenv->DeleteLocalRef(message);
27+
}
28+
29+
jenv->DeleteLocalRef(clazz);
30+
}

cpp/src/core/Calculator.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#pragma once
2+
3+
class Calculator {
4+
public:
5+
static int add(int a, int b);
6+
static int multiply(int a, int b);
7+
static void callJavaMethod(void* env, void* obj);
8+
};

0 commit comments

Comments
 (0)