blob: c07dcc4c6f4470536b123c0953f3f77ec73ce991 [file] [view]
# Calcite
> Calcite SQL transform plugin
## Description
SQL transform plugin powered by [Apache Calcite](https://calcite.apache.org/). Use standard SQL to transform data rows. The SQL plan is compiled once at job startup and applied to each row at runtime.
:::tip
- Each row is processed independently -- `JOIN` and cross-row aggregation (`GROUP BY`, `SUM`, `COUNT`) are **not** supported.
- Vector types (FLOAT_VECTOR, BINARY_VECTOR, etc.) are mapped to VARBINARY internally. Use the built-in vector UDFs (e.g., `COSINE_DISTANCE`, `VECTOR_REDUCE`) for vector operations.
:::
## Options
| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| sql | string | yes | - | SQL statement to execute |
| table_transform | list | no | [] | Per-table SQL overrides for multi-table CDC scenarios |
| table_match_regex | string | no | .* | Regex to match table paths. Unmatched tables pass through unchanged |
| row_error_handle_way | enum | no | FAIL | How to handle row-level errors: `FAIL`, `SKIP`, or `ROUTE_TO_TABLE` |
### sql [string]
```hocon
sql = "SELECT id, UPPER(name) AS name, age + 1 AS next_age FROM source_table WHERE age > 18"
```
### table_transform [list]
Per-table SQL overrides for multi-table CDC scenarios. Each entry specifies a `table_path` and a `sql` statement. Tables not listed fall back to the global `sql` (if their path matches `table_match_regex`) or pass through unchanged.
```hocon
table_transform = [
{
table_path = "db.users"
sql = "SELECT id, name, UPPER(email) AS email FROM users"
},
{
table_path = "db.orders"
sql = "SELECT order_id, amount * 100 AS amount_cents FROM orders"
}
]
```
### table_match_regex [string]
A regex pattern to filter which tables should be transformed. Only tables whose path matches this regex will have the global `sql` applied. Tables that do not match pass through unchanged. Default is `.*` (match all).
### row_error_handle_way [enum]
How to handle errors during SQL execution for a row:
- `FAIL` (default) -- fail the job immediately
- `SKIP` -- skip the problematic row and continue
- `ROUTE_TO_TABLE` -- route the error row to a separate error table
### common options [string]
Transform plugin common parameters, please refer to [Transform Plugin](common-options/common-options.md) for details.
## Built-in UDFs
All built-in UDFs return `NULL` when any required argument is `NULL`.
Function identifiers are case-insensitive. For example, `MASK(...)`, `mask(...)`, and `Mask(...)` are equivalent.
### Data Masking Functions
#### MASK
```MASK(value, start, end, maskChar) -> STRING```
Replaces characters in range `[start, end)` with `maskChar`. Returns the original string if the range is invalid. Default mask char is `*` when null or empty.
Example:
```sql
SELECT MASK(phone, 3, 7, '*') AS masked_phone FROM t
```
#### MASK_HASH
```MASK_HASH(value) -> STRING```
Returns the SHA-256 hex hash (64 characters) of the input. Deterministic -- same input always produces the same hash.
Example:
```sql
SELECT MASK_HASH(phone) AS phone_hash FROM t
```
#### DES_ENCRYPT
```DES_ENCRYPT(password, data) -> STRING```
Encrypts `data` with DES (CBC/PKCS5Padding) using `password` (must be >= 8 chars). Returns Base64-encoded ciphertext.
Example:
```sql
SELECT DES_ENCRYPT('12345678', secret) AS encrypted FROM t
```
#### DES_DECRYPT
```DES_DECRYPT(password, data) -> STRING```
Decrypts Base64-encoded `data` with the same password used for encryption.
Example:
```sql
SELECT DES_DECRYPT('12345678', encrypted_secret) AS original FROM t
```
### Vector Functions
#### COSINE_DISTANCE
```COSINE_DISTANCE(vector1, vector2) -> DOUBLE```
Returns a DOUBLE value between 0 and 1: 0 means identical vectors (completely similar), 1 means orthogonal vectors (completely dissimilar).
Example:
```sql
SELECT COSINE_DISTANCE(vec1, vec2) AS distance FROM t
```
#### L1_DISTANCE
```L1_DISTANCE(vector1, vector2) -> DOUBLE```
Calculates the Manhattan (L1) distance between two vectors.
Example:
```sql
SELECT L1_DISTANCE(vec1, vec2) AS dist FROM t
```
#### L2_DISTANCE
```L2_DISTANCE(vector1, vector2) -> DOUBLE```
Calculates the Euclidean (L2) distance between two vectors.
Example:
```sql
SELECT L2_DISTANCE(vec1, vec2) AS dist FROM t
```
#### VECTOR_DIMS
```VECTOR_DIMS(vector) -> INT```
Returns an INT value representing the number of dimensions (elements) in the vector.
Example:
```sql
SELECT VECTOR_DIMS(embedding) AS dims FROM t
```
#### VECTOR_NORM
```VECTOR_NORM(vector) -> DOUBLE```
Calculates the L2 norm (Euclidean norm) of a vector, which represents the length or magnitude of the vector.
Example:
```sql
SELECT VECTOR_NORM(embedding) AS norm FROM t
```
#### INNER_PRODUCT
```INNER_PRODUCT(vector1, vector2) -> DOUBLE```
Calculates the inner product (dot product) of two vectors, which is used to measure the similarity and projection between the vectors.
Example:
```sql
SELECT INNER_PRODUCT(vec1, vec2) AS ip FROM t
```
#### VECTOR_REDUCE
```VECTOR_REDUCE(vector_field, target_dimension, method)```
Generic vector dimension reduction function that supports multiple reduction methods.
**Parameters:**
- `vector_field`: The vector field to reduce (VECTOR type)
- `target_dimension`: The target dimension (INTEGER, must be smaller than source dimension)
- `method`: The reduction method (STRING):
- **'TRUNCATE'**: Truncates the vector by keeping only the first N elements. Simplest and fastest, but may lose information in truncated dimensions.
- **'RANDOM_PROJECTION'**: Uses Gaussian random projection. Preserves relative distances between vectors following the Johnson-Lindenstrauss lemma.
- **'SPARSE_RANDOM_PROJECTION'**: Uses sparse random projection where matrix elements are mostly zero. More computationally efficient than regular random projection.
**Returns:** VARBINARY -- the reduced vector
**Example:**
```sql
SELECT id, VECTOR_REDUCE(embedding, 256, 'TRUNCATE') AS reduced FROM t
SELECT id, VECTOR_REDUCE(embedding, 128, 'RANDOM_PROJECTION') AS reduced FROM t
SELECT id, VECTOR_REDUCE(embedding, 64, 'SPARSE_RANDOM_PROJECTION') AS reduced FROM t
```
#### VECTOR_NORMALIZE
```VECTOR_NORMALIZE(vector_field)```
Normalizes a vector to unit length (magnitude = 1). Useful for computing cosine similarity.
**Parameters:**
- `vector_field`: The vector field to normalize (VECTOR type)
**Returns:** VARBINARY -- the normalized vector
**Example:**
```sql
SELECT id, VECTOR_NORMALIZE(embedding) AS unit_vec FROM t
```
In addition to the UDFs listed above, all standard SQL functions provided by Apache Calcite are available (string, math, date/time, JSON, conditional, etc.). For the full function reference, see the [Apache Calcite SQL Reference](https://calcite.apache.org/docs/reference.html).
## Examples
### Basic SELECT + WHERE
The data read from source is a table like this:
| id | name | age |
|----|------|-----|
| 1 | Joy Ding | 20 |
| 2 | May Ding | 21 |
| 3 | Kin Dom | 24 |
| 4 | Joy Dom | 15 |
```hocon
transform {
Calcite {
plugin_input = "fake"
plugin_output = "result"
sql = "SELECT id, name, age FROM fake WHERE age >= 18"
}
}
```
Then the data in result table `result` will be:
| id | name | age |
|----|------|-----|
| 1 | Joy Ding | 20 |
| 2 | May Ding | 21 |
| 3 | Kin Dom | 24 |
Row with `age = 15` is filtered out.
### String and Math Functions
Input:
| id | name | salary |
|----|------|--------|
| 1 | Joy Ding | 5000 |
| 2 | May Ding | 8000 |
```hocon
transform {
Calcite {
plugin_input = "fake"
plugin_output = "result"
sql = "SELECT id, UPPER(name) AS name_upper, CHAR_LENGTH(name) AS name_len, salary * 1.1 AS new_salary FROM fake"
}
}
```
Output:
| id | name_upper | name_len | new_salary |
|----|------------|----------|------------|
| 1 | JOY DING | 8 | 5500.0 |
| 2 | MAY DING | 8 | 8800.0 |
### CASE WHEN Conditional
Input:
| id | name | age |
|----|------|-----|
| 1 | Alice | 8 |
| 2 | Bob | 15 |
| 3 | Carol | 30 |
| 4 | Dave | 70 |
```hocon
transform {
Calcite {
plugin_input = "fake"
plugin_output = "result"
sql = "SELECT id, name, CASE WHEN age < 13 THEN 'child' WHEN age < 18 THEN 'teen' WHEN age < 65 THEN 'adult' ELSE 'senior' END AS age_group FROM fake"
}
}
```
Output:
| id | name | age_group |
|----|------|-----------|
| 1 | Alice | child |
| 2 | Bob | teen |
| 3 | Carol | adult |
| 4 | Dave | senior |
### JSON Extraction
Input:
| id | payload |
|----|---------|
| 1 | {"user": {"name": "Joy Ding", "email": "joy@example.com"}} |
| 2 | {"user": {"name": "May Ding", "email": "may@example.com"}} |
```hocon
transform {
Calcite {
plugin_input = "fake"
plugin_output = "result"
sql = "SELECT id, JSON_VALUE(payload, '$.user.name') AS user_name, JSON_VALUE(payload, '$.user.email') AS email FROM fake"
}
}
```
Output:
| id | user_name | email |
|----|-----------|-------|
| 1 | Joy Ding | joy@example.com |
| 2 | May Ding | may@example.com |
### Data Masking (MASK + MASK_HASH + DES)
Input:
| id | phone | secret |
|----|-------|--------|
| 1 | 13812345678 | seatunnel-password |
| 2 | 13987654321 | connector-api-key |
```hocon
transform {
Calcite {
plugin_input = "fake"
plugin_output = "result"
sql = "SELECT id, MASK(phone, 3, 7, '*') AS masked_phone, MASK_HASH(phone) AS phone_hash, DES_ENCRYPT('12345678', secret) AS encrypted_secret FROM fake"
}
}
```
Output:
| id | masked_phone | phone_hash | encrypted_secret |
|----|--------------|------------|------------------|
| 1 | 138\*\*\*\*5678 | a1b2c3...(64-char SHA-256 hex) | Base64-encoded ciphertext |
| 2 | 139\*\*\*\*4321 | d4e5f6...(64-char SHA-256 hex) | Base64-encoded ciphertext |
To decrypt later in the pipeline:
```hocon
transform {
Calcite {
plugin_input = "result"
plugin_output = "decrypted"
sql = "SELECT id, DES_DECRYPT('12345678', encrypted_secret) AS original_secret FROM result"
}
}
```
### Vector Operations
Use built-in vector UDFs to compute distances, reduce dimensions, or normalize vectors in a data pipeline (e.g., between Milvus/Qdrant source and sink).
```hocon
transform {
Calcite {
plugin_input = "vector_source"
plugin_output = "result"
sql = "SELECT id, COSINE_DISTANCE(query_vec, doc_vec) AS distance, VECTOR_DIMS(doc_vec) AS dims, VECTOR_REDUCE(doc_vec, 128, 'TRUNCATE') AS reduced_vec FROM vector_source"
}
}
```
Given two FLOAT_VECTOR columns `query_vec` and `doc_vec`, this computes the cosine distance, extracts dimensions, and reduces `doc_vec` from its original dimension to 128.
### Multi-table CDC (table_transform)
```hocon
transform {
Calcite {
plugin_input = "cdc_source"
plugin_output = "result"
table_transform = [
{
table_path = "db.users"
sql = "SELECT id, name, UPPER(email) AS email FROM users"
},
{
table_path = "db.orders"
sql = "SELECT order_id, amount * 100 AS amount_cents FROM orders"
}
]
}
}
```
Tables not listed in `table_transform` but matching `table_match_regex` (default `.*`) will have the global `sql` applied. Tables not matching any rule pass through unchanged.
### Error Handling (row_error_handle_way)
```hocon
transform {
Calcite {
plugin_input = "source_table"
plugin_output = "result"
sql = "SELECT id, CAST(age AS VARCHAR) AS age_str FROM source_table"
row_error_handle_way = "SKIP"
}
}
```
When a row causes a SQL execution error:
- `FAIL` -- the job fails immediately (default, recommended for data quality)
- `SKIP` -- the problematic row is silently dropped
- `ROUTE_TO_TABLE` -- the row is sent to a separate error table for later inspection
## Custom UDF
Custom scalar functions can be added via the `CalciteUdf` SPI. For the full development guide, API reference, examples, and type mapping, see [Calcite UDF](calcite-udf.md).
## Limitations
| Limitation | Detail |
|------------|--------|
| Single input table | Only one table is registered in the Calcite schema per transform. Multi-table `JOIN` is not supported |
| Row-at-a-time processing | Each row is processed independently. `GROUP BY` / `SUM()` / `COUNT()` operate on a single row and are generally not useful for batch aggregation |
| WHERE filtering | `WHERE` conditions that evaluate to `false` cause the row to be dropped (not passed through) |
| Table name matching | The `FROM` table name in SQL must exactly match the `plugin_input` value |
| Scalar UDFs only | Only scalar functions are supported. Table-valued functions and aggregate UDFs are not available |
| Vector type mapping | Vector types are mapped to VARBINARY internally. Use built-in vector UDFs (COSINE_DISTANCE, L1_DISTANCE, etc.) for vector operations |
:::tip CDC schema changes
When an `AlterTableEvent` is received (for example, add/drop columns), the engine automatically rebuilds the SQL plan and re-infers the output schema. No manual intervention is needed.
:::
## Job Config Example
```hocon
env {
job.mode = "BATCH"
}
source {
FakeSource {
plugin_output = "fake"
row.num = 100
schema = {
fields {
id = "int"
name = "string"
age = "int"
phone = "string"
}
}
}
}
transform {
Calcite {
plugin_input = "fake"
plugin_output = "result"
sql = "SELECT id, UPPER(name) AS name, age + 1 AS age, MASK(phone, 3, 7, '*') AS phone FROM fake WHERE age >= 0"
}
}
sink {
Console {
plugin_input = "result"
}
}
```
## Changelog
### next-release
- Add Calcite Transform plugin