Skip to content

Commit e46c9dd

Browse files
[Feature] Add StarRocks Driver Support (#8454)
* only olap connector(not incloud model) * fix : message "not available for dialect 'starrocks'" & starrocks icon * feat(starrocks): implement histogram and timeseries support - Add StarRocks support for ColumnRugHistogram with generate_series() - Add StarRocks support for ColumnNumericHistogram (FD & Diagnostic methods) - Add CTE-based ColumnTimeseries for StarRocks (no TEMP TABLE required) - Add helper functions: rangeNumbersEnd(), isNonNullFinite() for StarRocks - Handle reserved keywords (values, range) with backtick escaping * feat(starrocks): add support for resolving timestamps in StarRocks dialect * feat(starrocks): enhance StarRocks connector with improved context handling, connection timeouts, and error messages * feat(starrocks): add StarRocks form and error handling to AddDataForm * Update docs * fix: update code based on PR review feedback - addressed multiple review comments - cleaned up import and minor issues * fix: apply fixes from PR comments * fix: resolve build errors - Remove unused 'strings' import from starrocks.go - Fix undefined variable 't' in olap.go StarRocks case - Update date_trunc comment to reference validation location * fix: update reserved keyword handling and SQL name sanitization for StarRocks dialect * fix: improve dry run handling in Query method and simplify SQL min query * fix: correct safeName call in Druid maxSQL query * fix: use equality check instead of Before for StarRocks time range bins * fix: resolve build errors and metrics context propagation in Data Explorer - Fix build compilation errors - Ensure selected catalog (Database), schema (DatabaseSchema), and table are properly propagated when creating metrics from Data Explorer * change test code and fix on AddStarRocksFrom * Apply minor fixes from code review comments * Refactor: removed unused code as suggested by Git CI * fix: Add only for the StarRocks compatibility by executor part * fix change c.getDB to c.db * fix: simplify database connection retrieval in StarRocks driver * fix: update column handling for StarRocks compatibility in numeric and rug histograms + build check * feat: enhance error handling and form structure in AddStarRocksForm component * fix the failing checks. * fix the failing checks 2 * fix : npx prettier check * [Fix] avoid creating a separate AddStarRocksForm * [Enhancement] Limit StarRocks Frontend until integration is supported + Fix external catalog information schema check * [Enhancement] Restrict changes to Backend components only (StarRocks) * fix: format OLAP_DRIVERS_WITHOUT_MODELING array for consistency * [Enhancement] Handle StarRocks icon in a more general way * fix: Prettier checks
1 parent d5259a4 commit e46c9dd

26 files changed

+1885
-71
lines changed

cli/cmd/runtime/start.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import (
5050
_ "github.com/rilldata/rill/runtime/drivers/slack"
5151
_ "github.com/rilldata/rill/runtime/drivers/snowflake"
5252
_ "github.com/rilldata/rill/runtime/drivers/sqlite"
53+
_ "github.com/rilldata/rill/runtime/drivers/starrocks"
5354
_ "github.com/rilldata/rill/runtime/reconcilers"
5455
_ "github.com/rilldata/rill/runtime/resolvers"
5556
)
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
---
2+
title: StarRocks
3+
description: Power Rill dashboards using StarRocks
4+
sidebar_label: StarRocks
5+
sidebar_position: 5
6+
---
7+
8+
[StarRocks](https://www.starrocks.io/) is an open-source, high-performance analytical database designed for real-time, multi-dimensional analytics on large-scale data. It supports both primary key and aggregate data models, making it suitable for a variety of analytical workloads including real-time dashboards, ad-hoc queries, and complex analytical tasks.
9+
10+
Rill supports connecting to an existing StarRocks cluster via a "live connector" and using it as an OLAP engine built against [external tables](/build/connectors/olap#external-olap-tables) to power Rill dashboards.
11+
12+
:::
13+
14+
## Connect to StarRocks
15+
16+
When using StarRocks for local development, you can connect via connection parameters or by using the DSN.
17+
18+
After selecting "Add Data", select StarRocks and fill in your connection parameters. This will automatically create the `starrocks.yaml` file in your `connectors` directory and populate the `.env` file with `connector.starrocks.password`.
19+
20+
### Connection Parameters
21+
22+
```yaml
23+
type: connector
24+
driver: starrocks
25+
26+
host: <HOSTNAME>
27+
port: 9030
28+
username: <USERNAME>
29+
password: "{{ .env.connector.starrocks.password }}"
30+
catalog: default_catalog
31+
database: <DATABASE>
32+
ssl: false
33+
```
34+
35+
### Connection String (DSN)
36+
37+
Rill can also connect to StarRocks using a DSN connection string. StarRocks uses MySQL protocol, so the connection string follows the MySQL DSN format:
38+
39+
```yaml
40+
type: connector
41+
driver: starrocks
42+
43+
dsn: "{{ .env.connector.starrocks.dsn }}"
44+
```
45+
46+
The DSN format is:
47+
```
48+
starrocks://user:password@host:port/database
49+
```
50+
51+
Or using MySQL-style format:
52+
```
53+
user:password@tcp(host:port)/database?parseTime=true
54+
```
55+
56+
## Configuration Properties
57+
58+
| Property | Description | Default |
59+
|----------|-------------|---------|
60+
| `host` | StarRocks FE (Frontend) server hostname | Required |
61+
| `port` | MySQL protocol port of StarRocks FE | `9030` |
62+
| `username` | Username for authentication | Required |
63+
| `password` | Password for authentication | - |
64+
| `catalog` | StarRocks catalog name (for external catalogs like Iceberg, Hive) | `default_catalog` |
65+
| `database` | StarRocks database name | - |
66+
| `ssl` | Enable SSL/TLS encryption | `false` |
67+
| `dsn` | Full connection string (alternative to individual parameters) | - |
68+
69+
## External Catalogs
70+
71+
StarRocks supports external catalogs for querying data in Hive, Iceberg, Delta Lake, and other external data sources. To use an external catalog:
72+
73+
1. Set the `catalog` property to your external catalog name (e.g., `iceberg_catalog`)
74+
2. Set the `database` property to the database within that catalog
75+
76+
```yaml
77+
type: connector
78+
driver: starrocks
79+
80+
host: starrocks-fe.example.com
81+
port: 9030
82+
username: analyst
83+
password: "{{ .env.connector.starrocks.password }}"
84+
catalog: iceberg_catalog
85+
database: my_database
86+
```
87+
88+
## Naming Mapping
89+
90+
StarRocks uses a three-level hierarchy: Catalog > Database > Table. In Rill's API:
91+
92+
| Rill Parameter | StarRocks Concept | Example |
93+
|----------------|-------------------|---------|
94+
| `database` | Catalog | `default_catalog`, `iceberg_catalog` |
95+
| `databaseSchema` | Database | `my_database` |
96+
| `table` | Table | `my_table` |
97+
98+
## Troubleshooting
99+
100+
### Connection Issues
101+
102+
If you encounter connection issues:
103+
104+
1. Verify the FE node hostname and port (default: 9030)
105+
2. Check that your user has appropriate permissions
106+
3. Ensure network connectivity to the StarRocks FE node
107+
4. For SSL connections, verify SSL is enabled on the StarRocks server
108+
109+
110+
## Known Limitations
111+
112+
- **Model execution**: Model creation and execution is not yet supported. This feature is under development.
113+
114+
:::info Need help connecting to StarRocks?
115+
116+
If you would like to connect Rill to an existing StarRocks instance, please don't hesitate to [contact us](/contact). We'd love to help!
117+
118+
:::

runtime/drivers/olap.go

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ const (
201201
DialectDruid
202202
DialectClickHouse
203203
DialectPinot
204+
DialectStarRocks
204205

205206
// Below dialects are not fully supported dialects.
206207
DialectBigQuery
@@ -223,6 +224,8 @@ func (d Dialect) String() string {
223224
return "clickhouse"
224225
case DialectPinot:
225226
return "pinot"
227+
case DialectStarRocks:
228+
return "starrocks"
226229
case DialectBigQuery:
227230
return "bigquery"
228231
case DialectSnowflake:
@@ -251,8 +254,8 @@ func (d Dialect) EscapeIdentifier(ident string) string {
251254
}
252255

253256
switch d {
254-
case DialectMySQL, DialectBigQuery:
255-
// MySQL uses backticks for quoting identifiers
257+
case DialectMySQL, DialectBigQuery, DialectStarRocks:
258+
// MySQL and StarRocks use backticks for quoting identifiers
256259
// Replace any backticks inside the identifier with double backticks.
257260
return fmt.Sprintf("`%s`", strings.ReplaceAll(ident, "`", "``"))
258261

@@ -297,7 +300,8 @@ func (d Dialect) ConvertToDateTruncSpecifier(grain runtimev1.TimeGrain) string {
297300
}
298301

299302
func (d Dialect) SupportsILike() bool {
300-
return d != DialectDruid && d != DialectPinot
303+
// StarRocks uses MySQL syntax which doesn't support ILIKE
304+
return d != DialectDruid && d != DialectPinot && d != DialectStarRocks
301305
}
302306

303307
// RequiresCastForLike returns true if the dialect requires an expression used in a LIKE or ILIKE condition to explicitly be cast to type TEXT.
@@ -473,6 +477,10 @@ func (d Dialect) JoinOnExpression(lhs, rhs string) string {
473477
if d == DialectClickHouse {
474478
return fmt.Sprintf("isNotDistinctFrom(%s, %s)", lhs, rhs)
475479
}
480+
// StarRocks uses MySQL's NULL-safe equal operator
481+
if d == DialectStarRocks {
482+
return fmt.Sprintf("%s <=> %s", lhs, rhs)
483+
}
476484
return fmt.Sprintf("%s IS NOT DISTINCT FROM %s", lhs, rhs)
477485
}
478486

@@ -584,6 +592,10 @@ func (d Dialect) DateTruncExpr(dim *runtimev1.MetricsViewSpec_Dimension, grain r
584592
return fmt.Sprintf("CAST(date_trunc('%s', %s, 'MILLISECONDS') AS TIMESTAMP)", specifier, expr), nil
585593
}
586594
return fmt.Sprintf("CAST(date_trunc('%s', %s, 'MILLISECONDS', '%s') AS TIMESTAMP)", specifier, expr, tz), nil
595+
case DialectStarRocks:
596+
// StarRocks supports date_trunc similar to DuckDB but does not support timezone parameter
597+
// NOTE: Timezone and time shift parameters are validated in runtime/metricsview/executor/executor_validate.go
598+
return fmt.Sprintf("date_trunc('%s', %s)", specifier, expr), nil
587599
default:
588600
return "", fmt.Errorf("unsupported dialect %q", d)
589601
}
@@ -596,7 +608,7 @@ func (d Dialect) DateDiff(grain runtimev1.TimeGrain, t1, t2 time.Time) (string,
596608
return fmt.Sprintf("DATEDIFF('%s', parseDateTimeBestEffort('%s'), parseDateTimeBestEffort('%s'))", unit, t1.Format(time.RFC3339), t2.Format(time.RFC3339)), nil
597609
case DialectDruid:
598610
return fmt.Sprintf("TIMESTAMPDIFF(%q, TIME_PARSE('%s'), TIME_PARSE('%s'))", unit, t1.Format(time.RFC3339), t2.Format(time.RFC3339)), nil
599-
case DialectDuckDB:
611+
case DialectDuckDB, DialectStarRocks:
600612
return fmt.Sprintf("DATEDIFF('%s', TIMESTAMP '%s', TIMESTAMP '%s')", unit, t1.Format(time.RFC3339), t2.Format(time.RFC3339)), nil
601613
case DialectPinot:
602614
return fmt.Sprintf("DATEDIFF('%s', %d, %d)", unit, t1.UnixMilli(), t2.UnixMilli()), nil
@@ -607,7 +619,7 @@ func (d Dialect) DateDiff(grain runtimev1.TimeGrain, t1, t2 time.Time) (string,
607619

608620
func (d Dialect) IntervalSubtract(tsExpr, unitExpr string, grain runtimev1.TimeGrain) (string, error) {
609621
switch d {
610-
case DialectClickHouse, DialectDruid, DialectDuckDB:
622+
case DialectClickHouse, DialectDruid, DialectDuckDB, DialectStarRocks:
611623
return fmt.Sprintf("(%s - INTERVAL (%s) %s)", tsExpr, unitExpr, d.ConvertToDateTruncSpecifier(grain)), nil
612624
case DialectPinot:
613625
return fmt.Sprintf("CAST((dateAdd('%s', -1 * %s, %s)) AS TIMESTAMP)", d.ConvertToDateTruncSpecifier(grain), unitExpr, tsExpr), nil
@@ -655,6 +667,18 @@ func (d Dialect) SelectTimeRangeBins(start, end time.Time, grain runtimev1.TimeG
655667
}
656668
sb.WriteString(fmt.Sprintf(") t (%s)", d.EscapeIdentifier(alias)))
657669
return sb.String(), args, nil
670+
case DialectStarRocks:
671+
// StarRocks uses UNION ALL for generating time series
672+
var sb strings.Builder
673+
first := true
674+
for t := start; t != end; t = timeutil.OffsetTime(t, g, 1, tz) {
675+
if !first {
676+
sb.WriteString(" UNION ALL ")
677+
}
678+
sb.WriteString(fmt.Sprintf("SELECT CAST('%s' AS DATETIME) AS %s", t.Format(time.DateTime), d.EscapeIdentifier(alias)))
679+
first = false
680+
}
681+
return sb.String(), nil, nil
658682
default:
659683
return "", nil, fmt.Errorf("unsupported dialect %q", d)
660684
}
@@ -852,6 +876,8 @@ func (d Dialect) GetTimeExpr(t time.Time) (bool, string) {
852876
return true, fmt.Sprintf("CAST('%s' AS TIMESTAMP)", t.Format(time.RFC3339Nano))
853877
case DialectPinot:
854878
return true, fmt.Sprintf("CAST(%d AS TIMESTAMP)", t.UnixMilli())
879+
case DialectStarRocks:
880+
return true, fmt.Sprintf("CAST('%s' AS DATETIME)", t.Format(time.DateTime))
855881
default:
856882
return false, ""
857883
}

0 commit comments

Comments
 (0)