← All Adapters

STARROCKS

StarRocks is a high-performance MPP OLAP database built for real-time analytics — and it speaks the MySQL wire protocol, so connecting is as plain as MySQL ever was: host, port, username, password. In DVT it's a default target candidate: name it in profiles.yml, make it your target, and your whole project — models, tests, materialized views — runs natively on StarRocks through its adapter.

A COMPLETE, WORKING PROFILE

StarRocks has exactly one authentication shape — username and password over the MySQL protocol — so this profile is the whole story. The one thing to get right: connect to a frontend (FE) node's query port, which is 9030 by default, never to a backend (BE) node.

# ~/.dbt/profiles.yml
my_project:
  target: sr
  outputs:
    sr:
      type: starrocks
      host: starrocks-fe.internal.com
      port: 9030                # the FE MySQL-protocol query port
      username: analytics       # note: username, not user
      password: "{{ env_var('STARROCKS_PASSWORD') }}"
      schema: analytics         # the StarRocks database models build in
      threads: 4

Run dvt sync once and the adapter and its MySQL driver install themselves from the profile — nothing to pip-install by hand. Two spellings that bite people coming from other engines: the field is username (not user), and schema is what StarRocks calls a database.

AUTHENTICATION — ONE SHAPE, TWO KNOBS

There are no key pairs, tokens, or OAuth flows here — MySQL-protocol username/password is the method. Two optional knobs adjust how that handshake happens:

auth_plugin — for servers that insist on a specific MySQL authentication plugin (for example mysql_native_password):

    sr:
      type: starrocks
      host: starrocks-fe.internal.com
      port: 9030
      username: analytics
      password: "{{ env_var('STARROCKS_PASSWORD') }}"
      schema: analytics
      auth_plugin: mysql_native_password

use_pure: "true"— forces the driver's pure-Python connection mode. In practice the pure-Python path is what runs by default in DVT's environment (no compiled client library to build or segfault); set the flag explicitly if you want it guaranteed in writing.

ENGINE BEHAVIOR WORTH KNOWING

database must equal schema — or be omitted. StarRocks treats database and schema as the same thing, and the adapter enforces it: a profile where database differs from schema is refused with On StarRocks, database must be omitted or have the same value as schema. Simplest rule: set schema, leave database out.

The schema is created for you. If the database named in schemadoesn't exist yet, the adapter creates it on first connect — a fresh cluster and an empty profile get you to a green dvt run without a manual CREATE DATABASE.

Catalogs. Connections land in default_catalog— StarRocks' internal catalog, where your models build. The catalog field re-aims the session if you work in another catalog; external catalogs (Hive, Iceberg, Hudi and friends) remain queryable from your model SQL the StarRocks way.

Table key models are model configs. StarRocks tables have a key type — the adapter accepts duplicate, primary and unique — and it exposes the full DDL surface per model: table_type, keys, partition_by, distributed_by, buckets, properties. A primary-key table straight from a model config:

{{ config(
    materialized='table',
    table_type='primary',
    keys=['event_id'],
    distributed_by=['event_id']
) }}
select event_id, user_id, event_time, payload
from {{ ref('stg_events') }}

Long ETL can run async. Set is_async: true and eligible statements (CREATE TABLE AS SELECT, INSERT INTO / OVERWRITE) are submitted as StarRocks tasks via SUBMIT TASK, then polled with exponential backoff — the async_query_timeout, poll_interval, poll_max_delay and poll_factor fields tune the polling. Useful when a materialization outlives an ordinary session timeout.

Quoting. Backticks, like the rest of the MySQL family: `my column`, not double quotes. If your model names stick to plain lowercase identifiers you'll never notice.

Local dev in one container. StarRocks ships an all-in-one image that runs a single-node FE+BE — ideal for trying the adapter before touching a real cluster:

docker run -p 9030:9030 -p 8030:8030 -p 8040:8040 -itd \
  --name starrocks starrocks/allin1-ubuntu
# then: host: 127.0.0.1, port: 9030, username: root, no password

DATA MOVEMENT TODAY

StarRocks is a default-target engine today: the native lane — full dbt projects running on StarRocks — is the supported path. DVT's data-movement lane doesn't map StarRocks connections yet, so asking it to extract from or bulk-load into a StarRocks output (a federated read of a StarRocks source, an f_table landing on it, a Sling-loaded seed) is refused by name: Adapter type 'starrocks' not supported. The lane to use now: make StarRocks the default target and let models run natively there.

INCREMENTAL MODELS — OVERWRITE IS THE IDIOM, AND THERE IS NO MERGE

StarRocks declares its accepted strategies twice — once in the adapter and once as a Jinja whitelist — and the two agree: default, insert_overwrite, dynamic_overwrite and microbatch. dynamic_overwriteis StarRocks' own; it is not a dbt strategy and exists nowhere else. Going the other way, append, delete+insert and merge — builtins on almost every other engine — are absent from that list and are rejected. Partition overwrite is the StarRocks idiom, and it is the whole idiom.

Invalid incremental strategy provided: merge
Expected one of: 'default', 'insert_overwrite', 'dynamic_overwrite', 'microbatch'

That check runs before the first-run, view and full-refresh branches, so a bad strategy name fails even on the run that would only have created the table. Unlike engines that validate late, StarRocks will not give you a green build #1 and a surprise on build #2.

With no incremental_strategy set, the strategy is default — and on StarRocks that means append. defaultis a real member of the accepted list here rather than only a sentinel, but StarRocks ships no macro of its own for it, so it resolves through dbt-core's generic default, which is the append path: a plain INSERT INTO. It does not deduplicate, and it does not consult unique_key. If you want overwrite semantics you have to name them.

{{ config(
    materialized='incremental',
    incremental_strategy='dynamic_overwrite',  -- StarRocks 3.4.0 or newer
    partition_by=['event_date'],
    distributed_by=['event_id']
) }}

select event_id, event_date, user_id, payload
from {{ ref('stg_events') }}
{% if is_incremental() %}
where event_date >= current_date() - interval 3 day
{% endif %}

The conditional support is where this engine actually bites, and there are three layers of it. First, version gates: dynamic_overwrite requires StarRocks 3.4.0 or newer, checked against the live server version. Second, required configs: microbatch demands all three of event_time, begin and batch_size. Third, delegation: microbatch is not its own SQL at all — it hands off to insert_overwrite, or to dynamic_overwrite when microbatch_use_dynamic_overwrite: true, which means a microbatch model configured that way inherits the 3.4.0 gate too. All three non-default strategies emit the same statement shape — insert /*+SET_VAR(dynamic_overwrite = …)*/ overwrite … — and the strategy only decides whether that session variable is TRUE or FALSE.

[dynamic_overwrite] is only available from version 3.4.0 onwards, current version is 3.2.1
The 'microbatch' incremental strategy requires the 'event_time' configuration to be set.

unique_key does not do what you expect here. No StarRocks strategy consumes it: the materialization reads a separate keys config, and the SQL builders use only the target relation, the temp relation and the destination columns. incremental_predicates are read and discarded the same way. What unique_key doesdo is rewrite the table's DDL: set it on an incremental model and the table is created as a PRIMARY KEY tablekeyed on those columns. On this engine it is a table-format setting wearing a strategy's name.

Because every incremental branch routes through StarRocks' create-table macro, the table-level rules apply on incremental builds too. table_type is checked against DUPLICATE, PRIMARY and UNIQUE, and the latter two must also set keys. The engine must be OLAP for non-external relations. Two further version gates can fire on the same path: distributed_by with no buckets on a server older than 2.5.7, and no distributed_by at all on a server older than 3.1.

"AGGREGATE" is not support
"PRIMARY" is must set "keys"
"ENGINE = MYSQL" does not support, currently only supports 'OLAP'
[buckets] must set before version 2.5.7, current version is 2.5.0
[distributed_by] must set before version 3.1, current version is 3.0.4

One more conditional, and it is quiet: point an incremental model at an external catalog — a catalog other than default_catalog, with a database — and both the target and its temp relation are rebuilt into that catalog and the DDL routes to StarRocks' external-table path, which emits only PARTITION BY and PROPERTIES. table_type, keys, distributed_by, buckets and order_by are dropped there. And where the plain table materialization refuses a half-configured external target with External tables require both 'catalog' and 'database', the incremental materialization has no such guard. On the normal path, --full-refresh is safe: the new table is built as a backup relation and swapped in with alter table … swap with … rather than dropped first.

When StarRocks is the target of a federated f_incremental model — it cannot be, today. DVT's data-movement layer does not map StarRocks connections, so a federated read of a StarRocks source, an f_table or f_incremental landing on it, or a Sling-loaded seed is refused by name with Adapter type 'starrocks' not supported. Separately, StarRocks is not among the engines whose connector has been verified to honour an update-key-only load, so a keyless f_incremental — one carrying a watermark_column and no unique_key — is refused with DVT026. The admitted set today is postgres, redshift, mysql, duckdb, sqlite, sqlserver, oracle, clickhouse and trino; starrocks, snowflake, databricks, bigquery, fabric and athena are still refused. The lane that works today is the native one: make StarRocks the default target and let incremental models compile to StarRocks SQL.

Which leads to one thing DVT deliberately does notdo for you. It will not rewrite your strategy when you flip a target onto StarRocks. DVT's native-strategy substitution map holds exactly one engine — ClickHouse, where merge becomes delete+insert — and it is that small on purpose: a replacement is only written when it has been measured to be semantically equal. Flip a project whose incrementals say merge onto StarRocks and StarRocks refuses them with the compiler error at the top of this section. That is the intended outcome. Guessing at a substitution nobody has measured would be worse than the error it hid.

REFERENCE — EVERY PROFILE FIELD

FIELDTYPEREQUIREDDEFAULTNOTES
typestringyesMust be starrocks.
hoststringyesA frontend (FE) node hostname — never a BE node.
portintegerno9030The FE MySQL-protocol query port.
usernamestringyesStarRocks username. The field is username, not user.
passwordstringyesStarRocks password. Use env_var() rather than a literal.
schemastringyesThe StarRocks database models build in. Created on first connect if missing.
databasestringnoOmit it — or set it identical to schema. Anything else is refused.
catalogstringnodefault_catalogCatalog the session targets. Models build in the internal catalog.
charsetstringnoConnection character set, when the server needs one named.
versionstringnoautoServer version override. Normally auto-detected via select current_version().
use_purestringno"true" forces the pure-Python MySQL driver mode.
auth_pluginstringnoMySQL auth plugin name, e.g. mysql_native_password.
is_asyncbooleannofalseSubmit eligible ETL statements as async StarRocks tasks (SUBMIT TASK).
async_query_timeoutintegerno300Seconds an async task may run before timing out.
poll_intervalintegerno1Base delay (seconds) between task polls.
poll_max_delayintegerno600Cap on the backoff delay between polls.
poll_factorfloatno2.0Backoff growth multiplier between polls.
threadsintegerno4Parallel model threads, as in dbt.