blob: db004314394aea2ba2b6118fa1020a451520fc76 [file]
# shellcheck shell=bash
# suite_sql — SQL execution surface: the JSON envelope, -f files, --set session
# vars, --no-cache, --profile, the three output formats, and the error path.
# Runs in stateless mode (env-var connection; no config files).
suite_sql() {
suite_banner "sql (execution, session vars, formats, errors)"
DCLI_STATELESS=1
# Envelope shape: query_id, exec_time_ms, rows_returned, columns[], rows[].
expect_json "sql: simple select returns the full JSON envelope" \
'.rows[0].one==1 and (.columns|index("one")) and (.query_id|type=="string") and (.rows_returned==1) and (.exec_time_ms>=0)' \
sql "SELECT 1 AS one"
# Mixed types: string stays a string, integer becomes a JSON number.
expect_json "sql: string and integer columns map correctly" \
'.rows[0].g=="hello" and .rows[0].n==42' \
sql "SELECT 'hello' AS g, 42 AS n"
# -f <file>: read the query from a file.
printf 'SELECT 7 AS seven' > "$WORKDIR/seven.sql"
expect_json "sql: -f reads the query from a file" \
'.rows[0].seven==7' \
sql -f "$WORKDIR/seven.sql"
# --set applies a session var before the query (integer var avoids normalization).
expect_json "sql: --set applies a session variable" \
'.rows[0].p==5' \
sql "SELECT @@parallel_pipeline_task_num AS p" --set "parallel_pipeline_task_num=5"
# --set is repeatable.
expect_json "sql: multiple --set flags all apply" \
'.rows[0].p==6 and .rows[0].w==2000' \
sql "SELECT @@parallel_pipeline_task_num AS p, @@runtime_filter_wait_time_ms AS w" \
--set "parallel_pipeline_task_num=6" --set "runtime_filter_wait_time_ms=2000"
# --no-cache: just confirm it executes cleanly.
expect_json "sql: --no-cache executes" \
'.rows_returned==1' \
sql "SELECT 1 AS one" --no-cache
# --profile: produces a real Doris query id (TUniqueId rendered as hex-hi-hex-lo,
# e.g. "a1b2c3d4...-e5f6..."), reused by the profile suite to fetch the profile.
expect_json "sql: --profile yields a real Doris query_id" \
'(.query_id|type=="string") and (.query_id|test("^[0-9a-fA-F]+-[0-9a-fA-F]+$"))' \
sql "SELECT 2 AS two" --profile
# Output formats: a TTY-style table and CSV both carry the column header.
expect_stdout_contains "sql: --format table renders the column" "one" \
sql "SELECT 1 AS one" --format table
expect_stdout_contains "sql: --format csv renders the header" "one" \
sql "SELECT 1 AS one" --format csv
# Empty result set: zero rows, empty rows array, still exit 0.
expect_json "sql: empty result set is handled" \
'.rows_returned==0 and (.rows==[])' \
sql "SELECT table_name FROM information_schema.tables WHERE 1=0"
# End-to-end against loaded data: row count matches what setup loaded, and the
# envelope reports a real (non-negative) execution time for a query that ran.
expect_json "sql: count over loaded table matches the load" \
'.rows[0].c=='"${ROWS:-2000}"' and (.exec_time_ms|type=="number") and (.exec_time_ms>=0)' \
sql "SELECT COUNT(*) AS c FROM \`$CFG_DB\`.events"
# Error path: a bad reference exits non-zero with an "SQL error:" message.
EXPECT_ERR_MATCH="SQL error"
expect_err "sql: invalid query exits non-zero" \
sql "SELECT * FROM ${CFG_DB}.no_such_table_xyz"
EXPECT_ERR_MATCH=""
}