跳转至

CLI 参考

dosi(crate crates/dosi-engine) 是 Dosi 的命令行界面:校验一份 OSI 语义模型、浏览它编译出的 指标/数据集/维度、把一次指标查询编译成方言 SQL,并把这段 SQL 在数仓上执行。 REST API 通过 HTTP 能做的一切,dosi 都能在 shell 里做: 同一个编译器、同样的结构化错误、同样的 --format json 机器契约。

$ dosi --help
Compile metric queries over OSI semantic models to dialect SQL

Usage: osi [OPTIONS] <COMMAND>

Commands:
  validate  Validate the model: structure, unique names, relationship integrity, and metric compilation
  list      List model objects
  query     Compile a metric query to SQL
  explain   Show the compiled IR shape and logical plan without generating SQL

安装

dosi单个自包含可执行文件的形式安装:工作区的库 crate,以及默认情况下的 DuckDB,都静态链接进同一个可执行文件,没有别的东西要装,也没有额外的包要管。 安装后落在 ~/.cargo/bin/osi(已在 PATH 上)。

要求: 一套 Rust 工具链(不低于工作区的 rust-version)和一个 C++ 编译器 (内置 DuckDB 需要从源码编译)。可选连接器里的 TLS 用 rustls, 不需要 OpenSSL 或其它系统库。--no-default-features 可以去掉内置 DuckDB (运行时改为依赖 duckdb CLI),同时也就不再需要 C++ 构建环境。

从 git 仓库安装

对私有仓库同样有效:任何有读权限的用户(SSH key 或 git 凭据助手)都能直接安装, 不必先发布到 crates.io。

$ cargo install --git ssh://git@github.com/datus-ai/osi-engine.git dosi-engine
# if SSH fetch fails, let cargo use your system git:
$ CARGO_NET_GIT_FETCH_WITH_CLI=true \
    cargo install --git ssh://git@github.com/datus-ai/osi-engine.git dosi-engine

--tag <tag> / --branch <branch> / --rev <sha> 固定版本,加 --locked 以提交进仓库的 Cargo.lock 构建,重新执行时加 --force 即可更新。因为每个用户都在 本地编译,可执行文件会自动匹配他们自己的操作系统/CPU (Linux、Intel 或 Apple Silicon 的 macOS)。

从 crates.io 安装

发布之后:

$ cargo install dosi-engine

带上数仓连接器

连接器是 cargo feature(见在数仓上执行); 安装时传入即可:

$ cargo install --git <url> dosi-engine --features exec-all          # every connector
$ cargo install --git <url> dosi-engine --features exec-mysql,exec-postgres
$ cargo install --git <url> dosi-engine --no-default-features        # lean, no bundled DuckDB

从源码构建

在一份 checkout 里,scripts/build_binary.sh 会构建一个 release 且 strip 过的可执行文件,并报告它的体积和自包含情况:

$ scripts/build_binary.sh            # default: bundled DuckDB, stripped
$ scripts/build_binary.sh --all      # + every connector (exec-all)
$ scripts/build_binary.sh --lean     # no bundled DuckDB

或者直接执行:cargo build --release -p dosi-engine --bin osi (可执行文件是 target/release/dosi)。产物只动态链接标准系统库 (glibc ≥ 2.34、libstdc++);DuckDB 本身是静态打包进去的。

全局选项

以下选项对每条命令都生效(clap 的 global = true):

参数 环境变量 默认值 含义
--model <path> DOSI_MODEL 必填 OSI 模型文件(.yaml / .yml / .json
--format <fmt> text 输出格式:textjsonarrow(见输出格式
--connections <path> DOSI_CONNECTIONS 发现链 --execute 用的连接配置文件(见在数仓上执行
--osi-datus 开启 Datus 模式:DATUS custom_extensions 会被采纳(datus-extensions.md
--osi-basic 关闭 基础模式,严格的标准 OSI。DATUS 扩展被忽略并给出警告;validate 还会额外运行上游校验器(见下)

--osi-datus--osi-basic 互斥,同时传入属于用法错误。基础模式下, 每个被忽略的扩展都会在 stderr 上打印一行 ! 警告(在 --format json 下则落在 warnings 里);警告永远不改变退出码。

如果设置了 DOSI_MODEL--model 可以省略;一条需要模型却两者都没找到的命令会以 no model given; pass --model <path> or set DOSI_MODEL 退出。

退出码: 0 成功,1 引擎拒绝了这次查询/执行(带结构化错误), 2 用法/CLI 错误(参数写错、缺模型、参数无法解析)。

命令

dosi info

报告引擎版本、OSI 规范版本和 datus-ext 版本,以及本引擎会读取的每一个 vendor_name: DATUScustom_extensions 键。它不需要模型, 所以也可以当作版本探测命令用。

$ dosi info
dosi       0.1.0
osi spec   0.2.0.dev0
datus-ext  1.1 (accepts 1.0 and up)
mode       datus

KEY               EXTENSION  CARRIERS         SINCE  IF IGNORED
join_type         D-JOIN     relationship     1.0    documented
fill_nulls_with   D-FILL     metric           1.0    documented
time_dimension    D-TIME     dataset, metric  1.1    degraded
time_granularity  D-GRAIN    field            1.1    documented
dataset           D-DATASET  metric           1.1    degraded

SINCE 是引入该键的 datus-ext 版本,IF IGNORED 是不采纳它要付出的代价, 见 datus-extensions.md §2.1--format json 以机器契约的形式输出同样的内容。 生成模型的 Agent 在决定输出哪些键之前,应该先读它。

dosi validate

加载模型并跑完整的校验流水线:结构、名称唯一性、关系引用完整性、 指标表达式编译,然后报告每一个问题。

$ dosi validate --model fixtures/tpcds/model.yaml
✓ 1 semantic model(s) valid

--format json 会输出 {issues, compile_errors, warnings} 供 CI 把关: 每个 issue 带严重级别和消息,每个编译错误带稳定的 code 和可选的 hint, 每条警告带稳定的 codelocationmessage。有错误的模型以非零码退出; 只有警告则不会。

上游校验(--osi-basic)。 在基础模式下,当能找到一份 checkout 时, validate 会额外调用上游的 apache/ossie 参考校验器(即 OSSIE_DIR,默认 ~/src/ossie,其下含有 validation/validate.py), 用已发布的规范来要求这份模型。上游失败会让整条命令失败; 找不到 checkout 或缺少 python3 时会打印一条提示,退回到只做内置校验。 --format json 会额外输出 "upstream": {ran, passed, output, note}

$ OSSIE_DIR=~/src/apache-ossie dosi validate --osi-basic --model model.yaml
✓ upstream OSI validation passed
✓ 1 semantic model(s) valid

dosi list <what>

浏览编译后的语义层。三个子命令,在 text 模式下各是一张表, 在 --format json 下则是一个对象数组:

子命令
dosi list datasets 名称、来源、主键、字段数、时间维度
dosi list metrics 名称、推断出的种类(aggregate / ratio / expression)、数据集、描述
dosi list dimensions dataset.field、时间标记、描述
$ dosi list metrics --model fixtures/tpcds/model.yaml
NAME                     KIND        DATASETS               DESCRIPTION
total_sales              aggregate   store_sales            Total sales revenue across all transactions
customer_lifetime_value  ratio       customer, store_sales  Average lifetime sales value per customer
store_productivity       expression  store, store_sales     Sales per employee across stores

dosi query

核心命令:把一次指标查询编译成方言 SQL,并可选地执行它。查询本身由下面共用的 query spec 参数来描述;在此之上,query 还接受:

参数 含义
--dialect <name> 目标 SQL 方言(默认 duckdb,或 --connection 所指配置的方言)
--pretty 美化打印生成的 SQL
--explain 在 SQL 上方一并打印逻辑计划
--execute 把编译出的 SQL 拿到数仓上执行并打印结果行
--connection <name> 连接配置文件里的具名配置(同时决定方言)
--db <path> 不带 --connection--execute 使用的 DuckDB 文件(默认:内存)

只编译(不加 --execute)会打印 SQL(text)或 {dialect, sql}json):

$ dosi query --model fixtures/tpcds/model.yaml \
    --metrics total_sales \
    --group-by store.s_state,date_dim.d_date:month \
    --where "item.i_category = 'Books'" \
    --start-time 2024-01-01 --end-time 2025-01-01 \
    --order -total_sales --limit 100 \
    --dialect starrocks
SELECT store.s_state AS s_state, DATE_TRUNC('MONTH', date_dim.d_date) AS d_date__month, ...

(把 total_sales 和基于 store 的指标(比如 store_productivity)放在一起, 会得到 fan_out_risk 错误:按门店计算的度量,无法按门店够不到的日期来分组。 这种保护正是要点所在,见 semantics.md §6。)

query spec 参数(与 explain 共用)

参数 含义
--metrics <a,b,…> 必填。 逗号分隔的指标名
--group-by <items> 逗号分隔的 dataset.fielddataset.field:grain(粒度:day\|week\|month\|quarter\|year);metric_time[:grain] 表示按每个指标各自的主时间维度分组
--where <sql> 作用在维度字段上的标量布尔 SQL,在聚合之前应用
--start-time <YYYY-MM-DD> 时间范围的下界,该值
--end-time <YYYY-MM-DD> 上界,不含该值
--time-dimension <field> 该范围作用在哪个时间维度上(默认:group-by 里唯一的那个时间维度,否则是每个指标的主时间;metric_time 用于显式指明)
--order <keys> 逗号分隔的排序键;前缀 - 表示降序
--limit <n> 行数上限

有三条行为值得记牢(完整契约见 semantics.md):

  • 粒度的输出列命名。 --group-by orders.order_date:month 会产生名为 order_date__month 的列({field}__{grain}),--order 里也引用这个名字。
  • 排序键用输出列名,不是限定字段名。--order -total_sales--order ds__month,而不是 orders.status。前缀 - 表示降序; clap 会把它当成参数标志,所以 --order 特意允许了前导连字符。
  • 时间范围是左闭右开的 [start, end) --start-time 2024-01-01 --end-time 2025-01-01 包含整个 2024 年,且恰好不含 2025-01-01。 当既没有 --time-dimension、group-by 里也没有时间项时,范围会回退到每个指标的 主(聚合)时间维度(通过 Datus 的 D-TIME 扩展声明,或取数据集里唯一的 is_time 字段,见 datus-extensions.md); 只有当 group-by 里同时存在多个时间维度时才仍然需要显式的 --time-dimensiontime_range_needs_dimension)。保留名 metric_time 用于显式选中主时间, 在 --group-bymetric_time:month → 输出列 metric_time__month)和 --time-dimension 里都可以用。

dosi explain

接受与 query 相同的 query spec 参数,但停在逻辑计划这一步,不生成 SQL。 适合在挑定方言之前,用来理解连接路径、扇出分支归属和粒度处理。

$ dosi explain --model fixtures/tpcds/model.yaml \
    --metrics customer_lifetime_value --group-by store.s_state

计划以文本渲染,其中包含每个 JOIN 的类型(left / inner), 据此可以确认 Datus 的 join_type 扩展是否生效。 这里的 --format json 只作用于错误路径,成功的计划只有文本形式。

输出格式

--format 接受三个值之一:

  • text(默认):面向人,结果行/列表是对齐的表格,编译结果是原始 SQL, 计划是缩进的树。NULL 单元格以灰显渲染。颜色会自动探测终端。
  • json:所有输出(以及所有错误)都是机器可读的。query 的编译结果是 {dialect, sql}--execute 会再加上 {columns, rows: [{col: val}], …}validate{issues, compile_errors}list 是一个对象数组。 错误带有稳定的 code、涉及到的名字、当一个错误引用存在备选时给出的 candidates,以及当某种改写能成功时给出的 suggested_retry, Agent 类调用方无需解析自然语言就能自我纠正。稳定 API 是错误码,不是错误文本。
  • arrow:在 stdout 上输出一个 Arrow IPC 流,用于 query --execute(其它命令会报错:--format arrow only applies to 'query --execute')。结果批次从数仓适配器直通 stdout,不做行物化, 可以零 JSON 解析地管进 DuckDB、Polars 或 pyarrow。需要支持 Arrow 的构建 (默认构建,或任意 exec-*-arrow / exec-flightsql / exec-duckdb feature; 一个不含这些 feature 的 --no-default-features 构建会在运行时拒绝 --format arrow)。
# Stream results into DuckDB for further analysis
$ dosi query --model model.yaml --metrics revenue --group-by orders.status \
    --execute --connection prod-ch --format arrow \
    | duckdb -c "SELECT * FROM read_arrow('/dev/stdin')"

# Or into Polars
$ dosi query ... --execute --format arrow \
    | python -c "import polars as pl,sys; print(pl.read_ipc_stream(sys.stdin.buffer))"

在数仓上执行

--execute 会执行编译出的 SQL 并打印结果行。不指定连接就用本地 DuckDB, 默认进程内、原生 Arrow(内置的 exec-duckdb);--db <file> 指向 DuckDB 文件,不指定则用内存库。带上 --connection <name>, 则指向该配置对应的数仓和方言。

$ dosi query --model model.yaml --metrics revenue --group-by orders.status \
    --execute --connection prod-sr

连接配置沿用 Datus agent.ymldatasources: 词汇表。可以把 --connections 指向一份完整的 agent.yml(读取 services.datasources),或一份独立的 datasources: 文件。不带该参数时, 按以下顺序发现该文件:DOSI_CONNECTIONS 环境变量 → ./dosi-connections.yaml./osi-connections.yaml~/.config/dosi/connections.yaml~/.config/osi/connections.yaml./conf/agent.yml~/.datus/conf/agent.yml。已有的 Datus 安装零配置就能用, 改名之前的 osi- 路径也仍然有效。密钥以 ${VAR} 的形式从环境变量插值。

datasources:
  prod-sr:
    type: starrocks
    host: sr.internal
    port: 9030
    arrow_flight_port: 9408      # opt into Arrow Flight SQL (SR ≥3.5.1)
    username: osi
    password: ${SR_PASSWORD}
    database: analytics
    default: true                # used by --execute without --connection
  prod-ch:
    type: clickhouse
    uri: http://ch.internal:8123
    username: default
    database: analytics

解析规则:

  • --execute 不带 --connection 时用标了 default: true 的那份配置; 一份都没标就退回本地 DuckDB(--db 或内存)。标了 default: true 却解析失败的配置(比如 ${VAR} 没设置)会在 stderr 上告警, 而不是悄悄用一个空的 DuckDB。
  • --dialect--connection 同时使用时,方言必须与配置一致, 否则命令报错。去掉 --dialect,让配置来决定就行。

数仓驱动是按 feature 开关的,好让默认的可执行文件保持精简。 按需构建(或直接用 exec-all):

Feature 引擎 结果通路
exec-duckdb(默认) DuckDB(进程内) 原生 Arrow
exec-mysql MySQL、TiDB、StarRocks、Doris(MySQL 协议)
exec-postgres Postgres
exec-hologres Hologres(Postgres 协议;隐含 exec-postgres
exec-gaussdb GaussDB / openGauss(原生 SHA256 认证驱动)
exec-oracle Oracle Database(ODPI-C;运行时需要 Instant Client)
exec-http ClickHouse、Trino
exec-http-arrow ClickHouse FORMAT ArrowStream 原生 Arrow
exec-flightsql StarRocks / Doris 的 Arrow Flight SQL(arrow_flight_port: 原生 Arrow
exec-snowflake Snowflake(SQL API v2 + 密钥对 JWT)

各引擎的配置方式与 Arrow 结果通路的支持状态见 connectors.mdarrow.md

另请参阅

  • semantics.md:CLI 所编译到的那份规范性行为契约 (指标推断、连接、扇出保护、时间处理)。
  • rest-api.md:同样的能力,通过 HTTP 提供。
  • extensions-guide.md:可选的 Datus 模型扩展 (join_typefill_nulls_withtime_dimensiontime_granularitydataset);它们的规范性契约与版本策略见 datus-extensions.md
  • connectors.md:数仓连接器的配置。
  • arrow.md:如何启用 Arrow 结果传输,以及哪些环节会变快。