blob: 4836e8a0f1e76f3cad84f5a9c9493b8c32f2de4b [file] [view]
---
title: Regression Testing Framework
language: en
description: Introduction to the Apache Doris regression testing framework and a guide to writing test cases, covering Suites, Actions, external data source e2e tests, and CI integration.
keywords:
- Apache Doris regression testing
- regression test
- Suite Action
- Groovy test case
- Doris testing framework
- run-regression-test.sh
- Docker Compose external data source
- TeamCity integration
---
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<!-- Knowledge type: Developer guide / Testing framework -->
<!-- Applicable scenario: Kernel development / Submitting PRs / Writing regression cases -->
# Regression Testing Framework
This document is for developers who need to write or run regression tests for Apache Doris. It introduces the core concepts, directory layout, and configuration of the testing framework, along with the built-in Actions and the usage of external data source e2e tests.
## Contents
- [Quick Start](#quick-start): Run your first case in 5 steps
- [Core Concepts](#core-concepts): The three abstractions Suite / Group / Action
- [Framework Directory Structure](#framework-directory-structure): How source code and data files are organized
- [Default Configuration File](#default-configuration-file): Key fields in `regression-conf.groovy`
- [Writing Test Cases](#writing-test-cases): Examples of sql / qt / test / explain / streamLoad and other Actions
- [Launch Script Usage](#launch-script-usage): Common commands for `run-regression-test.sh`
- [Auto-Generating `.out` Files](#auto-generating-out-files): Generate validation files with `-genOut` / `-forceGenOut`
- [Suite Plugin Mechanism](#suite-plugin-mechanism): Extend Suite with Groovy scripts
- [CI/CD Integration](#cicd-integration): TeamCity Service Message
- [External Data Source e2e Tests](#external-data-source-e2e-tests): MySQL / PostgreSQL / Hive / Iceberg / Hudi / Trino, and more
---
## Quick Start
<!-- Knowledge type: Procedure -->
1. Install a Doris cluster (FE + BE) in advance.
2. Edit `${DORIS_HOME}/regression-test/conf/regression-conf.groovy` and set connection information such as the JDBC URL and username.
3. Create a Groovy test case file under `regression-test/suites/<group>/`.
4. If the case contains a `qt` Action, create the corresponding `.out` TSV file under `regression-test/data/<group>/` for result validation.
5. Run the tests:
```bash
# Run all cases
${DORIS_HOME}/run-regression-test.sh
# Run a specific case
${DORIS_HOME}/run-regression-test.sh --run <suiteName>
```
See [Launch Script Usage](#launch-script-usage) for more startup options.
---
## Core Concepts
| Concept | Meaning |
|------|------|
| `Suite` | A test case. The file name is currently used as the suite name. |
| `Group` | A test set, corresponding to the directory the test case belongs to. |
| `Action` | A specific testing behavior provided by the framework, such as the `sql` Action that executes SQL, the `test` Action that validates results, and the `streamLoad` Action that imports data. |
---
## Framework Directory Structure
Important files and directories to be aware of during development:
| Path | Purpose |
|------|------|
| `run-regression-test.sh` | Regression test launch script |
| `regression-conf.groovy` | Default configuration file |
| `data/` | Input data and `.out` validation files |
| `suites/` | Test cases |
Full directory tree:
```text
./${DORIS_HOME}
|-- run-regression-test.sh Regression test launch script
|-- regression-test
| |-- plugins Plugins directory
| |-- conf
| | |-- logback.xml Logging configuration file
| | |-- regression-conf.groovy Default configuration file
| |
| |-- framework Regression test framework source code
| |-- data Input and output files for cases
| | |-- demo Input and output files for demo cases
| | |-- correctness Input and output for correctness tests
| | |-- performance Input and output for performance tests
| | |-- utils Input and output for other utilities
| |
| |-- suites Regression test cases
| |-- demo Demo cases
| |-- correctness Correctness test cases
| |-- performance Performance test cases
| |-- utils Other utility cases
|
|-- output
|-- regression-test
|-- log Regression test logs
```
---
## Default Configuration File
<!-- Knowledge type: Configuration parameters -->
Before running tests, update the JDBC and FE configuration to match the actual environment. The core fields of `regression-conf.groovy` are as follows:
```groovy
/* ============ Generally, only the section below needs attention ============ */
// Default DB. If it has not been created, the framework attempts to create it.
defaultDb = "regression_test"
// JDBC configuration
jdbcUrl = "jdbc:mysql://127.0.0.1:9030/?"
jdbcUser = "root"
jdbcPassword = ""
// FE address configuration, used for stream load
feHttpAddress = "127.0.0.1:8030"
feHttpUser = "root"
feHttpPassword = ""
/* ============ The section below generally does not need to be modified ============ */
// The DORIS_HOME variable is passed in by run-regression-test.sh,
// that is, java -DDORIS_HOME=./
// Set the directory of regression test cases
suitePath = "${DORIS_HOME}/regression-test/suites"
// Set the directory of input and output data
dataPath = "${DORIS_HOME}/regression-test/data"
// Set the plugins directory
pluginPath = "${DORIS_HOME}/regression-test/plugins"
// By default, all groups are loaded. Multiple groups can be separated by half-width commas, for example: "demo,performance"
// Usually you do not need to modify this in the config file; instead, override it dynamically through run-regression-test.sh --run -g
testGroups = ""
// By default, all cases are loaded. You can also override this through run-regression-test.sh --run -s
testSuites = ""
// The case directories loaded by default. Override through run-regression-test.sh --run -d
testDirectories = ""
// Exclude cases in these groups. Override through run-regression-test.sh --run -xg
excludeGroups = ""
// Exclude these suites. Override through run-regression-test.sh --run -xs
excludeSuites = ""
// Exclude these directories. Override through run-regression-test.sh --run -xd
excludeDirectories = ""
// Other custom configuration
customConf1 = "test_custom_conf_value"
```
---
## Writing Test Cases
<!-- Knowledge type: Procedure -->
1. Go to the `${DORIS_HOME}/regression-test` directory.
2. Choose a case directory based on the testing purpose: place correctness tests under `suites/correctness` and performance tests under `suites/performance`.
3. Create a new Groovy case file and combine several Actions to implement the test logic.
Actions are testing behaviors provided by the framework, defined through a DSL. The common Actions are introduced below.
### sql action
The `sql` Action submits SQL and retrieves the result. It throws an exception if the query fails.
Parameters:
| Parameter | Description |
|------|------|
| `String sql` | The input SQL string |
| Returns `List<List<Object>>` | The query result. For DDL/DML, it returns one row and one column, whose only value is updateRowCount. |
The sample code below is located at `${DORIS_HOME}/regression-test/suites/demo/sql_action.groovy`:
```groovy
suite("sql_action", "demo") {
// execute sql and ignore result
sql "show databases"
// execute sql and get result, outer List denote rows, inner List denote columns in a single row
List<List<Object>> tables = sql "show tables"
// assertXxx() will invoke junit5's Assertions.assertXxx() dynamically
assertTrue(tables.size() >= 0) // test rowCount >= 0
// syntax error
try {
sql "a b c d e"
throw new IllegalStateException("Should be syntax error")
} catch (java.sql.SQLException t) {
assertTrue(true)
}
def testTable = "test_sql_action1"
try {
sql "DROP TABLE IF EXISTS ${testTable}"
// multi-line sql
def result1 = sql """
CREATE TABLE IF NOT EXISTS ${testTable} (
id int
)
DISTRIBUTED BY HASH(id) BUCKETS 1
PROPERTIES (
"replication_num" = "1"
)
"""
// DDL/DML return 1 row and 1 column, the only value is update row count
assertTrue(result1.size() == 1)
assertTrue(result1[0].size() == 1)
assertTrue(result1[0][0] == 0, "Create table should update 0 rows")
def result2 = sql "INSERT INTO test_sql_action1 values(1), (2), (3)"
assertTrue(result2.size() == 1)
assertTrue(result2[0].size() == 1)
assertTrue(result2[0][0] == 3, "Insert should update 3 rows")
} finally {
/**
* try_xxx(args) means:
*
* try {
* return xxx(args)
* } catch (Throwable t) {
* // do nothing
* return null
* }
*/
try_sql("DROP TABLE IF EXISTS ${testTable}")
// you can see the error sql will not throw exception and return
try {
def errorSqlResult = try_sql("a b c d e f g")
assertTrue(errorSqlResult == null)
} catch (Throwable t) {
assertTrue(false, "Never catch exception")
}
}
// order_sql(sqlStr) equals to sql(sqlStr, isOrder=true)
// sort result by string dict
def list = order_sql """
select 2
union all
select 1
union all
select null
union all
select 15
union all
select 3
"""
assertEquals(null, list[0][0])
assertEquals(1, list[1][0])
assertEquals(15, list[2][0])
assertEquals(2, list[3][0])
assertEquals(3, list[4][0])
}
```
### qt action
The `qt` Action submits SQL and validates the result against the corresponding `.out` TSV file.
| Parameter | Description |
|------|------|
| `String sql` | The input SQL string |
| Returns | void |
The sample code below is located at `${DORIS_HOME}/regression-test/suites/demo/qt_action.groovy`:
```groovy
suite("qt_action", "demo") {
/**
* qt_xxx sql equals to quickTest(xxx, sql) witch xxx is tag.
* the result will be compare to the relate file: ${DORIS_HOME}/regression_test/data/qt_action.out.
*
* if you want to generate .out tsv file for real execute result. you can run with -genOut or -forceGenOut option.
* e.g
* ${DORIS_HOME}/run-regression-test.sh --run qt_action -genOut
* ${DORIS_HOME}/run-regression-test.sh --run qt_action -forceGenOut
*/
qt_select "select 1, 'beijing' union all select 2, 'shanghai'"
qt_select2 "select 2"
// order result by string dict then compare to .out file.
// order_qt_xxx sql equals to quickTest(xxx, sql, true).
order_qt_union_all """
select 2
union all
select 1
union all
select null
union all
select 15
union all
select 3
"""
}
```
### test action
The `test` Action supports more complex validation rules, such as checking row counts, execution time, and whether an exception is thrown.
Available parameters:
| Parameter | Description |
|------|------|
| `String sql` | The input SQL string |
| `List<List<Object>> result` | Compares the actual query result with a List object for equality |
| `Iterator<Object> resultIterator` | Compares the actual query result with an Iterator for equality |
| `String resultFile` | A file URI (a local relative path or an http(s) path) used to compare the query result. The format is similar to `.out` but without block headers and comments. |
| `String exception` | Validates that the thrown exception contains specific strings |
| `long rowNum` | Validates the row count of the result |
| `long time` | Validates that the execution time is less than this value, in milliseconds |
| `Closure<List<List<Object>>, Throwable, Long, Long> check` | A custom validation callback that receives the result, exception, and time. When the callback is present, other validation methods are disabled. |
The sample code below is located at `${DORIS_HOME}/regression-test/suites/demo/test_action.groovy`:
```groovy
suite("test_action", "demo") {
test {
sql "abcdefg"
// check exception message contains
exception "errCode = 2, detailMessage = Syntax error"
}
test {
sql """
select *
from (
select 1 id
union all
select 2
) a
order by id"""
// multi check condition
// check return 2 rows
rowNum 2
// execute time must <= 5000 millisecond
time 5000
// check result, must be 2 rows and 1 column, the first row is 1, second is 2
result(
[[1], [2]]
)
}
test {
sql "a b c d e f g"
// other check will not work because already declared a check callback
exception "aaaaaaaaa"
// callback
check { result, exception, startTime, endTime ->
// assertXxx() will invoke junit5's Assertions.assertXxx() dynamically
assertTrue(exception != null)
}
}
test {
sql """
select 2
union all
select 1
union all
select null
union all
select 15
union all
select 3
"""
check { result, ex, startTime, endTime ->
// same as order_sql(sqlStr)
result = sortRows(result)
assertEquals(null, result[0][0])
assertEquals(1, result[1][0])
assertEquals(15, result[2][0])
assertEquals(2, result[3][0])
assertEquals(3, result[4][0])
}
}
// execute sql and order query result, then compare to iterator
def selectValues = [1, 2, 3, 4]
test {
order true
sql selectUnionAll(selectValues)
resultIterator(selectValues.iterator())
}
// compare to data/demo/test_action.csv
test {
order true
sql selectUnionAll(selectValues)
// you can set to http://xxx or https://xxx
// and compare to http response body
resultFile "test_action.csv"
}
}
```
### explain action
The `explain` Action validates whether the string returned by `EXPLAIN` contains specific strings.
Available parameters:
| Parameter | Description |
|------|------|
| `String sql` | The SQL to query. Remove `EXPLAIN` from the SQL. |
| `String contains` | Validates that the explain output contains specific strings. Call multiple times to validate multiple strings at once. |
| `String notContains` | Validates that the explain output does not contain specific strings. Can be called multiple times. |
| `Closure<String> check` | A custom validation callback that receives the returned string. When the validation function is present, other validation methods are disabled. |
| `Closure<String, Throwable, Long, Long> check` | A custom validation callback that additionally receives the exception and time. |
The sample code below is located at `${DORIS_HOME}/regression-test/suites/demo/explain_action.groovy`:
```groovy
suite("explain_action", "demo") {
explain {
sql("select 100")
// contains("OUTPUT EXPRS:<slot 0> 100\n") && contains("PARTITION: UNPARTITIONED\n")
contains "OUTPUT EXPRS:<slot 0> 100\n"
contains "PARTITION: UNPARTITIONED\n"
}
explain {
sql("select 100")
// contains(" 100\n") && !contains("abcdefg") && !("1234567")
contains " 100\n"
notContains "abcdefg"
notContains "1234567"
}
explain {
sql("select 100")
// simple callback
check { explainStr -> explainStr.contains("abcdefg") || explainStr.contains(" 100\n") }
}
explain {
sql("a b c d e")
// callback with exception and time
check { explainStr, exception, startTime, endTime ->
// assertXxx() will invoke junit5's Assertions.assertXxx() dynamically
assertTrue(exception != null)
}
}
}
```
### streamLoad action
The `streamLoad` Action imports data.
Available parameters:
| Parameter | Description |
|------|------|
| `String db` | DB name. Defaults to `defaultDb` from `regression-conf.groovy`. |
| `String table` | Table name |
| `String file` | The path of the file to import. Can be a relative path under the `data/` directory, or an http URL. |
| `Iterator<List<Object>> inputIterator` | The iterator to import |
| `String inputText` | The text to import. Rarely used. |
| `InputStream inputStream` | The byte stream to import. Rarely used. |
| `long time` | Validates that the execution time is less than this value, in milliseconds |
| `void set(String key, String value)` | Sets the stream load HTTP request header, such as `label` or `columnSeparator` |
| `Closure<String, Throwable, Long, Long> check` | A custom validation callback. When the callback is present, other validation items are disabled. |
The sample code below is located at `${DORIS_HOME}/regression-test/suites/demo/streamLoad_action.groovy`:
```groovy
suite("streamLoad_action", "demo") {
def tableName = "test_streamload_action1"
sql """
CREATE TABLE IF NOT EXISTS ${tableName} (
id int,
name varchar(255)
)
DISTRIBUTED BY HASH(id) BUCKETS 1
PROPERTIES (
"replication_num" = "1"
)
"""
streamLoad {
// you can skip declare db, because a default db already specify in ${DORIS_HOME}/conf/regression-conf.groovy
// db 'regression_test'
table tableName
// default label is UUID:
// set 'label' UUID.randomUUID().toString()
// default column_separator is specify in doris fe config, usually is '\t'.
// this line change to ','
set 'column_separator', ','
// relate to ${DORIS_HOME}/regression-test/data/demo/streamload_input.csv.
// also, you can stream load a http stream, e.g. http://xxx/some.csv
file 'streamload_input.csv'
time 10000 // limit inflight 10s
// stream load action will check result, include Success status, and NumberTotalRows == NumberLoadedRows
}
// stream load 100 rows
def rowCount = 100
// range: [0, rowCount)
// or rangeClosed: [0, rowCount]
def rowIt = range(0, rowCount)
.mapToObj({i -> [i, "a_" + i]}) // change Long to List<Long, String>
.iterator()
streamLoad {
table tableName
// also, you can upload a memory iterator
inputIterator rowIt
// if declared a check callback, the default check condition will ignore.
// So you must check all condition
check { result, exception, startTime, endTime ->
if (exception != null) {
throw exception
}
log.info("Stream load result: ${result}".toString())
def json = parseJson(result)
assertEquals("success", json.Status.toLowerCase())
assertEquals(json.NumberTotalRows, json.NumberLoadedRows)
assertTrue(json.NumberLoadedRows > 0 && json.LoadBytes > 0)
}
}
}
```
### Other Actions
Examples of the `thread`, `lazyCheck`, `events`, `connect`, and `selectUnionAll` Actions can be found in the following directory:
```text
${DORIS_HOME}/regression-test/suites/demo
```
---
## Launch Script Usage
<!-- Knowledge type: Procedure -->
```bash
# View the script parameter description
./run-regression-test.sh h
# View the framework parameter description
./run-regression-test.sh --run -h
# Run all cases
./run-regression-test.sh
# Delete the framework compilation results and test logs
./run-regression-test.sh --clean
# Run the case whose suiteName is sql_action. Currently the suiteName equals the file name prefix; this example corresponds to the case file sql_action.groovy
./run-regression-test.sh --run sql_action
# Run cases whose suiteName contains 'sql'. **Note that single quotes are required.**
./run-regression-test.sh --run '*sql*'
# Run the demo and performance groups
./run-regression-test.sh --run -g 'demo,performance'
# Run sql_action under the demo group
./run-regression-test.sh --run -g demo -s sql_action
# Run sql_action under the demo directory
./run-regression-test.sh --run -d demo -s sql_action
# Run cases under the demo directory, excluding sql_action
./run-regression-test.sh --run -d demo -xs sql_action
# Exclude cases under the demo directory
./run-regression-test.sh --run -xd demo
# Exclude cases under the demo group
./run-regression-test.sh --run -xg demo
# Custom configuration
./run-regression-test.sh --run -conf a=b
# Concurrent execution
./run-regression-test.sh --run -parallel 5 -suiteParallel 10 -actionParallel 20
```
---
## Auto-Generating `.out` Files
```bash
# Automatically generate the .out file for the sql_action case from the query result. Skip if the .out file already exists.
./run-regression-test.sh --run sql_action -genOut
# Automatically generate the .out file for the sql_action case from the query result. Overwrite if the .out file already exists.
./run-regression-test.sh --run sql_action -forceGenOut
```
---
## Suite Plugin Mechanism
When you need to extend the Suite class without modifying the source code, you can do so through plugins. The default plugin directory is `${DORIS_HOME}/regression-test/plugins`, where you can define extension methods through Groovy scripts.
The following `plugin_example.groovy` adds a `testPlugin` function to the Suite class to print logs:
```groovy
import org.apache.doris.regression.suite.Suite
// register `testPlugin` function to Suite,
// and invoke in ${DORIS_HOME}/regression-test/suites/demo/test_plugin.groovy
Suite.metaClass.testPlugin = { String info /* param */ ->
// which suite invoke current function?
Suite suite = delegate as Suite
// function body
suite.getLogger().info("Test plugin: suiteName: ${suite.name}, info: ${info}".toString())
// optional return value
return "OK"
}
logger.info("Added 'testPlugin' function to Suite")
```
Once registered, regular cases can call this function. Take `${DORIS_HOME}/regression-test/suites/demo/test_plugin.groovy` as an example:
```groovy
suite("test_plugin", "demo") {
// register testPlugin function in ${DORIS_HOME}/regression-test/plugins/plugin_example.groovy
def result = testPlugin("message from suite")
assertEquals("OK", result)
}
```
---
## CI/CD Integration
### TeamCity
TeamCity can recognize Service Messages through stdout. When the regression test framework is started with the `--teamcity` parameter, the framework prints TeamCity Service Messages to stdout. TeamCity automatically reads these event logs and displays `Tests` in the current pipeline, including test cases and their logs.
A sample launch command is shown below. The `-Dteamcity.enableStdErr=false` setting makes error logs also print to stdout, which makes it easier to analyze them in chronological order:
```bash
JAVA_OPTS="-Dteamcity.enableStdErr=${enableStdErr}" ./run-regression-test.sh --teamcity --run
```
---
## External Data Source e2e Tests
<!-- Knowledge type: Procedure -->
<!-- Applicable scenario: External data source development / Catalog debugging -->
Doris supports querying multiple external data sources. The regression framework provides the capability to set up external data sources through Docker Compose, used for e2e testing of Doris integrating with external data sources.
### 0. Preparation
Before starting Docker, edit the `CONTAINER_UID` variable in `docker/thirdparties/custom_settings.env`, for example set it to `doris-10002-18sda1-`. The subsequent launch scripts replace the names in docker compose with this prefix, ensuring that the names and networks of multiple container environments do not conflict.
Before starting the containers, check the network configuration of the server or cloud host and confirm that `/etc/hosts` contains the mapping between the host name (`hostname`) and the host IP (`hostname -i`), such as:
```text
10.0.0.46 iZj6cbwlx5pl6y0681t6scZ iZj6cbwlx5pl6y0681t6scZ
```
These correspond to the IP address (output of `hostname -i`, usually the IP of eth0), the host name (output of `hostname`), and the alias (the same as the host name), respectively.
> **Note**: `run-thirdparties-docker.sh` detects the primary network interface by scanning for names matching `eth[0-9]`. Modern Linux distributions using systemd/udev assign predictable names such as `enp3s0`, `ens33`, or `eno1`, which the script does not recognize. If your host uses such a name, the script will fail to detect an interface — manually set the `eth_name` variable inside `run-thirdparties-docker.sh` to your actual interface name before running it.
### 1. Start the Container
Doris currently supports Docker compose for data sources including es, mysql, pg, hive, sqlserver, oracle, iceberg, hudi, and trino. The related files are stored in the `docker/thirdparties/docker-compose` directory.
By default, you can start the Docker containers of all external data sources directly with the following command (hive and hudi require downloading prebuilt data files; see the corresponding sections below):
```bash
cd docker/thirdparties && sh run-thirdparties-docker.sh
```
This command requires root or sudo privileges. A successful return indicates that all containers have started, and you can check them with `docker ps -a`. During container startup, you can view logs with `docker logs -f <container-name>`.
Stop all containers:
```bash
cd docker/thirdparties && sh run-thirdparties-docker.sh --stop
```
Start or stop specific components:
```bash
cd docker/thirdparties
# Start mysql
sh run-thirdparties-docker.sh -c mysql
# Start mysql, pg, iceberg
sh run-thirdparties-docker.sh -c mysql,pg,iceberg
# Stop mysql, pg, iceberg
sh run-thirdparties-docker.sh -c mysql,pg,iceberg --stop
```
#### 1.1 MySQL
The MySQL-related Docker compose files are stored under `docker/thirdparties/docker-compose/mysql`:
- `mysql-5.7.yaml.tpl`: Docker compose file template. No modifications required. The default username and password are `root` / `123456`.
- `mysql-5.7.env`: Configuration file. Configures the port exposed by the MySQL container, which defaults to 3316.
- `init/`: SQL files in this directory run automatically after the container is created. By default, they create databases and tables and insert a small amount of data.
- `data/`: The local data directory mounted after the container starts. `run-thirdparties-docker.sh` clears and recreates it on every launch.
#### 1.2 PostgreSQL
The PostgreSQL-related Docker compose files are stored under `docker/thirdparties/docker-compose/postgresql`:
- `postgresql-14.yaml.tpl`: Docker compose file template. No modifications required. The default username and password are `postgres` / `123456`.
- `postgresql-14.env`: Configuration file. Configures the port exposed by the PostgreSQL container, which defaults to 5442.
- `init/`: SQL files in this directory run automatically after the container is created. By default, they create databases and tables and insert a small amount of data.
- `data/`: The local data directory mounted after the container starts. `run-thirdparties-docker.sh` clears and recreates it on every launch.
#### 1.3 Hive
The Hive-related Docker compose files are stored under `docker/thirdparties/docker-compose/hive`, supporting both Hive2 and Hive3:
- `hive-2x.yaml.tpl`, `hive-3x.yaml.tpl`: Docker compose file templates. No modifications required.
- `hadoop-hive.env.tpl`, `hadoop-hive-2x.env.tpl`, `hadoop-hive-3x.env.tpl`: Configuration file templates. No modifications required.
- `hive-2x_settings.env`: Hive2 initialization configuration script. `run-thirdparties-docker.sh` calls it automatically on startup. You can modify the four exposed ports `FS_PORT`, `HMS_PORT`, `HS_PORT`, and `PG_PORT`, which correspond to `hive2HdfsPort`, `hive2HmsPort`, `hive2ServerPort`, and `hive2PgPort` in `regression-conf.groovy`. The first two are the hadoop defaultFs and Hive metastore ports, defaulting to 8020 and 9083.
- `hive-3x_settings.env`: Hive3 initialization configuration script. You can modify `FS_PORT`, `HMS_PORT`, `HS_PORT`, and `PG_PORT`, which correspond to `hive3HdfsPort`, `hive3HmsPort`, `hive3ServerPort`, and `hive3PgPort`. The first two default to 8320 and 9383.
- The `scripts/` directory is mounted into the container after the container starts. The file contents do not need to be modified. Before starting the container, however, you must first download the prebuilt files:
Download `https://doris-regression-hk.oss-cn-hongkong.aliyuncs.com/regression/datalake/pipeline_data/tpch1.db.tar.gz` into the `scripts/` directory and extract it.
#### 1.4 Elasticsearch
Includes Docker images for ES6, ES7, and ES8, stored under `docker/thirdparties/docker-compose/elasticsearch/`:
- `es.yaml.tpl`: Docker compose file template. Includes the ES6, ES7, and ES8 versions. No modifications required.
- `es.env`: Configuration file. You need to configure the ES port numbers.
- `scripts/`: Stores initialization scripts that run after the image starts.
#### 1.5 Oracle
Provides an Oracle 11 image, stored under `docker/thirdparties/docker-compose/oracle/`:
- `oracle-11.yaml.tpl`: Docker compose file template. No modifications required.
- `oracle-11.env`: Configures the Oracle exposed port, which defaults to 1521.
#### 1.6 SQLServer
Provides a SQLServer 2022 image, stored under `docker/thirdparties/docker-compose/sqlserver/`:
- `sqlserver.yaml.tpl`: Docker compose file template. No modifications required.
- `sqlserver.env`: Configures the SQLServer exposed port, which defaults to 1433.
#### 1.7 ClickHouse
Provides a ClickHouse 22 image, stored under `docker/thirdparties/docker-compose/clickhouse/`:
- `clickhouse.yaml.tpl`: Docker compose file template. No modifications required.
- `clickhouse.env`: Configures the ClickHouse exposed port, which defaults to 8123.
#### 1.8 Iceberg
Provides an Iceberg + Spark + Minio image combination, stored under `docker/thirdparties/docker-compose/iceberg/`:
- `iceberg.yaml.tpl`: Docker compose file template. No modifications required.
- `entrypoint.sh.tpl`: Initialization script template that runs after the image starts. No modifications required.
- `spark-defaults.conf.tpl`: Spark configuration file template. No modifications required.
- `iceberg.env`: Exposed port configuration file. Modify the ports as needed to avoid conflicts.
After startup, you can start spark-sql with the following command:
```bash
docker exec -it doris-xx-spark-iceberg spark-sql
```
Here, `doris-xx-spark-iceberg` is the container name.
Example spark-sql Iceberg operations:
```sql
create database db1;
show databases;
create table db1.test1(k1 bigint, k2 bigint, k3 string) partitioned by (k1);
insert into db1.test1 values(1,2,'abc');
select * from db1.test1;
quit;
```
You can also access it through spark-shell:
```scala
docker exec -it doris-xx-spark-iceberg spark-shell
spark.sql(s"create database db1")
spark.sql(s"show databases").show()
spark.sql(s"create table db1.test1(k1 bigint, k2 bigint, k3 string) partitioned by (k1)").show()
spark.sql(s"show tables from db1").show()
spark.sql(s"insert into db1.test1 values(1,2,'abc')").show()
spark.sql(s"select * from db1.test1").show()
:q
```
For more usage details, see the [Tabular official documentation](https://tabular.io/blog/docker-spark-and-iceberg/).
#### 1.9 Hudi
The Hudi-related Docker compose files are stored under `docker/thirdparties/docker-compose/hudi`:
- `hudi.yaml.tpl`: Docker compose file template. No modifications required.
- `hadoop.env`: Configuration file template. No modifications required.
- The `scripts/` directory is mounted into the container after the container starts. The file contents do not need to be modified. Before starting the container, however, you must first download the prebuilt files:
Download `https://doris-build-hk-1308700295.cos.ap-hongkong.myqcloud.com/regression/load/hudi/hudi_docker_compose_attached_file.zip` into the `scripts/` directory and extract it.
Before starting, you can add the following configuration to `/etc/hosts` to avoid `UnknownHostException` errors:
```text
127.0.0.1 adhoc-1
127.0.0.1 adhoc-2
127.0.0.1 namenode
127.0.0.1 datanode1
127.0.0.1 hiveserver
127.0.0.1 hivemetastore
127.0.0.1 sparkmaster
```
After startup, you can start a hive query with the following command:
```bash
docker exec -it adhoc-2 /bin/bash
beeline -u jdbc:hive2://hiveserver:10000 \
--hiveconf hive.input.format=org.apache.hadoop.hive.ql.io.HiveInputFormat \
--hiveconf hive.stats.autogather=false
show tables;
show partitions stock_ticks_mor_rt;
select symbol, max(ts) from stock_ticks_cow group by symbol HAVING symbol = 'GOOG';
select symbol, max(ts) from stock_ticks_mor_ro group by symbol HAVING symbol = 'GOOG';
exit;
```
You can also access it through spark-shell:
```bash
docker exec -it adhoc-1 /bin/bash
$SPARK_INSTALL/bin/spark-shell \
--jars /var/scripts/hudi_docker_compose_attached_file/jar/hoodie-hive-sync-bundle.jar \
--master local[2] \
--driver-class-path $HADOOP_CONF_DIR \
--conf spark.sql.hive.convertMetastoreParquet=false \
--deploy-mode client \
--driver-memory 1G \
--executor-memory 3G \
--num-executors 1
spark.sql("show tables").show(100, false)
spark.sql("select symbol, max(ts) from stock_ticks_cow group by symbol HAVING symbol = 'GOOG'").show(100, false)
spark.sql("select `_hoodie_commit_time`, symbol, ts, volume, open, close from stock_ticks_cow where symbol = 'GOOG'").show(100, false)
spark.sql("select symbol, max(ts) from stock_ticks_mor_ro group by symbol HAVING symbol = 'GOOG'").show(100, false)
spark.sql("select symbol, max(ts) from stock_ticks_mor_rt group by symbol HAVING symbol = 'GOOG'").show(100, false)
spark.sql("select `_hoodie_commit_time`, symbol, ts, volume, open, close from stock_ticks_mor_ro where symbol = 'GOOG'").show(100, false)
:q
```
For more usage details, see the [Hudi official documentation](https://hudi.apache.org/docs/docker_demo).
#### 1.10 Trino
The Trino-related Docker compose files are stored under `docker/thirdparties/docker-compose/trino`. Template files:
- `gen_env.sh.tpl`: Generates HDFS-related port numbers. No modifications required. If port conflicts occur, the port numbers can be modified.
- `hive.properties.tpl`: Configures trino catalog information. No modifications required.
- `trino_hive.env.tpl`: Environment configuration for Hive. No modifications required.
- `trino_hive.yaml.tpl`: Docker compose file. No modifications required.
After starting the Trino docker, a Trino + hive catalog environment is configured. At this point Trino has two catalogs:
1. `hive`
2. `tpch` (bundled with the trino docker)
For more usage details, see the [Trino official documentation](https://trino.io/docs/current/installation/containers.html).
### 2. Run Regression Tests
External-table-related regression tests are disabled by default. You can enable them by editing the configuration in `regression-test/conf/regression-conf.groovy`. Example configuration items:
| Configuration item | Description |
|--------|------|
| `enableJdbcTest` | Enables the JDBC external table test. Requires starting the MySQL and PostgreSQL containers. |
| `mysql_57_port` | The MySQL exposed port, which defaults to 3316. |
| `pg_14_port` | The PostgreSQL exposed port, which defaults to 5442. |
| `enableHiveTest` | Enables the Hive external table test. Requires starting the Hive container. |
| `hive2HmsPort` | The Hive2 metastore exposed port, which defaults to 9083. |
| `hive2HdfsPort` | The Hive2 HDFS namenode exposed port, which defaults to 8020. |
| `enableEsTest` | Enables the ES external table test. Requires starting the ES container. |
| `es_6_port` | The ES6 port |
| `es_7_port` | The ES7 port |
| `es_8_port` | The ES8 port |
---
## FAQ
<!-- Knowledge type: Troubleshooting -->
**Q: `UnknownHostException` is reported when starting the hudi container.**
A: Before startup, the mappings `adhoc-1` / `adhoc-2` / `namenode` / `datanode1` / `hiveserver` / `hivemetastore` / `sparkmaster` to `127.0.0.1` were not configured in `/etc/hosts`. Refer to [1.9 Hudi](#19-hudi) to add them.
**Q: Network or container name conflicts between multiple container environments.**
A: Edit `CONTAINER_UID` in `docker/thirdparties/custom_settings.env` to a unique prefix (such as `doris-10002-18sda1-`). All container names and networks are regenerated based on this prefix.
**Q: Connection failure errors are reported when running external table tests.**
A: Confirm that the corresponding `enableXxxTest` in `regression-conf.groovy` is `true`, that the configured ports match the actual ports exposed by docker compose after startup, and that the container status is normal (`docker ps -a`).
**Q: How do I see the actual query result of a `qt` Action when the `.out` file does not yet exist?**
A: Run with the `-genOut` (does not overwrite existing files) or `-forceGenOut` (force overwrite) parameter, for example `./run-regression-test.sh --run qt_action -genOut`.
**Q: How do I prevent a SQL failure in a case from aborting the entire test suite?**
A: Use `try_sql(...)` instead of `sql(...)`. It returns `null` on failure instead of throwing an exception.