Compare commits

..

2 Commits

Author SHA1 Message Date
Lei, HUANG
2ed98ff558 fix: some cr comments 2024-02-20 14:10:57 +08:00
Lei, HUANG
b46386d52a feat: data buffer and related structs 2024-02-19 22:57:25 +08:00
502 changed files with 5447 additions and 25677 deletions

View File

@@ -3,3 +3,13 @@ linker = "aarch64-linux-gnu-gcc"
[alias] [alias]
sqlness = "run --bin sqlness-runner --" sqlness = "run --bin sqlness-runner --"
[build]
rustflags = [
# lints
# TODO: use lint configuration in cargo https://github.com/rust-lang/cargo/issues/5034
"-Wclippy::print_stdout",
"-Wclippy::print_stderr",
"-Wclippy::implicit_clone",
]

View File

@@ -1,10 +0,0 @@
root = true
[*]
end_of_line = lf
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[{Makefile,**.mk}]
indent_style = tab

View File

@@ -21,6 +21,3 @@ GT_GCS_CREDENTIAL_PATH = GCS credential path
GT_GCS_ENDPOINT = GCS end point GT_GCS_ENDPOINT = GCS end point
# Settings for kafka wal test # Settings for kafka wal test
GT_KAFKA_ENDPOINTS = localhost:9092 GT_KAFKA_ENDPOINTS = localhost:9092
# Setting for fuzz tests
GT_MYSQL_ADDR = localhost:4002

View File

@@ -34,7 +34,7 @@ runs:
- name: Upload sqlness logs - name: Upload sqlness logs
if: ${{ failure() && inputs.disable-run-tests == 'false' }} # Only upload logs when the integration tests failed. if: ${{ failure() && inputs.disable-run-tests == 'false' }} # Only upload logs when the integration tests failed.
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: sqlness-logs name: sqlness-logs
path: /tmp/greptime-*.log path: /tmp/greptime-*.log

View File

@@ -67,7 +67,7 @@ runs:
- name: Upload sqlness logs - name: Upload sqlness logs
if: ${{ failure() }} # Only upload logs when the integration tests failed. if: ${{ failure() }} # Only upload logs when the integration tests failed.
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: sqlness-logs name: sqlness-logs
path: /tmp/greptime-*.log path: /tmp/greptime-*.log

View File

@@ -62,10 +62,10 @@ runs:
- name: Upload sqlness logs - name: Upload sqlness logs
if: ${{ failure() }} # Only upload logs when the integration tests failed. if: ${{ failure() }} # Only upload logs when the integration tests failed.
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: sqlness-logs name: sqlness-logs
path: /tmp/greptime-*.log path: ${{ runner.temp }}/greptime-*.log
retention-days: 3 retention-days: 3
- name: Build greptime binary - name: Build greptime binary

View File

@@ -1,13 +0,0 @@
name: Fuzz Test
description: 'Fuzz test given setup and service'
inputs:
target:
description: "The fuzz target to test"
runs:
using: composite
steps:
- name: Run Fuzz Test
shell: bash
run: cargo fuzz run ${{ inputs.target }} --fuzz-dir tests-fuzz -D -s none -- -max_total_time=120
env:
GT_MYSQL_ADDR: 127.0.0.1:4002

View File

@@ -1,10 +1,8 @@
I hereby agree to the terms of the [GreptimeDB CLA](https://github.com/GreptimeTeam/.github/blob/main/CLA.md). I hereby agree to the terms of the [GreptimeDB CLA](https://gist.github.com/xtang/6378857777706e568c1949c7578592cc)
## Refer to a related PR or issue link (optional)
## What's changed and what's your intention? ## What's changed and what's your intention?
__!!! DO NOT LEAVE THIS BLOCK EMPTY !!!__ _PLEASE DO NOT LEAVE THIS EMPTY !!!_
Please explain IN DETAIL what the changes are in this PR and why they are needed: Please explain IN DETAIL what the changes are in this PR and why they are needed:
@@ -18,3 +16,5 @@ Please explain IN DETAIL what the changes are in this PR and why they are needed
- [ ] I have written the necessary rustdoc comments. - [ ] I have written the necessary rustdoc comments.
- [ ] I have added the necessary unit tests and integration tests. - [ ] I have added the necessary unit tests and integration tests.
- [x] This PR does not require documentation updates. - [x] This PR does not require documentation updates.
## Refer to a related PR or issue link (optional)

View File

@@ -1,7 +1,7 @@
on: on:
merge_group: merge_group:
pull_request: pull_request:
types: [ opened, synchronize, reopened, ready_for_review ] types: [opened, synchronize, reopened, ready_for_review]
paths-ignore: paths-ignore:
- 'docs/**' - 'docs/**'
- 'config/**' - 'config/**'
@@ -57,7 +57,7 @@ jobs:
toolchain: ${{ env.RUST_TOOLCHAIN }} toolchain: ${{ env.RUST_TOOLCHAIN }}
- name: Rust Cache - name: Rust Cache
uses: Swatinem/rust-cache@v2 uses: Swatinem/rust-cache@v2
with: with:
# Shares across multiple jobs # Shares across multiple jobs
# Shares with `Clippy` job # Shares with `Clippy` job
shared-key: "check-lint" shared-key: "check-lint"
@@ -75,7 +75,7 @@ jobs:
toolchain: stable toolchain: stable
- name: Rust Cache - name: Rust Cache
uses: Swatinem/rust-cache@v2 uses: Swatinem/rust-cache@v2
with: with:
# Shares across multiple jobs # Shares across multiple jobs
shared-key: "check-toml" shared-key: "check-toml"
- name: Install taplo - name: Install taplo
@@ -102,7 +102,7 @@ jobs:
shared-key: "build-binaries" shared-key: "build-binaries"
- name: Build greptime binaries - name: Build greptime binaries
shell: bash shell: bash
run: cargo build --bin greptime --bin sqlness-runner run: cargo build
- name: Pack greptime binaries - name: Pack greptime binaries
shell: bash shell: bash
run: | run: |
@@ -117,46 +117,6 @@ jobs:
artifacts-dir: bins artifacts-dir: bins
version: current version: current
fuzztest:
name: Fuzz Test
needs: build
runs-on: ubuntu-latest
strategy:
matrix:
target: [ "fuzz_create_table", "fuzz_alter_table" ]
steps:
- uses: actions/checkout@v4
- uses: arduino/setup-protoc@v3
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.RUST_TOOLCHAIN }}
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
# Shares across multiple jobs
shared-key: "fuzz-test-targets"
- name: Set Rust Fuzz
shell: bash
run: |
sudo apt update && sudo apt install -y libfuzzer-14-dev
cargo install cargo-fuzz
- name: Download pre-built binaries
uses: actions/download-artifact@v4
with:
name: bins
path: .
- name: Unzip binaries
run: tar -xvf ./bins.tar.gz
- name: Run GreptimeDB
run: |
./bins/greptime standalone start&
- name: Fuzz Test
uses: ./.github/actions/fuzz-test
env:
CUSTOM_LIBFUZZER_PATH: /usr/lib/llvm-14/lib/libFuzzer.a
with:
target: ${{ matrix.target }}
sqlness: sqlness:
name: Sqlness Test name: Sqlness Test
needs: build needs: build
@@ -176,12 +136,13 @@ jobs:
run: tar -xvf ./bins.tar.gz run: tar -xvf ./bins.tar.gz
- name: Run sqlness - name: Run sqlness
run: RUST_BACKTRACE=1 ./bins/sqlness-runner -c ./tests/cases --bins-dir ./bins run: RUST_BACKTRACE=1 ./bins/sqlness-runner -c ./tests/cases --bins-dir ./bins
# FIXME: Logs cannot found be on failure (or even success). Need to figure out the cause.
- name: Upload sqlness logs - name: Upload sqlness logs
if: always() if: always()
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: sqlness-logs name: sqlness-logs
path: /tmp/greptime-*.log path: ${{ runner.temp }}/greptime-*.log
retention-days: 3 retention-days: 3
sqlness-kafka-wal: sqlness-kafka-wal:
@@ -206,12 +167,13 @@ jobs:
run: docker compose -f docker-compose-standalone.yml up -d --wait run: docker compose -f docker-compose-standalone.yml up -d --wait
- name: Run sqlness - name: Run sqlness
run: RUST_BACKTRACE=1 ./bins/sqlness-runner -w kafka -k 127.0.0.1:9092 -c ./tests/cases --bins-dir ./bins run: RUST_BACKTRACE=1 ./bins/sqlness-runner -w kafka -k 127.0.0.1:9092 -c ./tests/cases --bins-dir ./bins
# FIXME: Logs cannot be found on failure (or even success). Need to figure out the cause.
- name: Upload sqlness logs - name: Upload sqlness logs
if: always() if: always()
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: sqlness-logs-with-kafka-wal name: sqlness-logs
path: /tmp/greptime-*.log path: ${{ runner.temp }}/greptime-*.log
retention-days: 3 retention-days: 3
fmt: fmt:
@@ -229,7 +191,7 @@ jobs:
components: rustfmt components: rustfmt
- name: Rust Cache - name: Rust Cache
uses: Swatinem/rust-cache@v2 uses: Swatinem/rust-cache@v2
with: with:
# Shares across multiple jobs # Shares across multiple jobs
shared-key: "check-rust-fmt" shared-key: "check-rust-fmt"
- name: Run cargo fmt - name: Run cargo fmt
@@ -250,7 +212,7 @@ jobs:
components: clippy components: clippy
- name: Rust Cache - name: Rust Cache
uses: Swatinem/rust-cache@v2 uses: Swatinem/rust-cache@v2
with: with:
# Shares across multiple jobs # Shares across multiple jobs
# Shares with `Check` job # Shares with `Check` job
shared-key: "check-lint" shared-key: "check-lint"
@@ -309,28 +271,10 @@ jobs:
GT_KAFKA_ENDPOINTS: 127.0.0.1:9092 GT_KAFKA_ENDPOINTS: 127.0.0.1:9092
UNITTEST_LOG_DIR: "__unittest_logs" UNITTEST_LOG_DIR: "__unittest_logs"
- name: Codecov upload - name: Codecov upload
uses: codecov/codecov-action@v4 uses: codecov/codecov-action@v2
with: with:
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}
files: ./lcov.info files: ./lcov.info
flags: rust flags: rust
fail_ci_if_error: false fail_ci_if_error: false
verbose: true verbose: true
compat:
name: Compatibility Test
needs: build
runs-on: ubuntu-20.04
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
- name: Download pre-built binaries
uses: actions/download-artifact@v4
with:
name: bins
path: .
- name: Unzip binaries
run: |
mkdir -p ./bins/current
tar -xvf ./bins.tar.gz --strip-components=1 -C ./bins/current
- run: ./tests/compat/test-compat.sh 0.6.0

View File

@@ -61,18 +61,6 @@ jobs:
sqlness: sqlness:
name: Sqlness Test name: Sqlness Test
runs-on: ${{ matrix.os }} runs-on: ubuntu-20.04
strategy:
matrix:
os: [ ubuntu-20.04 ]
steps:
- run: 'echo "No action required"'
sqlness-kafka-wal:
name: Sqlness Test with Kafka Wal
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ ubuntu-20.04 ]
steps: steps:
- run: 'echo "No action required"' - run: 'echo "No action required"'

View File

@@ -45,10 +45,10 @@ jobs:
{"text": "Nightly CI failed for sqlness tests"} {"text": "Nightly CI failed for sqlness tests"}
- name: Upload sqlness logs - name: Upload sqlness logs
if: always() if: always()
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: sqlness-logs name: sqlness-logs
path: /tmp/greptime-*.log path: ${{ runner.temp }}/greptime-*.log
retention-days: 3 retention-days: 3
test-on-windows: test-on-windows:

View File

@@ -91,7 +91,7 @@ env:
# The scheduled version is '${{ env.NEXT_RELEASE_VERSION }}-nightly-YYYYMMDD', like v0.2.0-nigthly-20230313; # The scheduled version is '${{ env.NEXT_RELEASE_VERSION }}-nightly-YYYYMMDD', like v0.2.0-nigthly-20230313;
NIGHTLY_RELEASE_PREFIX: nightly NIGHTLY_RELEASE_PREFIX: nightly
# Note: The NEXT_RELEASE_VERSION should be modified manually by every formal release. # Note: The NEXT_RELEASE_VERSION should be modified manually by every formal release.
NEXT_RELEASE_VERSION: v0.8.0 NEXT_RELEASE_VERSION: v0.7.0
jobs: jobs:
allocate-runners: allocate-runners:
@@ -221,8 +221,6 @@ jobs:
arch: x86_64-apple-darwin arch: x86_64-apple-darwin
artifacts-dir-prefix: greptime-darwin-amd64-pyo3 artifacts-dir-prefix: greptime-darwin-amd64-pyo3
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
outputs:
build-macos-result: ${{ steps.set-build-macos-result.outputs.build-macos-result }}
needs: [ needs: [
allocate-runners, allocate-runners,
] ]
@@ -262,8 +260,6 @@ jobs:
features: pyo3_backend,servers/dashboard features: pyo3_backend,servers/dashboard
artifacts-dir-prefix: greptime-windows-amd64-pyo3 artifacts-dir-prefix: greptime-windows-amd64-pyo3
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
outputs:
build-windows-result: ${{ steps.set-build-windows-result.outputs.build-windows-result }}
needs: [ needs: [
allocate-runners, allocate-runners,
] ]
@@ -288,7 +284,7 @@ jobs:
- name: Set build windows result - name: Set build windows result
id: set-build-windows-result id: set-build-windows-result
run: | run: |
echo "build-windows-result=success" >> $Env:GITHUB_OUTPUT echo "build-windows-result=success" >> $GITHUB_OUTPUT
release-images-to-dockerhub: release-images-to-dockerhub:
name: Build and push images to DockerHub name: Build and push images to DockerHub
@@ -299,8 +295,6 @@ jobs:
build-linux-arm64-artifacts, build-linux-arm64-artifacts,
] ]
runs-on: ubuntu-2004-16-cores runs-on: ubuntu-2004-16-cores
outputs:
build-image-result: ${{ steps.set-build-image-result.outputs.build-image-result }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with: with:
@@ -316,7 +310,7 @@ jobs:
version: ${{ needs.allocate-runners.outputs.version }} version: ${{ needs.allocate-runners.outputs.version }}
- name: Set build image result - name: Set build image result
id: set-build-image-result id: set-image-build-result
run: | run: |
echo "build-image-result=success" >> $GITHUB_OUTPUT echo "build-image-result=success" >> $GITHUB_OUTPUT

4
.gitignore vendored
View File

@@ -46,7 +46,3 @@ benchmarks/data
*.code-workspace *.code-workspace
venv/ venv/
# Fuzz tests
tests-fuzz/artifacts/
tests-fuzz/corpus/

520
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -18,7 +18,6 @@ members = [
"src/common/grpc-expr", "src/common/grpc-expr",
"src/common/mem-prof", "src/common/mem-prof",
"src/common/meta", "src/common/meta",
"src/common/plugins",
"src/common/procedure", "src/common/procedure",
"src/common/procedure-test", "src/common/procedure-test",
"src/common/query", "src/common/query",
@@ -62,23 +61,17 @@ members = [
resolver = "2" resolver = "2"
[workspace.package] [workspace.package]
version = "0.7.0" version = "0.6.0"
edition = "2021" edition = "2021"
license = "Apache-2.0" license = "Apache-2.0"
[workspace.lints]
clippy.print_stdout = "warn"
clippy.print_stderr = "warn"
clippy.implicit_clone = "warn"
rust.unknown_lints = "deny"
[workspace.dependencies] [workspace.dependencies]
ahash = { version = "0.8", features = ["compile-time-rng"] } ahash = { version = "0.8", features = ["compile-time-rng"] }
aquamarine = "0.3" aquamarine = "0.3"
arrow = { version = "47.0" } arrow = { version = "47.0" }
arrow-array = "47.0" arrow-array = "47.0"
arrow-flight = "47.0" arrow-flight = "47.0"
arrow-ipc = { version = "47.0", features = ["lz4"] } arrow-ipc = "47.0"
arrow-schema = { version = "47.0", features = ["serde"] } arrow-schema = { version = "47.0", features = ["serde"] }
async-stream = "0.3" async-stream = "0.3"
async-trait = "0.1" async-trait = "0.1"
@@ -107,7 +100,7 @@ greptime-proto = { git = "https://github.com/GreptimeTeam/greptime-proto.git", r
humantime-serde = "1.1" humantime-serde = "1.1"
itertools = "0.10" itertools = "0.10"
lazy_static = "1.4" lazy_static = "1.4"
meter-core = { git = "https://github.com/GreptimeTeam/greptime-meter.git", rev = "80b72716dcde47ec4161478416a5c6c21343364d" } meter-core = { git = "https://github.com/GreptimeTeam/greptime-meter.git", rev = "abbd357c1e193cd270ea65ee7652334a150b628f" }
mockall = "0.11.4" mockall = "0.11.4"
moka = "0.12" moka = "0.12"
num_cpus = "1.16" num_cpus = "1.16"
@@ -134,7 +127,7 @@ reqwest = { version = "0.11", default-features = false, features = [
rskafka = "0.5" rskafka = "0.5"
rust_decimal = "1.33" rust_decimal = "1.33"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", features = ["float_roundtrip"] } serde_json = "1.0"
serde_with = "3" serde_with = "3"
smallvec = { version = "1", features = ["serde"] } smallvec = { version = "1", features = ["serde"] }
snafu = "0.7" snafu = "0.7"
@@ -171,7 +164,6 @@ common-grpc-expr = { path = "src/common/grpc-expr" }
common-macro = { path = "src/common/macro" } common-macro = { path = "src/common/macro" }
common-mem-prof = { path = "src/common/mem-prof" } common-mem-prof = { path = "src/common/mem-prof" }
common-meta = { path = "src/common/meta" } common-meta = { path = "src/common/meta" }
common-plugins = { path = "src/common/plugins" }
common-procedure = { path = "src/common/procedure" } common-procedure = { path = "src/common/procedure" }
common-procedure-test = { path = "src/common/procedure-test" } common-procedure-test = { path = "src/common/procedure-test" }
common-query = { path = "src/common/query" } common-query = { path = "src/common/query" }
@@ -209,7 +201,7 @@ table = { path = "src/table" }
[workspace.dependencies.meter-macros] [workspace.dependencies.meter-macros]
git = "https://github.com/GreptimeTeam/greptime-meter.git" git = "https://github.com/GreptimeTeam/greptime-meter.git"
rev = "80b72716dcde47ec4161478416a5c6c21343364d" rev = "abbd357c1e193cd270ea65ee7652334a150b628f"
[profile.release] [profile.release]
debug = 1 debug = 1

View File

@@ -3,7 +3,6 @@ CARGO_PROFILE ?=
FEATURES ?= FEATURES ?=
TARGET_DIR ?= TARGET_DIR ?=
TARGET ?= TARGET ?=
BUILD_BIN ?= greptime
CARGO_BUILD_OPTS := --locked CARGO_BUILD_OPTS := --locked
IMAGE_REGISTRY ?= docker.io IMAGE_REGISTRY ?= docker.io
IMAGE_NAMESPACE ?= greptime IMAGE_NAMESPACE ?= greptime
@@ -46,10 +45,6 @@ ifneq ($(strip $(TARGET)),)
CARGO_BUILD_OPTS += --target ${TARGET} CARGO_BUILD_OPTS += --target ${TARGET}
endif endif
ifneq ($(strip $(BUILD_BIN)),)
CARGO_BUILD_OPTS += --bin ${BUILD_BIN}
endif
ifneq ($(strip $(RELEASE)),) ifneq ($(strip $(RELEASE)),)
CARGO_BUILD_OPTS += --release CARGO_BUILD_OPTS += --release
endif endif

View File

@@ -4,9 +4,6 @@ version.workspace = true
edition.workspace = true edition.workspace = true
license.workspace = true license.workspace = true
[lints]
workspace = true
[dependencies] [dependencies]
arrow.workspace = true arrow.workspace = true
chrono.workspace = true chrono.workspace = true

View File

@@ -29,7 +29,7 @@ use client::api::v1::column::Values;
use client::api::v1::{ use client::api::v1::{
Column, ColumnDataType, ColumnDef, CreateTableExpr, InsertRequest, InsertRequests, SemanticType, Column, ColumnDataType, ColumnDef, CreateTableExpr, InsertRequest, InsertRequests, SemanticType,
}; };
use client::{Client, Database, OutputData, DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME}; use client::{Client, Database, Output, DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME};
use futures_util::TryStreamExt; use futures_util::TryStreamExt;
use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
use parquet::arrow::arrow_reader::ParquetRecordBatchReaderBuilder; use parquet::arrow::arrow_reader::ParquetRecordBatchReaderBuilder;
@@ -502,9 +502,9 @@ async fn do_query(num_iter: usize, db: &Database, table_name: &str) {
for i in 0..num_iter { for i in 0..num_iter {
let now = Instant::now(); let now = Instant::now();
let res = db.sql(&query).await.unwrap(); let res = db.sql(&query).await.unwrap();
match res.data { match res {
OutputData::AffectedRows(_) | OutputData::RecordBatches(_) => (), Output::AffectedRows(_) | Output::RecordBatches(_) => (),
OutputData::Stream(stream) => { Output::Stream(stream) => {
stream.try_collect::<Vec<_>>().await.unwrap(); stream.try_collect::<Vec<_>>().await.unwrap();
} }
} }

View File

@@ -8,6 +8,5 @@ coverage:
ignore: ignore:
- "**/error*.rs" # ignore all error.rs files - "**/error*.rs" # ignore all error.rs files
- "tests/runner/*.rs" # ignore integration test runner - "tests/runner/*.rs" # ignore integration test runner
- "tests-integration/**/*.rs" # ignore integration tests
comment: # this is a top-level key comment: # this is a top-level key
layout: "diff" layout: "diff"

View File

@@ -134,22 +134,10 @@ create_on_compaction = "auto"
apply_on_query = "auto" apply_on_query = "auto"
# Memory threshold for performing an external sort during index creation. # Memory threshold for performing an external sort during index creation.
# Setting to empty will disable external sorting, forcing all sorting operations to happen in memory. # Setting to empty will disable external sorting, forcing all sorting operations to happen in memory.
mem_threshold_on_create = "64M" mem_threshold_on_create = "64MB"
# File system path to store intermediate files for external sorting (default `{data_home}/index_intermediate`). # File system path to store intermediate files for external sorting (default `{data_home}/index_intermediate`).
intermediate_path = "" intermediate_path = ""
[region_engine.mito.memtable]
# Memtable type.
# - "experimental": experimental memtable
# - "time_series": time-series memtable (deprecated)
type = "experimental"
# The max number of keys in one shard.
index_max_keys_per_shard = 8192
# The max rows of data inside the actively writing buffer in one shard.
data_freeze_threshold = 32768
# Max dictionary bytes.
fork_dictionary_bytes = "1GiB"
# Log options, see `standalone.example.toml` # Log options, see `standalone.example.toml`
# [logging] # [logging]
# dir = "/tmp/greptimedb/logs" # dir = "/tmp/greptimedb/logs"

View File

@@ -31,7 +31,6 @@ runtime_size = 2
mode = "disable" mode = "disable"
cert_path = "" cert_path = ""
key_path = "" key_path = ""
watch = false
# PostgresSQL server options, see `standalone.example.toml`. # PostgresSQL server options, see `standalone.example.toml`.
[postgres] [postgres]
@@ -44,7 +43,6 @@ runtime_size = 2
mode = "disable" mode = "disable"
cert_path = "" cert_path = ""
key_path = "" key_path = ""
watch = false
# OpenTSDB protocol options, see `standalone.example.toml`. # OpenTSDB protocol options, see `standalone.example.toml`.
[opentsdb] [opentsdb]

View File

@@ -44,8 +44,6 @@ mode = "disable"
cert_path = "" cert_path = ""
# Private key file path. # Private key file path.
key_path = "" key_path = ""
# Watch for Certificate and key file change and auto reload
watch = false
# PostgresSQL server options. # PostgresSQL server options.
[postgres] [postgres]
@@ -64,8 +62,6 @@ mode = "disable"
cert_path = "" cert_path = ""
# private key file path. # private key file path.
key_path = "" key_path = ""
# Watch for Certificate and key file change and auto reload
watch = false
# OpenTSDB protocol options. # OpenTSDB protocol options.
[opentsdb] [opentsdb]
@@ -122,7 +118,7 @@ sync_period = "1000ms"
# Number of topics to be created upon start. # Number of topics to be created upon start.
# num_topics = 64 # num_topics = 64
# Topic selector type. # Topic selector type.
# Available selector types: # Available selector types:
# - "round_robin" (default) # - "round_robin" (default)
# selector_type = "round_robin" # selector_type = "round_robin"
# The prefix of topic name. # The prefix of topic name.
@@ -244,18 +240,6 @@ mem_threshold_on_create = "64M"
# File system path to store intermediate files for external sorting (default `{data_home}/index_intermediate`). # File system path to store intermediate files for external sorting (default `{data_home}/index_intermediate`).
intermediate_path = "" intermediate_path = ""
[region_engine.mito.memtable]
# Memtable type.
# - "experimental": experimental memtable
# - "time_series": time-series memtable (deprecated)
type = "experimental"
# The max number of keys in one shard.
index_max_keys_per_shard = 8192
# The max rows of data inside the actively writing buffer in one shard.
data_freeze_threshold = 32768
# Max dictionary bytes.
fork_dictionary_bytes = "1GiB"
# Log options # Log options
# [logging] # [logging]
# Specify logs directory. # Specify logs directory.
@@ -266,11 +250,10 @@ fork_dictionary_bytes = "1GiB"
# enable_otlp_tracing = false # enable_otlp_tracing = false
# tracing exporter endpoint with format `ip:port`, we use grpc oltp as exporter, default endpoint is `localhost:4317` # tracing exporter endpoint with format `ip:port`, we use grpc oltp as exporter, default endpoint is `localhost:4317`
# otlp_endpoint = "localhost:4317" # otlp_endpoint = "localhost:4317"
# The percentage of tracing will be sampled and exported. Valid range `[0, 1]`, 1 means all traces are sampled, 0 means all traces are not sampled, the default value is 1. ratio > 1 are treated as 1. Fractions < 0 are treated as 0
# tracing_sample_ratio = 1.0
# Whether to append logs to stdout. Defaults to true. # Whether to append logs to stdout. Defaults to true.
# append_stdout = true # append_stdout = true
# The percentage of tracing will be sampled and exported. Valid range `[0, 1]`, 1 means all traces are sampled, 0 means all traces are not sampled, the default value is 1. ratio > 1 are treated as 1. Fractions < 0 are treated as 0
# [logging.tracing_sample_ratio]
# default_ratio = 0.0
# Standalone export the metrics generated by itself # Standalone export the metrics generated by itself
# encoded to Prometheus remote-write format # encoded to Prometheus remote-write format

View File

@@ -79,7 +79,7 @@ This RFC proposes to add a new expression node `MergeScan` to merge result from
│ │ │ │ │ │ │ │
└─Frontend──────┘ └─Remote-Sources──────────────┘ └─Frontend──────┘ └─Remote-Sources──────────────┘
``` ```
This merge operation simply chains all the underlying remote data sources and return `RecordBatch`, just like a coalesce op. And each remote sources is a gRPC query to datanode via the substrait logical plan interface. The plan is transformed and divided from the original query that comes to frontend. This merge operation simply chains all the the underlying remote data sources and return `RecordBatch`, just like a coalesce op. And each remote sources is a gRPC query to datanode via the substrait logical plan interface. The plan is transformed and divided from the original query that comes to frontend.
## Commutativity of MergeScan ## Commutativity of MergeScan

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

View File

@@ -1,101 +0,0 @@
---
Feature Name: Multi-dimension Partition Rule
Tracking Issue: https://github.com/GreptimeTeam/greptimedb/issues/3351
Date: 2024-02-21
Author: "Ruihang Xia <waynestxia@gmail.com>"
---
# Summary
A new region partition scheme that runs on multiple dimensions of the key space. The partition rule is defined by a set of simple expressions on the partition key columns.
# Motivation
The current partition rule is from MySQL's [`RANGE Partition`](https://dev.mysql.com/doc/refman/8.0/en/partitioning-range.html), which is based on a single dimension. It is sort of a [Hilbert Curve](https://en.wikipedia.org/wiki/Hilbert_curve) and pick several point on the curve to divide the space. It is neither easy to understand how the data get partitioned nor flexible enough to handle complex partitioning requirements.
Considering the future requirements like region repartitioning or autonomous rebalancing, where both workload and partition may change frequently. Here proposes a new region partition scheme that uses a set of simple expressions on the partition key columns to divide the key space.
# Details
## Partition rule
First, we define a simple expression that can be used to define the partition rule. The simple expression is a binary expression expression on the partition key columns that can be evaluated to a boolean value. The binary operator is limited to comparison operators only, like `=`, `!=`, `>`, `>=`, `<`, `<=`. And the operands are limited either literal value or partition column.
Example of valid simple expressions are $`col_A = 10`$, $`col_A \gt 10 \& col_B \gt 20`$ or $`col_A \ne 10`$.
Those expressions can be used as predicates to divide the key space into different regions. The following example have two partition columns `Col A` and `Col B`, and four partitioned regions.
```math
\left\{\begin{aligned}
&col_A \le 10 &Region_1 \\
&10 \lt col_A \& col_A \le 20 &Region_2 \\
&20 \lt col_A \space \& \space col_B \lt 100 &Region_3 \\
&20 \lt col_A \space \& \space col_B \ge 100 &Region_4
\end{aligned}\right\}
```
An advantage of this scheme is that it is easy to understand how the data get partitioned. The above example can be visualized in a 2D space (two partition column is involved in the example).
![example](2d-example.png)
Here each expression draws a line in the 2D space. Managing data partitioning becomes a matter of drawing lines in the key space.
To make it easy to use, there is a "default region" which catches all the data that doesn't match any of previous expressions. The default region exist by default and do not need to specify. It is also possible to remove this default region if the DB finds it is not necessary.
## SQL interface
The SQL interface is in response to two parts: specifying the partition columns and the partition rule. Thouth we are targeting an autonomous system, it's still allowed to give some bootstrap rules or hints on creating table.
Partition column is specified by `PARTITION ON COLUMNS` sub-clause in `CREATE TABLE`:
```sql
CREATE TABLE t (...)
PARTITION ON COLUMNS (...) ();
```
Two following brackets are for partition columns and partition rule respectively.
Columns provided here are only used as an allow-list of how the partition rule can be defined. Which means (a) the sequence between columns doesn't matter, (b) the columns provided here are not necessarily being used in the partition rule.
The partition rule part is a list of comma-separated simple expressions. Expressions here are not corresponding to region, as they might be changed by system to fit various workload.
A full example of `CREATE TABLE` with partition rule is:
```sql
CREATE TABLE IF NOT EXISTS demo (
a STRING,
b STRING,
c STRING,
d STRING,
ts TIMESTAMP,
memory DOUBLE,
TIME INDEX (ts),
PRIMARY KEY (a, b, c, d)
)
PARTITION ON COLUMNS (c, b, a) (
a < 10,
10 >= a AND a < 20,
20 >= a AND b < 100,
20 >= a AND b > 100
)
```
## Combine with storage
Examining columns separately suits our columnar storage very well in two aspects.
1. The simple expression can be pushed down to storage and file format, and is likely to hit existing index. Makes pruning operation very efficient.
2. Columns in columnar storage are not tightly coupled like in the traditional row storages, which means we can easily add or remove columns from partition rule without much impact (like a global reshuffle) on data.
The data file itself can be "projected" to the key space as a polyhedron, it is guaranteed that each plane is in parallel with some coordinate planes (in a 2D scenario, this is saying that all the files can be projected to a rectangle). Thus partition or repartition also only need to consider related columns.
![sst-project](sst-project.png)
An additional limitation is that considering how the index works and how we organize the primary keys at present, the partition columns are limited to be a subset of primary keys for better performance.
# Drawbacks
This is a breaking change.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

View File

@@ -66,7 +66,7 @@
}, },
"editable": true, "editable": true,
"fiscalYearStartMonth": 0, "fiscalYearStartMonth": 0,
"graphTooltip": 1, "graphTooltip": 0,
"id": null, "id": null,
"links": [], "links": [],
"liveNow": false, "liveNow": false,
@@ -2116,7 +2116,7 @@
} }
] ]
}, },
"unit": "none" "unit": "bytes"
}, },
"overrides": [] "overrides": []
}, },
@@ -2126,7 +2126,7 @@
"x": 0, "x": 0,
"y": 61 "y": 61
}, },
"id": 17, "id": 12,
"interval": "1s", "interval": "1s",
"options": { "options": {
"legend": { "legend": {
@@ -2147,8 +2147,8 @@
"uid": "${DS_PROMETHEUS-1}" "uid": "${DS_PROMETHEUS-1}"
}, },
"disableTextWrap": false, "disableTextWrap": false,
"editorMode": "code", "editorMode": "builder",
"expr": "rate(raft_engine_sync_log_duration_seconds_count[2s])", "expr": "histogram_quantile(0.95, sum by(le) (rate(raft_engine_write_size_bucket[$__rate_interval])))",
"fullMetaSearch": false, "fullMetaSearch": false,
"includeNullMetadata": false, "includeNullMetadata": false,
"instant": false, "instant": false,
@@ -2158,7 +2158,7 @@
"useBackend": false "useBackend": false
} }
], ],
"title": "raft engine sync count", "title": "wal write size",
"type": "timeseries" "type": "timeseries"
}, },
{ {
@@ -2378,120 +2378,6 @@
], ],
"title": "raft engine write duration seconds", "title": "raft engine write duration seconds",
"type": "timeseries" "type": "timeseries"
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS-1}"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "bytes"
},
"overrides": []
},
"gridPos": {
"h": 7,
"w": 12,
"x": 12,
"y": 68
},
"id": 12,
"interval": "1s",
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS-1}"
},
"disableTextWrap": false,
"editorMode": "code",
"expr": "histogram_quantile(0.95, sum by(le) (rate(raft_engine_write_size_bucket[$__rate_interval])))",
"fullMetaSearch": false,
"includeNullMetadata": false,
"instant": false,
"legendFormat": "req-size-p95",
"range": true,
"refId": "A",
"useBackend": false
},
{
"datasource": {
"type": "prometheus",
"uid": "${DS_PROMETHEUS-1}"
},
"editorMode": "code",
"expr": "rate(raft_engine_write_size_sum[$__rate_interval])",
"hide": false,
"instant": false,
"legendFormat": "throughput",
"range": true,
"refId": "B"
}
],
"title": "wal write size",
"type": "timeseries"
} }
], ],
"refresh": "10s", "refresh": "10s",
@@ -2501,13 +2387,13 @@
"list": [] "list": []
}, },
"time": { "time": {
"from": "now-30m", "from": "now-3h",
"to": "now" "to": "now"
}, },
"timepicker": {}, "timepicker": {},
"timezone": "", "timezone": "",
"title": "GreptimeDB", "title": "GreptimeDB",
"uid": "e7097237-669b-4f8d-b751-13067afbfb68", "uid": "e7097237-669b-4f8d-b751-13067afbfb68",
"version": 12, "version": 9,
"weekStart": "" "weekStart": ""
} }

View File

@@ -1,7 +1,8 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# This script is used to download built dashboard assets from the "GreptimeTeam/dashboard" repository. # This script is used to download built dashboard assets from the "GreptimeTeam/dashboard" repository.
set -ex
set -e -x
declare -r SCRIPT_DIR=$(cd $(dirname ${0}) >/dev/null 2>&1 && pwd) declare -r SCRIPT_DIR=$(cd $(dirname ${0}) >/dev/null 2>&1 && pwd)
declare -r ROOT_DIR=$(dirname ${SCRIPT_DIR}) declare -r ROOT_DIR=$(dirname ${SCRIPT_DIR})
@@ -12,34 +13,13 @@ RELEASE_VERSION="$(cat $STATIC_DIR/VERSION | tr -d '\t\r\n ')"
echo "Downloading assets to dir: $OUT_DIR" echo "Downloading assets to dir: $OUT_DIR"
cd $OUT_DIR cd $OUT_DIR
if [[ -z "$GITHUB_PROXY_URL" ]]; then
GITHUB_URL="https://github.com"
else
GITHUB_URL="${GITHUB_PROXY_URL%/}"
fi
function retry_fetch() {
local url=$1
local filename=$2
curl --connect-timeout 10 --retry 3 -fsSL $url --output $filename || {
echo "Failed to download $url"
echo "You may try to set http_proxy and https_proxy environment variables."
if [[ -z "$GITHUB_PROXY_URL" ]]; then
echo "You may try to set GITHUB_PROXY_URL=http://mirror.ghproxy.com/"
fi
exit 1
}
}
# Download the SHA256 checksum attached to the release. To verify the integrity # Download the SHA256 checksum attached to the release. To verify the integrity
# of the download, this checksum will be used to check the download tar file # of the download, this checksum will be used to check the download tar file
# containing the built dashboard assets. # containing the built dashboard assets.
retry_fetch "${GITHUB_URL}/GreptimeTeam/dashboard/releases/download/${RELEASE_VERSION}/sha256.txt" sha256.txt curl -Ls https://github.com/GreptimeTeam/dashboard/releases/download/$RELEASE_VERSION/sha256.txt --output sha256.txt
# Download the tar file containing the built dashboard assets. # Download the tar file containing the built dashboard assets.
retry_fetch "${GITHUB_URL}/GreptimeTeam/dashboard/releases/download/$RELEASE_VERSION/build.tar.gz" build.tar.gz curl -L https://github.com/GreptimeTeam/dashboard/releases/download/$RELEASE_VERSION/build.tar.gz --output build.tar.gz
# Verify the checksums match; exit if they don't. # Verify the checksums match; exit if they don't.
case "$(uname -s)" in case "$(uname -s)" in

View File

@@ -4,9 +4,6 @@ version.workspace = true
edition.workspace = true edition.workspace = true
license.workspace = true license.workspace = true
[lints]
workspace = true
[dependencies] [dependencies]
common-base.workspace = true common-base.workspace = true
common-decimal.workspace = true common-decimal.workspace = true

View File

@@ -8,9 +8,6 @@ license.workspace = true
default = [] default = []
testing = [] testing = []
[lints]
workspace = true
[dependencies] [dependencies]
api.workspace = true api.workspace = true
async-trait.workspace = true async-trait.workspace = true

View File

@@ -7,9 +7,6 @@ license.workspace = true
[features] [features]
testing = [] testing = []
[lints]
workspace = true
[dependencies] [dependencies]
api.workspace = true api.workspace = true
arc-swap = "1.0" arc-swap = "1.0"

View File

@@ -164,8 +164,11 @@ pub enum Error {
location: Location, location: Location,
}, },
#[snafu(display("Failed to find table partitions"))] #[snafu(display("Failed to find table partitions: #{table}"))]
FindPartitions { source: partition::error::Error }, FindPartitions {
source: partition::error::Error,
table: String,
},
#[snafu(display("Failed to find region routes"))] #[snafu(display("Failed to find region routes"))]
FindRegionRoutes { source: partition::error::Error }, FindRegionRoutes { source: partition::error::Error },
@@ -251,12 +254,6 @@ pub enum Error {
source: common_meta::error::Error, source: common_meta::error::Error,
location: Location, location: Location,
}, },
#[snafu(display("Get null from table cache, key: {}", key))]
TableCacheNotGet { key: String, location: Location },
#[snafu(display("Failed to get table cache, err: {}", err_msg))]
GetTableCache { err_msg: String },
} }
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;
@@ -317,7 +314,6 @@ impl ErrorExt for Error {
Error::QueryAccessDenied { .. } => StatusCode::AccessDenied, Error::QueryAccessDenied { .. } => StatusCode::AccessDenied,
Error::Datafusion { .. } => StatusCode::EngineExecuteQuery, Error::Datafusion { .. } => StatusCode::EngineExecuteQuery,
Error::TableMetadataManager { source, .. } => source.status_code(), Error::TableMetadataManager { source, .. } => source.status_code(),
Error::TableCacheNotGet { .. } | Error::GetTableCache { .. } => StatusCode::Internal,
} }
} }

View File

@@ -19,9 +19,9 @@ mod partitions;
mod predicate; mod predicate;
mod region_peers; mod region_peers;
mod runtime_metrics; mod runtime_metrics;
pub mod schemata; mod schemata;
mod table_names; mod table_names;
pub mod tables; mod tables;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::{Arc, Weak}; use std::sync::{Arc, Weak};

View File

@@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use core::pin::pin;
use std::sync::{Arc, Weak}; use std::sync::{Arc, Weak};
use arrow_schema::SchemaRef as ArrowSchemaRef; use arrow_schema::SchemaRef as ArrowSchemaRef;
@@ -32,7 +31,7 @@ use datatypes::vectors::{
ConstantVector, DateTimeVector, DateTimeVectorBuilder, Int64Vector, Int64VectorBuilder, ConstantVector, DateTimeVector, DateTimeVectorBuilder, Int64Vector, Int64VectorBuilder,
MutableVector, StringVector, StringVectorBuilder, UInt64VectorBuilder, MutableVector, StringVector, StringVectorBuilder, UInt64VectorBuilder,
}; };
use futures::{StreamExt, TryStreamExt}; use futures::TryStreamExt;
use partition::manager::PartitionInfo; use partition::manager::PartitionInfo;
use partition::partition::PartitionDef; use partition::partition::PartitionDef;
use snafu::{OptionExt, ResultExt}; use snafu::{OptionExt, ResultExt};
@@ -241,64 +240,40 @@ impl InformationSchemaPartitionsBuilder {
let predicates = Predicates::from_scan_request(&request); let predicates = Predicates::from_scan_request(&request);
for schema_name in catalog_manager.schema_names(&catalog_name).await? { for schema_name in catalog_manager.schema_names(&catalog_name).await? {
let table_info_stream = catalog_manager let mut stream = catalog_manager.tables(&catalog_name, &schema_name).await;
.tables(&catalog_name, &schema_name)
.await
.try_filter_map(|t| async move {
let table_info = t.table_info();
if table_info.table_type == TableType::Temporary {
Ok(None)
} else {
Ok(Some(table_info))
}
});
const BATCH_SIZE: usize = 128; while let Some(table) = stream.try_next().await? {
let table_info = table.table_info();
// Split table infos into chunks if table_info.table_type == TableType::Temporary {
let mut table_info_chunks = pin!(table_info_stream.ready_chunks(BATCH_SIZE)); continue;
}
while let Some(table_infos) = table_info_chunks.next().await { let table_id = table_info.ident.table_id;
let table_infos = table_infos.into_iter().collect::<Result<Vec<_>>>()?; let partitions = if let Some(partition_manager) = &partition_manager {
let table_ids: Vec<TableId> =
table_infos.iter().map(|info| info.ident.table_id).collect();
let mut table_partitions = if let Some(partition_manager) = &partition_manager {
partition_manager partition_manager
.batch_find_table_partitions(&table_ids) .find_table_partitions(table_id)
.await .await
.context(FindPartitionsSnafu)? .context(FindPartitionsSnafu {
table: &table_info.name,
})?
} else { } else {
// Current node must be a standalone instance, contains only one partition by default. // Current node must be a standalone instance, contains only one partition by default.
// TODO(dennis): change it when we support multi-regions for standalone. // TODO(dennis): change it when we support multi-regions for standalone.
table_ids vec![PartitionInfo {
.into_iter() id: RegionId::new(table_id, 0),
.map(|table_id| { partition: PartitionDef::new(vec![], vec![]),
( }]
table_id,
vec![PartitionInfo {
id: RegionId::new(table_id, 0),
partition: PartitionDef::new(vec![], vec![]),
}],
)
})
.collect()
}; };
for table_info in table_infos { self.add_partitions(
let partitions = table_partitions &predicates,
.remove(&table_info.ident.table_id) &table_info,
.unwrap_or(vec![]); &catalog_name,
&schema_name,
self.add_partitions( &table_info.name,
&predicates, &partitions,
&table_info, );
&catalog_name,
&schema_name,
&table_info.name,
&partitions,
);
}
} }
} }

View File

@@ -199,7 +199,7 @@ impl InformationSchemaRegionPeersBuilder {
let table_routes = if let Some(partition_manager) = &partition_manager { let table_routes = if let Some(partition_manager) = &partition_manager {
partition_manager partition_manager
.batch_find_region_routes(&table_ids) .find_region_routes_batch(&table_ids)
.await .await
.context(FindRegionRoutesSnafu)? .context(FindRegionRoutesSnafu)?
} else { } else {

View File

@@ -37,8 +37,8 @@ use crate::error::{
use crate::information_schema::{InformationTable, Predicates}; use crate::information_schema::{InformationTable, Predicates};
use crate::CatalogManager; use crate::CatalogManager;
pub const CATALOG_NAME: &str = "catalog_name"; const CATALOG_NAME: &str = "catalog_name";
pub const SCHEMA_NAME: &str = "schema_name"; const SCHEMA_NAME: &str = "schema_name";
const DEFAULT_CHARACTER_SET_NAME: &str = "default_character_set_name"; const DEFAULT_CHARACTER_SET_NAME: &str = "default_character_set_name";
const DEFAULT_COLLATION_NAME: &str = "default_collation_name"; const DEFAULT_COLLATION_NAME: &str = "default_collation_name";
const INIT_CAPACITY: usize = 42; const INIT_CAPACITY: usize = 42;

View File

@@ -39,10 +39,10 @@ use crate::error::{
use crate::information_schema::{InformationTable, Predicates}; use crate::information_schema::{InformationTable, Predicates};
use crate::CatalogManager; use crate::CatalogManager;
pub const TABLE_CATALOG: &str = "table_catalog"; const TABLE_CATALOG: &str = "table_catalog";
pub const TABLE_SCHEMA: &str = "table_schema"; const TABLE_SCHEMA: &str = "table_schema";
pub const TABLE_NAME: &str = "table_name"; const TABLE_NAME: &str = "table_name";
pub const TABLE_TYPE: &str = "table_type"; const TABLE_TYPE: &str = "table_type";
const TABLE_ID: &str = "table_id"; const TABLE_ID: &str = "table_id";
const ENGINE: &str = "engine"; const ENGINE: &str = "engine";
const INIT_CAPACITY: usize = 42; const INIT_CAPACITY: usize = 42;

View File

@@ -82,10 +82,12 @@ impl CachedMetaKvBackendBuilder {
let cache_ttl = self.cache_ttl.unwrap_or(DEFAULT_CACHE_TTL); let cache_ttl = self.cache_ttl.unwrap_or(DEFAULT_CACHE_TTL);
let cache_tti = self.cache_tti.unwrap_or(DEFAULT_CACHE_TTI); let cache_tti = self.cache_tti.unwrap_or(DEFAULT_CACHE_TTI);
let cache = CacheBuilder::new(cache_max_capacity) let cache = Arc::new(
.time_to_live(cache_ttl) CacheBuilder::new(cache_max_capacity)
.time_to_idle(cache_tti) .time_to_live(cache_ttl)
.build(); .time_to_idle(cache_tti)
.build(),
);
let kv_backend = Arc::new(MetaKvBackend { let kv_backend = Arc::new(MetaKvBackend {
client: self.meta_client, client: self.meta_client,
@@ -102,7 +104,7 @@ impl CachedMetaKvBackendBuilder {
} }
} }
pub type CacheBackend = Cache<Vec<u8>, KeyValue>; pub type CacheBackendRef = Arc<Cache<Vec<u8>, KeyValue>>;
/// A wrapper of `MetaKvBackend` with cache support. /// A wrapper of `MetaKvBackend` with cache support.
/// ///
@@ -115,7 +117,7 @@ pub type CacheBackend = Cache<Vec<u8>, KeyValue>;
/// TTL and TTI for cache. /// TTL and TTI for cache.
pub struct CachedMetaKvBackend { pub struct CachedMetaKvBackend {
kv_backend: KvBackendRef, kv_backend: KvBackendRef,
cache: CacheBackend, cache: CacheBackendRef,
name: String, name: String,
version: AtomicUsize, version: AtomicUsize,
} }
@@ -315,10 +317,12 @@ impl CachedMetaKvBackend {
// only for test // only for test
#[cfg(test)] #[cfg(test)]
fn wrap(kv_backend: KvBackendRef) -> Self { fn wrap(kv_backend: KvBackendRef) -> Self {
let cache = CacheBuilder::new(DEFAULT_CACHE_MAX_CAPACITY) let cache = Arc::new(
.time_to_live(DEFAULT_CACHE_TTL) CacheBuilder::new(DEFAULT_CACHE_MAX_CAPACITY)
.time_to_idle(DEFAULT_CACHE_TTI) .time_to_live(DEFAULT_CACHE_TTL)
.build(); .time_to_idle(DEFAULT_CACHE_TTI)
.build(),
);
let name = format!("CachedKvBackend({})", kv_backend.name()); let name = format!("CachedKvBackend({})", kv_backend.name());
Self { Self {
@@ -329,7 +333,7 @@ impl CachedMetaKvBackend {
} }
} }
pub fn cache(&self) -> &CacheBackend { pub fn cache(&self) -> &CacheBackendRef {
&self.cache &self.cache
} }

View File

@@ -15,13 +15,9 @@
use std::any::Any; use std::any::Any;
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::sync::{Arc, Weak}; use std::sync::{Arc, Weak};
use std::time::Duration;
use async_stream::try_stream; use async_stream::try_stream;
use common_catalog::consts::{ use common_catalog::consts::{DEFAULT_SCHEMA_NAME, INFORMATION_SCHEMA_NAME, NUMBERS_TABLE_ID};
DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME, INFORMATION_SCHEMA_NAME, NUMBERS_TABLE_ID,
};
use common_catalog::format_full_table_name;
use common_error::ext::BoxedError; use common_error::ext::BoxedError;
use common_meta::cache_invalidator::{CacheInvalidator, CacheInvalidatorRef, Context}; use common_meta::cache_invalidator::{CacheInvalidator, CacheInvalidatorRef, Context};
use common_meta::error::Result as MetaResult; use common_meta::error::Result as MetaResult;
@@ -34,7 +30,6 @@ use common_meta::kv_backend::KvBackendRef;
use common_meta::table_name::TableName; use common_meta::table_name::TableName;
use futures_util::stream::BoxStream; use futures_util::stream::BoxStream;
use futures_util::{StreamExt, TryStreamExt}; use futures_util::{StreamExt, TryStreamExt};
use moka::future::{Cache as AsyncCache, CacheBuilder};
use moka::sync::Cache; use moka::sync::Cache;
use partition::manager::{PartitionRuleManager, PartitionRuleManagerRef}; use partition::manager::{PartitionRuleManager, PartitionRuleManagerRef};
use snafu::prelude::*; use snafu::prelude::*;
@@ -43,10 +38,9 @@ use table::metadata::TableId;
use table::table::numbers::{NumbersTable, NUMBERS_TABLE_NAME}; use table::table::numbers::{NumbersTable, NUMBERS_TABLE_NAME};
use table::TableRef; use table::TableRef;
use crate::error::Error::{GetTableCache, TableCacheNotGet};
use crate::error::{ use crate::error::{
self as catalog_err, ListCatalogsSnafu, ListSchemasSnafu, ListTablesSnafu, self as catalog_err, ListCatalogsSnafu, ListSchemasSnafu, ListTablesSnafu,
Result as CatalogResult, TableCacheNotGetSnafu, TableMetadataManagerSnafu, Result as CatalogResult, TableMetadataManagerSnafu,
}; };
use crate::information_schema::InformationSchemaProvider; use crate::information_schema::InformationSchemaProvider;
use crate::CatalogManager; use crate::CatalogManager;
@@ -66,7 +60,6 @@ pub struct KvBackendCatalogManager {
table_metadata_manager: TableMetadataManagerRef, table_metadata_manager: TableMetadataManagerRef,
/// A sub-CatalogManager that handles system tables /// A sub-CatalogManager that handles system tables
system_catalog: SystemCatalog, system_catalog: SystemCatalog,
table_cache: AsyncCache<String, TableRef>,
} }
fn make_table(table_info_value: TableInfoValue) -> CatalogResult<TableRef> { fn make_table(table_info_value: TableInfoValue) -> CatalogResult<TableRef> {
@@ -86,24 +79,13 @@ impl CacheInvalidator for KvBackendCatalogManager {
} }
async fn invalidate_table_name(&self, ctx: &Context, table_name: TableName) -> MetaResult<()> { async fn invalidate_table_name(&self, ctx: &Context, table_name: TableName) -> MetaResult<()> {
let table_cache_key = format_full_table_name(
&table_name.catalog_name,
&table_name.schema_name,
&table_name.table_name,
);
self.cache_invalidator self.cache_invalidator
.invalidate_table_name(ctx, table_name) .invalidate_table_name(ctx, table_name)
.await?; .await
self.table_cache.invalidate(&table_cache_key).await;
Ok(())
} }
} }
const CATALOG_CACHE_MAX_CAPACITY: u64 = 128; const DEFAULT_CACHED_CATALOG: u64 = 128;
const TABLE_CACHE_MAX_CAPACITY: u64 = 65536;
const TABLE_CACHE_TTL: Duration = Duration::from_secs(10 * 60);
const TABLE_CACHE_TTI: Duration = Duration::from_secs(5 * 60);
impl KvBackendCatalogManager { impl KvBackendCatalogManager {
pub fn new(backend: KvBackendRef, cache_invalidator: CacheInvalidatorRef) -> Arc<Self> { pub fn new(backend: KvBackendRef, cache_invalidator: CacheInvalidatorRef) -> Arc<Self> {
@@ -113,16 +95,13 @@ impl KvBackendCatalogManager {
cache_invalidator, cache_invalidator,
system_catalog: SystemCatalog { system_catalog: SystemCatalog {
catalog_manager: me.clone(), catalog_manager: me.clone(),
catalog_cache: Cache::new(CATALOG_CACHE_MAX_CAPACITY), catalog_cache: Cache::new(DEFAULT_CACHED_CATALOG),
information_schema_provider: Arc::new(InformationSchemaProvider::new( information_schema_provider: Arc::new(InformationSchemaProvider::new(
DEFAULT_CATALOG_NAME.to_string(), // The catalog name is not used in system_catalog, so let it empty
String::default(),
me.clone(), me.clone(),
)), )),
}, },
table_cache: CacheBuilder::new(TABLE_CACHE_MAX_CAPACITY)
.time_to_live(TABLE_CACHE_TTL)
.time_to_idle(TABLE_CACHE_TTI)
.build(),
}) })
} }
@@ -237,52 +216,29 @@ impl CatalogManager for KvBackendCatalogManager {
return Ok(Some(table)); return Ok(Some(table));
} }
let init = async { let key = TableNameKey::new(catalog, schema, table_name);
let table_name_key = TableNameKey::new(catalog, schema, table_name); let Some(table_name_value) = self
let Some(table_name_value) = self .table_metadata_manager
.table_metadata_manager .table_name_manager()
.table_name_manager() .get(key)
.get(table_name_key)
.await
.context(TableMetadataManagerSnafu)?
else {
return TableCacheNotGetSnafu {
key: table_name_key.to_string(),
}
.fail();
};
let table_id = table_name_value.table_id();
let Some(table_info_value) = self
.table_metadata_manager
.table_info_manager()
.get(table_id)
.await
.context(TableMetadataManagerSnafu)?
.map(|v| v.into_inner())
else {
return TableCacheNotGetSnafu {
key: table_name_key.to_string(),
}
.fail();
};
make_table(table_info_value)
};
match self
.table_cache
.try_get_with_by_ref(&format_full_table_name(catalog, schema, table_name), init)
.await .await
{ .context(TableMetadataManagerSnafu)?
Ok(table) => Ok(Some(table)), else {
Err(err) => match err.as_ref() { return Ok(None);
TableCacheNotGet { .. } => Ok(None), };
_ => Err(err), let table_id = table_name_value.table_id();
},
} let Some(table_info_value) = self
.map_err(|err| GetTableCache { .table_metadata_manager
err_msg: err.to_string(), .table_info_manager()
}) .get(table_id)
.await
.context(TableMetadataManagerSnafu)?
.map(|v| v.into_inner())
else {
return Ok(None);
};
make_table(table_info_value).map(Some)
} }
async fn tables<'a>( async fn tables<'a>(

View File

@@ -7,9 +7,6 @@ license.workspace = true
[features] [features]
testing = [] testing = []
[lints]
workspace = true
[dependencies] [dependencies]
api.workspace = true api.workspace = true
arc-swap = "1.6" arc-swap = "1.6"
@@ -37,8 +34,6 @@ parking_lot = "0.12"
prometheus.workspace = true prometheus.workspace = true
prost.workspace = true prost.workspace = true
rand.workspace = true rand.workspace = true
serde.workspace = true
serde_json.workspace = true
session.workspace = true session.workspace = true
snafu.workspace = true snafu.workspace = true
tokio.workspace = true tokio.workspace = true

View File

@@ -307,7 +307,7 @@ impl Database {
reason: "Expect 'AffectedRows' Flight messages to be the one and the only!" reason: "Expect 'AffectedRows' Flight messages to be the one and the only!"
} }
); );
Ok(Output::new_with_affected_rows(rows)) Ok(Output::AffectedRows(rows))
} }
FlightMessage::Recordbatch(_) | FlightMessage::Metrics(_) => { FlightMessage::Recordbatch(_) | FlightMessage::Metrics(_) => {
IllegalFlightMessagesSnafu { IllegalFlightMessagesSnafu {
@@ -340,7 +340,7 @@ impl Database {
output_ordering: None, output_ordering: None,
metrics: Default::default(), metrics: Default::default(),
}; };
Ok(Output::new_with_stream(Box::pin(record_batch_stream))) Ok(Output::Stream(Box::pin(record_batch_stream)))
} }
} }
} }

View File

@@ -134,17 +134,10 @@ impl From<Status> for Error {
impl Error { impl Error {
pub fn should_retry(&self) -> bool { pub fn should_retry(&self) -> bool {
// TODO(weny): figure out each case of these codes. !matches!(
matches!(
self, self,
Self::RegionServer { Self::RegionServer {
code: Code::Cancelled, code: Code::InvalidArgument,
..
} | Self::RegionServer {
code: Code::DeadlineExceeded,
..
} | Self::RegionServer {
code: Code::Unavailable,
.. ..
} }
) )

View File

@@ -26,7 +26,7 @@ use api::v1::greptime_response::Response;
use api::v1::{AffectedRows, GreptimeResponse}; use api::v1::{AffectedRows, GreptimeResponse};
pub use common_catalog::consts::{DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME}; pub use common_catalog::consts::{DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME};
use common_error::status_code::StatusCode; use common_error::status_code::StatusCode;
pub use common_query::{Output, OutputData, OutputMeta}; pub use common_query::Output;
pub use common_recordbatch::{RecordBatches, SendableRecordBatchStream}; pub use common_recordbatch::{RecordBatches, SendableRecordBatchStream};
use snafu::OptionExt; use snafu::OptionExt;

View File

@@ -123,8 +123,8 @@ impl RegionRequester {
.fail(); .fail();
}; };
let metrics = Arc::new(ArcSwapOption::from(None)); let metrics_str = Arc::new(ArcSwapOption::from(None));
let metrics_ref = metrics.clone(); let ref_str = metrics_str.clone();
let tracing_context = TracingContext::from_current_span(); let tracing_context = TracingContext::from_current_span();
@@ -140,8 +140,7 @@ impl RegionRequester {
match flight_message { match flight_message {
FlightMessage::Recordbatch(record_batch) => yield Ok(record_batch), FlightMessage::Recordbatch(record_batch) => yield Ok(record_batch),
FlightMessage::Metrics(s) => { FlightMessage::Metrics(s) => {
let m = serde_json::from_str(&s).ok().map(Arc::new); ref_str.swap(Some(Arc::new(s)));
metrics_ref.swap(m);
break; break;
} }
_ => { _ => {
@@ -160,7 +159,7 @@ impl RegionRequester {
schema, schema,
stream, stream,
output_ordering: None, output_ordering: None,
metrics, metrics: metrics_str,
}; };
Ok(Box::pin(record_batch_stream)) Ok(Box::pin(record_batch_stream))
} }
@@ -197,7 +196,7 @@ impl RegionRequester {
check_response_header(header)?; check_response_header(header)?;
Ok(affected_rows as _) Ok(affected_rows)
} }
pub async fn handle(&self, request: RegionRequest) -> Result<AffectedRows> { pub async fn handle(&self, request: RegionRequest) -> Result<AffectedRows> {

View File

@@ -12,9 +12,6 @@ path = "src/bin/greptime.rs"
[features] [features]
tokio-console = ["common-telemetry/tokio-console"] tokio-console = ["common-telemetry/tokio-console"]
[lints]
workspace = true
[dependencies] [dependencies]
anymap = "1.0.0-beta.2" anymap = "1.0.0-beta.2"
async-trait.workspace = true async-trait.workspace = true

View File

@@ -62,9 +62,7 @@ pub struct BenchTableMetadataCommand {
impl BenchTableMetadataCommand { impl BenchTableMetadataCommand {
pub async fn build(&self) -> Result<Instance> { pub async fn build(&self) -> Result<Instance> {
let etcd_store = EtcdStore::with_endpoints([&self.etcd_addr], 128) let etcd_store = EtcdStore::with_endpoints([&self.etcd_addr]).await.unwrap();
.await
.unwrap();
let table_metadata_manager = Arc::new(TableMetadataManager::new(etcd_store)); let table_metadata_manager = Arc::new(TableMetadataManager::new(etcd_store));

View File

@@ -19,7 +19,8 @@ use async_trait::async_trait;
use clap::{Parser, ValueEnum}; use clap::{Parser, ValueEnum};
use client::api::v1::auth_header::AuthScheme; use client::api::v1::auth_header::AuthScheme;
use client::api::v1::Basic; use client::api::v1::Basic;
use client::{Client, Database, OutputData, DEFAULT_SCHEMA_NAME}; use client::{Client, Database, DEFAULT_SCHEMA_NAME};
use common_query::Output;
use common_recordbatch::util::collect; use common_recordbatch::util::collect;
use common_telemetry::{debug, error, info, warn}; use common_telemetry::{debug, error, info, warn};
use datatypes::scalars::ScalarVector; use datatypes::scalars::ScalarVector;
@@ -141,7 +142,7 @@ impl Export {
.with_context(|_| RequestDatabaseSnafu { .with_context(|_| RequestDatabaseSnafu {
sql: "show databases".to_string(), sql: "show databases".to_string(),
})?; })?;
let OutputData::Stream(stream) = result.data else { let Output::Stream(stream) = result else {
NotDataFromOutputSnafu.fail()? NotDataFromOutputSnafu.fail()?
}; };
let record_batch = collect(stream) let record_batch = collect(stream)
@@ -182,7 +183,7 @@ impl Export {
.sql(&sql) .sql(&sql)
.await .await
.with_context(|_| RequestDatabaseSnafu { sql })?; .with_context(|_| RequestDatabaseSnafu { sql })?;
let OutputData::Stream(stream) = result.data else { let Output::Stream(stream) = result else {
NotDataFromOutputSnafu.fail()? NotDataFromOutputSnafu.fail()?
}; };
let Some(record_batch) = collect(stream) let Some(record_batch) = collect(stream)
@@ -234,7 +235,7 @@ impl Export {
.sql(&sql) .sql(&sql)
.await .await
.with_context(|_| RequestDatabaseSnafu { sql })?; .with_context(|_| RequestDatabaseSnafu { sql })?;
let OutputData::Stream(stream) = result.data else { let Output::Stream(stream) = result else {
NotDataFromOutputSnafu.fail()? NotDataFromOutputSnafu.fail()?
}; };
let record_batch = collect(stream) let record_batch = collect(stream)

View File

@@ -19,7 +19,7 @@ use std::time::Instant;
use catalog::kvbackend::{ use catalog::kvbackend::{
CachedMetaKvBackend, CachedMetaKvBackendBuilder, KvBackendCatalogManager, CachedMetaKvBackend, CachedMetaKvBackendBuilder, KvBackendCatalogManager,
}; };
use client::{Client, Database, OutputData, DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME}; use client::{Client, Database, DEFAULT_CATALOG_NAME, DEFAULT_SCHEMA_NAME};
use common_base::Plugins; use common_base::Plugins;
use common_error::ext::ErrorExt; use common_error::ext::ErrorExt;
use common_query::Output; use common_query::Output;
@@ -184,15 +184,15 @@ impl Repl {
} }
.context(RequestDatabaseSnafu { sql: &sql })?; .context(RequestDatabaseSnafu { sql: &sql })?;
let either = match output.data { let either = match output {
OutputData::Stream(s) => { Output::Stream(s) => {
let x = RecordBatches::try_collect(s) let x = RecordBatches::try_collect(s)
.await .await
.context(CollectRecordBatchesSnafu)?; .context(CollectRecordBatchesSnafu)?;
Either::Left(x) Either::Left(x)
} }
OutputData::RecordBatches(x) => Either::Left(x), Output::RecordBatches(x) => Either::Left(x),
OutputData::AffectedRows(rows) => Either::Right(rows), Output::AffectedRows(rows) => Either::Right(rows),
}; };
let end = Instant::now(); let end = Instant::now();
@@ -260,7 +260,6 @@ async fn create_query_engine(meta_addr: &str) -> Result<DatafusionQueryEngine> {
catalog_list, catalog_list,
None, None,
None, None,
None,
false, false,
plugins.clone(), plugins.clone(),
)); ));

View File

@@ -70,7 +70,7 @@ impl UpgradeCommand {
etcd_addr: &self.etcd_addr, etcd_addr: &self.etcd_addr,
})?; })?;
let tool = MigrateTableMetadata { let tool = MigrateTableMetadata {
etcd_store: EtcdStore::with_etcd_client(client, 128), etcd_store: EtcdStore::with_etcd_client(client),
dryrun: self.dryrun, dryrun: self.dryrun,
skip_catalog_keys: self.skip_catalog_keys, skip_catalog_keys: self.skip_catalog_keys,
skip_table_global_keys: self.skip_table_global_keys, skip_table_global_keys: self.skip_table_global_keys,

View File

@@ -43,10 +43,6 @@ impl Instance {
pub fn datanode_mut(&mut self) -> &mut Datanode { pub fn datanode_mut(&mut self) -> &mut Datanode {
&mut self.datanode &mut self.datanode
} }
pub fn datanode(&self) -> &Datanode {
&self.datanode
}
} }
#[async_trait] #[async_trait]
@@ -239,7 +235,6 @@ impl StartCommand {
.with_default_grpc_server(&datanode.region_server()) .with_default_grpc_server(&datanode.region_server())
.enable_http_service() .enable_http_service()
.build() .build()
.await
.context(StartDatanodeSnafu)?; .context(StartDatanodeSnafu)?;
datanode.setup_services(services); datanode.setup_services(services);

View File

@@ -43,17 +43,13 @@ pub struct Instance {
} }
impl Instance { impl Instance {
pub fn new(frontend: FeInstance) -> Self { fn new(frontend: FeInstance) -> Self {
Self { frontend } Self { frontend }
} }
pub fn mut_inner(&mut self) -> &mut FeInstance { pub fn mut_inner(&mut self) -> &mut FeInstance {
&mut self.frontend &mut self.frontend
} }
pub fn inner(&self) -> &FeInstance {
&self.frontend
}
} }
#[async_trait] #[async_trait]
@@ -275,7 +271,6 @@ impl StartCommand {
let servers = Services::new(opts.clone(), Arc::new(instance.clone()), plugins) let servers = Services::new(opts.clone(), Arc::new(instance.clone()), plugins)
.build() .build()
.await
.context(StartFrontendSnafu)?; .context(StartFrontendSnafu)?;
instance instance
.build_servers(opts, servers) .build_servers(opts, servers)

View File

@@ -32,11 +32,11 @@ lazy_static::lazy_static! {
} }
#[async_trait] #[async_trait]
pub trait App: Send { pub trait App {
fn name(&self) -> &str; fn name(&self) -> &str;
/// A hook for implementor to make something happened before actual startup. Defaults to no-op. /// A hook for implementor to make something happened before actual startup. Defaults to no-op.
async fn pre_start(&mut self) -> error::Result<()> { fn pre_start(&mut self) -> error::Result<()> {
Ok(()) Ok(())
} }
@@ -46,21 +46,24 @@ pub trait App: Send {
} }
pub async fn start_app(mut app: Box<dyn App>) -> error::Result<()> { pub async fn start_app(mut app: Box<dyn App>) -> error::Result<()> {
info!("Starting app: {}", app.name()); let name = app.name().to_string();
app.pre_start().await?; app.pre_start()?;
app.start().await?; tokio::select! {
result = app.start() => {
if let Err(e) = tokio::signal::ctrl_c().await { if let Err(err) = result {
error!("Failed to listen for ctrl-c signal: {}", e); error!(err; "Failed to start app {name}!");
// It's unusual to fail to listen for ctrl-c signal, maybe there's something unexpected in }
// the underlying system. So we stop the app instead of running nonetheless to let people }
// investigate the issue. _ = tokio::signal::ctrl_c() => {
if let Err(err) = app.stop().await {
error!(err; "Failed to stop app {name}!");
}
info!("Goodbye!");
}
} }
app.stop().await?;
info!("Goodbye!");
Ok(()) Ok(())
} }

View File

@@ -117,12 +117,10 @@ struct StartCommand {
/// The working home directory of this metasrv instance. /// The working home directory of this metasrv instance.
#[clap(long)] #[clap(long)]
data_home: Option<String>, data_home: Option<String>,
/// If it's not empty, the metasrv will store all data with this key prefix. /// If it's not empty, the metasrv will store all data with this key prefix.
#[clap(long, default_value = "")] #[clap(long, default_value = "")]
store_key_prefix: String, store_key_prefix: String,
/// The max operations per txn
#[clap(long)]
max_txn_ops: Option<usize>,
} }
impl StartCommand { impl StartCommand {
@@ -183,10 +181,6 @@ impl StartCommand {
opts.store_key_prefix = self.store_key_prefix.clone() opts.store_key_prefix = self.store_key_prefix.clone()
} }
if let Some(max_txn_ops) = self.max_txn_ops {
opts.max_txn_ops = max_txn_ops;
}
// Disable dashboard in metasrv. // Disable dashboard in metasrv.
opts.http.disable_dashboard = true; opts.http.disable_dashboard = true;

View File

@@ -21,8 +21,8 @@ use common_catalog::consts::MIN_USER_TABLE_ID;
use common_config::{metadata_store_dir, KvBackendConfig}; use common_config::{metadata_store_dir, KvBackendConfig};
use common_meta::cache_invalidator::DummyCacheInvalidator; use common_meta::cache_invalidator::DummyCacheInvalidator;
use common_meta::datanode_manager::DatanodeManagerRef; use common_meta::datanode_manager::DatanodeManagerRef;
use common_meta::ddl::table_meta::{TableMetadataAllocator, TableMetadataAllocatorRef}; use common_meta::ddl::table_meta::TableMetadataAllocator;
use common_meta::ddl::ProcedureExecutorRef; use common_meta::ddl::DdlTaskExecutorRef;
use common_meta::ddl_manager::DdlManager; use common_meta::ddl_manager::DdlManager;
use common_meta::key::{TableMetadataManager, TableMetadataManagerRef}; use common_meta::key::{TableMetadataManager, TableMetadataManagerRef};
use common_meta::kv_backend::KvBackendRef; use common_meta::kv_backend::KvBackendRef;
@@ -419,11 +419,11 @@ impl StartCommand {
let table_metadata_manager = let table_metadata_manager =
Self::create_table_metadata_manager(kv_backend.clone()).await?; Self::create_table_metadata_manager(kv_backend.clone()).await?;
let table_meta_allocator = Arc::new(TableMetadataAllocator::new( let table_meta_allocator = TableMetadataAllocator::new(
table_id_sequence, table_id_sequence,
wal_options_allocator.clone(), wal_options_allocator.clone(),
table_metadata_manager.table_name_manager().clone(), table_metadata_manager.clone(),
)); );
let ddl_task_executor = Self::create_ddl_task_executor( let ddl_task_executor = Self::create_ddl_task_executor(
table_metadata_manager, table_metadata_manager,
@@ -441,7 +441,6 @@ impl StartCommand {
let servers = Services::new(fe_opts.clone(), Arc::new(frontend.clone()), fe_plugins) let servers = Services::new(fe_opts.clone(), Arc::new(frontend.clone()), fe_plugins)
.build() .build()
.await
.context(StartFrontendSnafu)?; .context(StartFrontendSnafu)?;
frontend frontend
.build_servers(fe_opts, servers) .build_servers(fe_opts, servers)
@@ -459,9 +458,9 @@ impl StartCommand {
table_metadata_manager: TableMetadataManagerRef, table_metadata_manager: TableMetadataManagerRef,
procedure_manager: ProcedureManagerRef, procedure_manager: ProcedureManagerRef,
datanode_manager: DatanodeManagerRef, datanode_manager: DatanodeManagerRef,
table_meta_allocator: TableMetadataAllocatorRef, table_meta_allocator: TableMetadataAllocator,
) -> Result<ProcedureExecutorRef> { ) -> Result<DdlTaskExecutorRef> {
let procedure_executor: ProcedureExecutorRef = Arc::new( let ddl_task_executor: DdlTaskExecutorRef = Arc::new(
DdlManager::try_new( DdlManager::try_new(
procedure_manager, procedure_manager,
datanode_manager, datanode_manager,
@@ -473,7 +472,7 @@ impl StartCommand {
.context(InitDdlManagerSnafu)?, .context(InitDdlManagerSnafu)?,
); );
Ok(procedure_executor) Ok(ddl_task_executor)
} }
pub async fn create_table_metadata_manager( pub async fn create_table_metadata_manager(

View File

@@ -4,9 +4,6 @@ version.workspace = true
edition.workspace = true edition.workspace = true
license.workspace = true license.workspace = true
[lints]
workspace = true
[dependencies] [dependencies]
anymap = "1.0.0-beta.2" anymap = "1.0.0-beta.2"
bitvec = "1.0" bitvec = "1.0"

View File

@@ -21,8 +21,6 @@ pub mod readable_size;
use core::any::Any; use core::any::Any;
use std::sync::{Arc, Mutex, MutexGuard}; use std::sync::{Arc, Mutex, MutexGuard};
pub type AffectedRows = usize;
pub use bit_vec::BitVec; pub use bit_vec::BitVec;
/// [`Plugins`] is a wrapper of Arc contents. /// [`Plugins`] is a wrapper of Arc contents.

View File

@@ -4,9 +4,6 @@ version.workspace = true
edition.workspace = true edition.workspace = true
license.workspace = true license.workspace = true
[lints]
workspace = true
[dependencies] [dependencies]
common-error.workspace = true common-error.workspace = true
common-macro.workspace = true common-macro.workspace = true

View File

@@ -4,9 +4,6 @@ version.workspace = true
edition.workspace = true edition.workspace = true
license.workspace = true license.workspace = true
[lints]
workspace = true
[dependencies] [dependencies]
common-base.workspace = true common-base.workspace = true
humantime-serde.workspace = true humantime-serde.workspace = true

View File

@@ -4,9 +4,6 @@ version.workspace = true
edition.workspace = true edition.workspace = true
license.workspace = true license.workspace = true
[lints]
workspace = true
[dependencies] [dependencies]
arrow.workspace = true arrow.workspace = true
arrow-schema.workspace = true arrow-schema.workspace = true

View File

@@ -28,15 +28,12 @@ const REGION: &str = "region";
const ENABLE_VIRTUAL_HOST_STYLE: &str = "enable_virtual_host_style"; const ENABLE_VIRTUAL_HOST_STYLE: &str = "enable_virtual_host_style";
pub fn is_supported_in_s3(key: &str) -> bool { pub fn is_supported_in_s3(key: &str) -> bool {
[ key == ENDPOINT
ENDPOINT, || key == ACCESS_KEY_ID
ACCESS_KEY_ID, || key == SECRET_ACCESS_KEY
SECRET_ACCESS_KEY, || key == SESSION_TOKEN
SESSION_TOKEN, || key == REGION
REGION, || key == ENABLE_VIRTUAL_HOST_STYLE
ENABLE_VIRTUAL_HOST_STYLE,
]
.contains(&key)
} }
pub fn build_s3_backend( pub fn build_s3_backend(

View File

@@ -4,9 +4,6 @@ version.workspace = true
edition.workspace = true edition.workspace = true
license.workspace = true license.workspace = true
[lints]
workspace = true
[dependencies] [dependencies]
arrow.workspace = true arrow.workspace = true
bigdecimal.workspace = true bigdecimal.workspace = true

View File

@@ -4,9 +4,6 @@ version.workspace = true
edition.workspace = true edition.workspace = true
license.workspace = true license.workspace = true
[lints]
workspace = true
[dependencies] [dependencies]
snafu.workspace = true snafu.workspace = true
strum.workspace = true strum.workspace = true

View File

@@ -19,9 +19,7 @@ pub mod format;
pub mod mock; pub mod mock;
pub mod status_code; pub mod status_code;
pub use snafu;
// HACK - these headers are here for shared in gRPC services. For common HTTP headers,
// please define in `src/servers/src/http/header.rs`.
pub const GREPTIME_DB_HEADER_ERROR_CODE: &str = "x-greptime-err-code"; pub const GREPTIME_DB_HEADER_ERROR_CODE: &str = "x-greptime-err-code";
pub const GREPTIME_DB_HEADER_ERROR_MSG: &str = "x-greptime-err-msg"; pub const GREPTIME_DB_HEADER_ERROR_MSG: &str = "x-greptime-err-msg";
pub use snafu;

View File

@@ -4,19 +4,13 @@ edition.workspace = true
version.workspace = true version.workspace = true
license.workspace = true license.workspace = true
[lints]
workspace = true
[dependencies] [dependencies]
api.workspace = true api.workspace = true
arc-swap = "1.0" arc-swap = "1.0"
async-trait.workspace = true async-trait.workspace = true
chrono-tz = "0.6" chrono-tz = "0.6"
common-base.workspace = true
common-catalog.workspace = true
common-error.workspace = true common-error.workspace = true
common-macro.workspace = true common-macro.workspace = true
common-meta.workspace = true
common-query.workspace = true common-query.workspace = true
common-runtime.workspace = true common-runtime.workspace = true
common-telemetry.workspace = true common-telemetry.workspace = true
@@ -29,12 +23,9 @@ num = "0.4"
num-traits = "0.2" num-traits = "0.2"
once_cell.workspace = true once_cell.workspace = true
paste = "1.0" paste = "1.0"
serde.workspace = true
serde_json.workspace = true
session.workspace = true session.workspace = true
snafu.workspace = true snafu.workspace = true
statrs = "0.16" statrs = "0.16"
store-api.workspace = true
table.workspace = true table.workspace = true
[dev-dependencies] [dev-dependencies]

View File

@@ -30,17 +30,6 @@ pub struct FunctionContext {
pub state: Arc<FunctionState>, pub state: Arc<FunctionState>,
} }
impl FunctionContext {
/// Create a mock [`FunctionContext`] for test.
#[cfg(any(test, feature = "testing"))]
pub fn mock() -> Self {
Self {
query_ctx: QueryContextBuilder::default().build(),
state: Arc::new(FunctionState::mock()),
}
}
}
impl Default for FunctionContext { impl Default for FunctionContext {
fn default() -> Self { fn default() -> Self {
Self { Self {

View File

@@ -21,7 +21,6 @@ use once_cell::sync::Lazy;
use crate::function::FunctionRef; use crate::function::FunctionRef;
use crate::scalars::aggregate::{AggregateFunctionMetaRef, AggregateFunctions}; use crate::scalars::aggregate::{AggregateFunctionMetaRef, AggregateFunctions};
use crate::scalars::date::DateFunction; use crate::scalars::date::DateFunction;
use crate::scalars::expression::ExpressionFunction;
use crate::scalars::math::MathFunction; use crate::scalars::math::MathFunction;
use crate::scalars::numpy::NumpyFunction; use crate::scalars::numpy::NumpyFunction;
use crate::scalars::timestamp::TimestampFunction; use crate::scalars::timestamp::TimestampFunction;
@@ -81,7 +80,6 @@ pub static FUNCTION_REGISTRY: Lazy<Arc<FunctionRegistry>> = Lazy::new(|| {
NumpyFunction::register(&function_registry); NumpyFunction::register(&function_registry);
TimestampFunction::register(&function_registry); TimestampFunction::register(&function_registry);
DateFunction::register(&function_registry); DateFunction::register(&function_registry);
ExpressionFunction::register(&function_registry);
// Aggregate functions // Aggregate functions
AggregateFunctions::register(&function_registry); AggregateFunctions::register(&function_registry);

View File

@@ -13,14 +13,15 @@
// limitations under the License. // limitations under the License.
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration;
use api::v1::meta::ProcedureStateResponse;
use async_trait::async_trait; use async_trait::async_trait;
use common_base::AffectedRows;
use common_meta::rpc::procedure::{MigrateRegionRequest, ProcedureStateResponse};
use common_query::error::Result; use common_query::error::Result;
use session::context::QueryContextRef; use session::context::QueryContextRef;
use store_api::storage::RegionId; use table::requests::{DeleteRequest, InsertRequest};
use table::requests::{CompactTableRequest, DeleteRequest, FlushTableRequest, InsertRequest};
pub type AffectedRows = usize;
/// A trait for handling table mutations in `QueryEngine`. /// A trait for handling table mutations in `QueryEngine`.
#[async_trait] #[async_trait]
@@ -31,39 +32,23 @@ pub trait TableMutationHandler: Send + Sync {
/// Delete rows from the table. /// Delete rows from the table.
async fn delete(&self, request: DeleteRequest, ctx: QueryContextRef) -> Result<AffectedRows>; async fn delete(&self, request: DeleteRequest, ctx: QueryContextRef) -> Result<AffectedRows>;
/// Trigger a flush task for table. /// Migrate a region from source peer to target peer, returns the procedure id if success.
async fn flush(&self, request: FlushTableRequest, ctx: QueryContextRef) async fn migrate_region(
-> Result<AffectedRows>;
/// Trigger a compaction task for table.
async fn compact(
&self, &self,
request: CompactTableRequest, region_id: u64,
ctx: QueryContextRef, from_peer: u64,
) -> Result<AffectedRows>; to_peer: u64,
replay_timeout: Duration,
/// Trigger a flush task for a table region. ) -> Result<String>;
async fn flush_region(&self, region_id: RegionId, ctx: QueryContextRef)
-> Result<AffectedRows>;
/// Trigger a compaction task for a table region.
async fn compact_region(
&self,
region_id: RegionId,
ctx: QueryContextRef,
) -> Result<AffectedRows>;
} }
/// A trait for handling procedure service requests in `QueryEngine`. /// A trait for handling meta service requests in `QueryEngine`.
#[async_trait] #[async_trait]
pub trait ProcedureServiceHandler: Send + Sync { pub trait MetaServiceHandler: Send + Sync {
/// Migrate a region from source peer to target peer, returns the procedure id if success.
async fn migrate_region(&self, request: MigrateRegionRequest) -> Result<Option<String>>;
/// Query the procedure' state by its id /// Query the procedure' state by its id
async fn query_procedure_state(&self, pid: &str) -> Result<ProcedureStateResponse>; async fn query_procedure_state(&self, pid: &str) -> Result<ProcedureStateResponse>;
} }
pub type TableMutationHandlerRef = Arc<dyn TableMutationHandler>; pub type TableMutationHandlerRef = Arc<dyn TableMutationHandler>;
pub type ProcedureServiceHandlerRef = Arc<dyn ProcedureServiceHandler>; pub type MetaServiceHandlerRef = Arc<dyn MetaServiceHandler>;

View File

@@ -12,12 +12,8 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use common_query::error::{InvalidInputTypeSnafu, Result};
use common_query::prelude::{Signature, TypeSignature, Volatility}; use common_query::prelude::{Signature, TypeSignature, Volatility};
use datatypes::prelude::ConcreteDataType; use datatypes::prelude::ConcreteDataType;
use datatypes::types::cast::cast;
use datatypes::value::ValueRef;
use snafu::ResultExt;
/// Create a function signature with oneof signatures of interleaving two arguments. /// Create a function signature with oneof signatures of interleaving two arguments.
pub fn one_of_sigs2(args1: Vec<ConcreteDataType>, args2: Vec<ConcreteDataType>) -> Signature { pub fn one_of_sigs2(args1: Vec<ConcreteDataType>, args2: Vec<ConcreteDataType>) -> Signature {
@@ -31,15 +27,3 @@ pub fn one_of_sigs2(args1: Vec<ConcreteDataType>, args2: Vec<ConcreteDataType>)
Signature::one_of(sigs, Volatility::Immutable) Signature::one_of(sigs, Volatility::Immutable)
} }
/// Cast a [`ValueRef`] to u64, returns `None` if fails
pub fn cast_u64(value: &ValueRef) -> Result<Option<u64>> {
cast((*value).into(), &ConcreteDataType::uint64_datatype())
.context(InvalidInputTypeSnafu {
err_msg: format!(
"Failed to cast input into uint64, actual type: {:#?}",
value.data_type(),
),
})
.map(|v| v.as_u64())
}

View File

@@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
mod macros;
pub mod scalars; pub mod scalars;
mod system; mod system;
mod table; mod table;

View File

@@ -1,27 +0,0 @@
// Copyright 2023 Greptime Team
//
// Licensed 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.
/// Ensure current function is invokded under `greptime` catalog.
#[macro_export]
macro_rules! ensure_greptime {
($func_ctx: expr) => {{
use common_catalog::consts::DEFAULT_CATALOG_NAME;
snafu::ensure!(
$func_ctx.query_ctx.current_catalog() == DEFAULT_CATALOG_NAME,
common_query::error::PermissionDeniedSnafu {
err_msg: format!("current catalog is not {DEFAULT_CATALOG_NAME}")
}
);
}};
}

View File

@@ -14,22 +14,8 @@
mod binary; mod binary;
mod ctx; mod ctx;
mod is_null;
mod unary; mod unary;
use std::sync::Arc;
pub use binary::scalar_binary_op; pub use binary::scalar_binary_op;
pub use ctx::EvalContext; pub use ctx::EvalContext;
pub use unary::scalar_unary_op; pub use unary::scalar_unary_op;
use crate::function_registry::FunctionRegistry;
use crate::scalars::expression::is_null::IsNullFunction;
pub(crate) struct ExpressionFunction;
impl ExpressionFunction {
pub fn register(registry: &FunctionRegistry) {
registry.register(Arc::new(IsNullFunction));
}
}

View File

@@ -1,109 +0,0 @@
// Copyright 2023 Greptime Team
//
// Licensed 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.
use std::fmt;
use std::fmt::Display;
use std::sync::Arc;
use common_query::error;
use common_query::error::{ArrowComputeSnafu, InvalidFuncArgsSnafu};
use common_query::prelude::{Signature, Volatility};
use datafusion::arrow::array::ArrayRef;
use datafusion::arrow::compute::is_null;
use datatypes::data_type::ConcreteDataType;
use datatypes::prelude::VectorRef;
use datatypes::vectors::Helper;
use snafu::{ensure, ResultExt};
use crate::function::{Function, FunctionContext};
const NAME: &str = "isnull";
/// The function to check whether an expression is NULL
#[derive(Clone, Debug, Default)]
pub struct IsNullFunction;
impl Display for IsNullFunction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", NAME.to_ascii_uppercase())
}
}
impl Function for IsNullFunction {
fn name(&self) -> &str {
NAME
}
fn return_type(&self, _: &[ConcreteDataType]) -> common_query::error::Result<ConcreteDataType> {
Ok(ConcreteDataType::boolean_datatype())
}
fn signature(&self) -> Signature {
Signature::any(1, Volatility::Immutable)
}
fn eval(
&self,
_func_ctx: FunctionContext,
columns: &[VectorRef],
) -> common_query::error::Result<VectorRef> {
ensure!(
columns.len() == 1,
InvalidFuncArgsSnafu {
err_msg: format!(
"The length of the args is not correct, expect exactly one, have: {}",
columns.len()
),
}
);
let values = &columns[0];
let arrow_array = &values.to_arrow_array();
let result = is_null(arrow_array).context(ArrowComputeSnafu)?;
Helper::try_into_vector(Arc::new(result) as ArrayRef).context(error::FromArrowArraySnafu)
}
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use common_query::prelude::TypeSignature;
use datatypes::scalars::ScalarVector;
use datatypes::vectors::{BooleanVector, Float32Vector};
use super::*;
#[test]
fn test_is_null_function() {
let is_null = IsNullFunction;
assert_eq!("isnull", is_null.name());
assert_eq!(
ConcreteDataType::boolean_datatype(),
is_null.return_type(&[]).unwrap()
);
assert_eq!(
is_null.signature(),
Signature {
type_signature: TypeSignature::Any(1),
volatility: Volatility::Immutable
}
);
let values = vec![None, Some(3.0), None];
let args: Vec<VectorRef> = vec![Arc::new(Float32Vector::from(values))];
let vector = is_null.eval(FunctionContext::default(), &args).unwrap();
let expect: VectorRef = Arc::new(BooleanVector::from_vec(vec![true, false, true]));
assert_eq!(expect, vector);
}
}

View File

@@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
mod clamp;
mod modulo; mod modulo;
mod pow; mod pow;
mod rate; mod rate;
@@ -20,7 +19,6 @@ mod rate;
use std::fmt; use std::fmt;
use std::sync::Arc; use std::sync::Arc;
pub use clamp::ClampFunction;
use common_query::error::{GeneralDataFusionSnafu, Result}; use common_query::error::{GeneralDataFusionSnafu, Result};
use common_query::prelude::Signature; use common_query::prelude::Signature;
use datafusion::error::DataFusionError; use datafusion::error::DataFusionError;
@@ -42,8 +40,7 @@ impl MathFunction {
registry.register(Arc::new(ModuloFunction)); registry.register(Arc::new(ModuloFunction));
registry.register(Arc::new(PowFunction)); registry.register(Arc::new(PowFunction));
registry.register(Arc::new(RateFunction)); registry.register(Arc::new(RateFunction));
registry.register(Arc::new(RangeFunction)); registry.register(Arc::new(RangeFunction))
registry.register(Arc::new(ClampFunction));
} }
} }

View File

@@ -1,403 +0,0 @@
// Copyright 2023 Greptime Team
//
// Licensed 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.
use std::fmt::{self, Display};
use std::sync::Arc;
use common_query::error::{InvalidFuncArgsSnafu, Result};
use common_query::prelude::Signature;
use datafusion::arrow::array::{ArrayIter, PrimitiveArray};
use datafusion::logical_expr::Volatility;
use datatypes::data_type::{ConcreteDataType, DataType};
use datatypes::prelude::VectorRef;
use datatypes::types::LogicalPrimitiveType;
use datatypes::value::TryAsPrimitive;
use datatypes::vectors::PrimitiveVector;
use datatypes::with_match_primitive_type_id;
use snafu::{ensure, OptionExt};
use crate::function::Function;
#[derive(Clone, Debug, Default)]
pub struct ClampFunction;
const CLAMP_NAME: &str = "clamp";
impl Function for ClampFunction {
fn name(&self) -> &str {
CLAMP_NAME
}
fn return_type(&self, input_types: &[ConcreteDataType]) -> Result<ConcreteDataType> {
// Type check is done by `signature`
Ok(input_types[0].clone())
}
fn signature(&self) -> Signature {
// input, min, max
Signature::uniform(3, ConcreteDataType::numerics(), Volatility::Immutable)
}
fn eval(
&self,
_func_ctx: crate::function::FunctionContext,
columns: &[VectorRef],
) -> Result<VectorRef> {
ensure!(
columns.len() == 3,
InvalidFuncArgsSnafu {
err_msg: format!(
"The length of the args is not correct, expect exactly 3, have: {}",
columns.len()
),
}
);
ensure!(
columns[0].data_type().is_numeric(),
InvalidFuncArgsSnafu {
err_msg: format!(
"The first arg's type is not numeric, have: {}",
columns[0].data_type()
),
}
);
ensure!(
columns[0].data_type() == columns[1].data_type()
&& columns[1].data_type() == columns[2].data_type(),
InvalidFuncArgsSnafu {
err_msg: format!(
"Arguments don't have identical types: {}, {}, {}",
columns[0].data_type(),
columns[1].data_type(),
columns[2].data_type()
),
}
);
ensure!(
columns[1].len() == 1 && columns[2].len() == 1,
InvalidFuncArgsSnafu {
err_msg: format!(
"The second and third args should be scalar, have: {:?}, {:?}",
columns[1], columns[2]
),
}
);
with_match_primitive_type_id!(columns[0].data_type().logical_type_id(), |$S| {
let input_array = columns[0].to_arrow_array();
let input = input_array
.as_any()
.downcast_ref::<PrimitiveArray<<$S as LogicalPrimitiveType>::ArrowPrimitive>>()
.unwrap();
let min = TryAsPrimitive::<$S>::try_as_primitive(&columns[1].get(0))
.with_context(|| {
InvalidFuncArgsSnafu {
err_msg: "The second arg should not be none",
}
})?;
let max = TryAsPrimitive::<$S>::try_as_primitive(&columns[2].get(0))
.with_context(|| {
InvalidFuncArgsSnafu {
err_msg: "The third arg should not be none",
}
})?;
// ensure min <= max
ensure!(
min <= max,
InvalidFuncArgsSnafu {
err_msg: format!(
"The second arg should be less than or equal to the third arg, have: {:?}, {:?}",
columns[1], columns[2]
),
}
);
clamp_impl::<$S, true, true>(input, min, max)
},{
unreachable!()
})
}
}
impl Display for ClampFunction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", CLAMP_NAME.to_ascii_uppercase())
}
}
fn clamp_impl<T: LogicalPrimitiveType, const CLAMP_MIN: bool, const CLAMP_MAX: bool>(
input: &PrimitiveArray<T::ArrowPrimitive>,
min: T::Native,
max: T::Native,
) -> Result<VectorRef> {
common_telemetry::info!("[DEBUG] min {min:?}, max {max:?}");
let iter = ArrayIter::new(input);
let result = iter.map(|x| {
x.map(|x| {
if CLAMP_MIN && x < min {
min
} else if CLAMP_MAX && x > max {
max
} else {
x
}
})
});
let result = PrimitiveArray::<T::ArrowPrimitive>::from_iter(result);
Ok(Arc::new(PrimitiveVector::<T>::from(result)))
}
#[cfg(test)]
mod test {
use std::sync::Arc;
use datatypes::prelude::ScalarVector;
use datatypes::vectors::{
ConstantVector, Float64Vector, Int64Vector, StringVector, UInt64Vector,
};
use super::*;
use crate::function::FunctionContext;
#[test]
fn clamp_i64() {
let inputs = [
(
vec![Some(-3), Some(-2), Some(-1), Some(0), Some(1), Some(2)],
-1,
10,
vec![Some(-1), Some(-1), Some(-1), Some(0), Some(1), Some(2)],
),
(
vec![Some(-3), Some(-2), Some(-1), Some(0), Some(1), Some(2)],
0,
0,
vec![Some(0), Some(0), Some(0), Some(0), Some(0), Some(0)],
),
(
vec![Some(-3), None, Some(-1), None, None, Some(2)],
-2,
1,
vec![Some(-2), None, Some(-1), None, None, Some(1)],
),
(
vec![None, None, None, None, None],
0,
1,
vec![None, None, None, None, None],
),
];
let func = ClampFunction;
for (in_data, min, max, expected) in inputs {
let args = [
Arc::new(Int64Vector::from(in_data)) as _,
Arc::new(Int64Vector::from_vec(vec![min])) as _,
Arc::new(Int64Vector::from_vec(vec![max])) as _,
];
let result = func
.eval(FunctionContext::default(), args.as_slice())
.unwrap();
let expected: VectorRef = Arc::new(Int64Vector::from(expected));
assert_eq!(expected, result);
}
}
#[test]
fn clamp_u64() {
let inputs = [
(
vec![Some(0), Some(1), Some(2), Some(3), Some(4), Some(5)],
1,
3,
vec![Some(1), Some(1), Some(2), Some(3), Some(3), Some(3)],
),
(
vec![Some(0), Some(1), Some(2), Some(3), Some(4), Some(5)],
0,
0,
vec![Some(0), Some(0), Some(0), Some(0), Some(0), Some(0)],
),
(
vec![Some(0), None, Some(2), None, None, Some(5)],
1,
3,
vec![Some(1), None, Some(2), None, None, Some(3)],
),
(
vec![None, None, None, None, None],
0,
1,
vec![None, None, None, None, None],
),
];
let func = ClampFunction;
for (in_data, min, max, expected) in inputs {
let args = [
Arc::new(UInt64Vector::from(in_data)) as _,
Arc::new(UInt64Vector::from_vec(vec![min])) as _,
Arc::new(UInt64Vector::from_vec(vec![max])) as _,
];
let result = func
.eval(FunctionContext::default(), args.as_slice())
.unwrap();
let expected: VectorRef = Arc::new(UInt64Vector::from(expected));
assert_eq!(expected, result);
}
}
#[test]
fn clamp_f64() {
let inputs = [
(
vec![Some(-3.0), Some(-2.0), Some(-1.0), Some(0.0), Some(1.0)],
-1.0,
10.0,
vec![Some(-1.0), Some(-1.0), Some(-1.0), Some(0.0), Some(1.0)],
),
(
vec![Some(-2.0), Some(-1.0), Some(0.0), Some(1.0)],
0.0,
0.0,
vec![Some(0.0), Some(0.0), Some(0.0), Some(0.0)],
),
(
vec![Some(-3.0), None, Some(-1.0), None, None, Some(2.0)],
-2.0,
1.0,
vec![Some(-2.0), None, Some(-1.0), None, None, Some(1.0)],
),
(
vec![None, None, None, None, None],
0.0,
1.0,
vec![None, None, None, None, None],
),
];
let func = ClampFunction;
for (in_data, min, max, expected) in inputs {
let args = [
Arc::new(Float64Vector::from(in_data)) as _,
Arc::new(Float64Vector::from_vec(vec![min])) as _,
Arc::new(Float64Vector::from_vec(vec![max])) as _,
];
let result = func
.eval(FunctionContext::default(), args.as_slice())
.unwrap();
let expected: VectorRef = Arc::new(Float64Vector::from(expected));
assert_eq!(expected, result);
}
}
#[test]
fn clamp_const_i32() {
let input = vec![Some(5)];
let min = 2;
let max = 4;
let func = ClampFunction;
let args = [
Arc::new(ConstantVector::new(Arc::new(Int64Vector::from(input)), 1)) as _,
Arc::new(Int64Vector::from_vec(vec![min])) as _,
Arc::new(Int64Vector::from_vec(vec![max])) as _,
];
let result = func
.eval(FunctionContext::default(), args.as_slice())
.unwrap();
let expected: VectorRef = Arc::new(Int64Vector::from(vec![Some(4)]));
assert_eq!(expected, result);
}
#[test]
fn clamp_invalid_min_max() {
let input = vec![Some(-3.0), Some(-2.0), Some(-1.0), Some(0.0), Some(1.0)];
let min = 10.0;
let max = -1.0;
let func = ClampFunction;
let args = [
Arc::new(Float64Vector::from(input)) as _,
Arc::new(Float64Vector::from_vec(vec![min])) as _,
Arc::new(Float64Vector::from_vec(vec![max])) as _,
];
let result = func.eval(FunctionContext::default(), args.as_slice());
assert!(result.is_err());
}
#[test]
fn clamp_type_not_match() {
let input = vec![Some(-3.0), Some(-2.0), Some(-1.0), Some(0.0), Some(1.0)];
let min = -1;
let max = 10;
let func = ClampFunction;
let args = [
Arc::new(Float64Vector::from(input)) as _,
Arc::new(Int64Vector::from_vec(vec![min])) as _,
Arc::new(UInt64Vector::from_vec(vec![max])) as _,
];
let result = func.eval(FunctionContext::default(), args.as_slice());
assert!(result.is_err());
}
#[test]
fn clamp_min_is_not_scalar() {
let input = vec![Some(-3.0), Some(-2.0), Some(-1.0), Some(0.0), Some(1.0)];
let min = -10.0;
let max = 1.0;
let func = ClampFunction;
let args = [
Arc::new(Float64Vector::from(input)) as _,
Arc::new(Float64Vector::from_vec(vec![min, min])) as _,
Arc::new(Float64Vector::from_vec(vec![max])) as _,
];
let result = func.eval(FunctionContext::default(), args.as_slice());
assert!(result.is_err());
}
#[test]
fn clamp_no_max() {
let input = vec![Some(-3.0), Some(-2.0), Some(-1.0), Some(0.0), Some(1.0)];
let min = -10.0;
let func = ClampFunction;
let args = [
Arc::new(Float64Vector::from(input)) as _,
Arc::new(Float64Vector::from_vec(vec![min])) as _,
];
let result = func.eval(FunctionContext::default(), args.as_slice());
assert!(result.is_err());
}
#[test]
fn clamp_on_string() {
let input = vec![Some("foo"), Some("foo"), Some("foo"), Some("foo")];
let func = ClampFunction;
let args = [
Arc::new(StringVector::from(input)) as _,
Arc::new(StringVector::from_vec(vec!["bar"])) as _,
Arc::new(StringVector::from_vec(vec!["baz"])) as _,
];
let result = func.eval(FunctionContext::default(), args.as_slice());
assert!(result.is_err());
}
}

View File

@@ -128,7 +128,7 @@ mod tests {
]; ];
let result = function.eval(FunctionContext::default(), &args).unwrap(); let result = function.eval(FunctionContext::default(), &args).unwrap();
assert_eq!(result.len(), 4); assert_eq!(result.len(), 4);
for i in 0..4 { for i in 0..3 {
let p: i64 = (nums[i] % divs[i]) as i64; let p: i64 = (nums[i] % divs[i]) as i64;
assert!(matches!(result.get(i), Value::Int64(v) if v == p)); assert!(matches!(result.get(i), Value::Int64(v) if v == p));
} }
@@ -160,7 +160,7 @@ mod tests {
]; ];
let result = function.eval(FunctionContext::default(), &args).unwrap(); let result = function.eval(FunctionContext::default(), &args).unwrap();
assert_eq!(result.len(), 4); assert_eq!(result.len(), 4);
for i in 0..4 { for i in 0..3 {
let p: u64 = (nums[i] % divs[i]) as u64; let p: u64 = (nums[i] % divs[i]) as u64;
assert!(matches!(result.get(i), Value::UInt64(v) if v == p)); assert!(matches!(result.get(i), Value::UInt64(v) if v == p));
} }
@@ -192,7 +192,7 @@ mod tests {
]; ];
let result = function.eval(FunctionContext::default(), &args).unwrap(); let result = function.eval(FunctionContext::default(), &args).unwrap();
assert_eq!(result.len(), 4); assert_eq!(result.len(), 4);
for i in 0..4 { for i in 0..3 {
let p: f64 = nums[i] % divs[i]; let p: f64 = nums[i] % divs[i];
assert!(matches!(result.get(i), Value::Float64(v) if v == p)); assert!(matches!(result.get(i), Value::Float64(v) if v == p));
} }

View File

@@ -14,11 +14,9 @@
use std::sync::Arc; use std::sync::Arc;
mod greatest; mod greatest;
mod to_timezone;
mod to_unixtime; mod to_unixtime;
use greatest::GreatestFunction; use greatest::GreatestFunction;
use to_timezone::ToTimezoneFunction;
use to_unixtime::ToUnixtimeFunction; use to_unixtime::ToUnixtimeFunction;
use crate::function_registry::FunctionRegistry; use crate::function_registry::FunctionRegistry;
@@ -27,7 +25,6 @@ pub(crate) struct TimestampFunction;
impl TimestampFunction { impl TimestampFunction {
pub fn register(registry: &FunctionRegistry) { pub fn register(registry: &FunctionRegistry) {
registry.register(Arc::new(ToTimezoneFunction));
registry.register(Arc::new(ToUnixtimeFunction)); registry.register(Arc::new(ToUnixtimeFunction));
registry.register(Arc::new(GreatestFunction)); registry.register(Arc::new(GreatestFunction));
} }

View File

@@ -1,260 +0,0 @@
// Copyright 2023 Greptime Team
//
// Licensed 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.
use std::fmt;
use std::sync::Arc;
use common_query::error::{InvalidFuncArgsSnafu, Result, UnsupportedInputDataTypeSnafu};
use common_query::prelude::Signature;
use common_time::{Timestamp, Timezone};
use datatypes::data_type::ConcreteDataType;
use datatypes::prelude::VectorRef;
use datatypes::types::TimestampType;
use datatypes::value::Value;
use datatypes::vectors::{
StringVector, TimestampMicrosecondVector, TimestampMillisecondVector,
TimestampNanosecondVector, TimestampSecondVector, Vector,
};
use snafu::{ensure, OptionExt};
use crate::function::{Function, FunctionContext};
use crate::helper;
#[derive(Clone, Debug, Default)]
pub struct ToTimezoneFunction;
const NAME: &str = "to_timezone";
fn convert_to_timezone(arg: &str) -> Option<Timezone> {
Timezone::from_tz_string(arg).ok()
}
fn convert_to_timestamp(arg: &Value) -> Option<Timestamp> {
match arg {
Value::Timestamp(ts) => Some(*ts),
_ => None,
}
}
impl fmt::Display for ToTimezoneFunction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "TO_TIMEZONE")
}
}
impl Function for ToTimezoneFunction {
fn name(&self) -> &str {
NAME
}
fn return_type(&self, input_types: &[ConcreteDataType]) -> Result<ConcreteDataType> {
// type checked by signature - MUST BE timestamp
Ok(input_types[0].clone())
}
fn signature(&self) -> Signature {
helper::one_of_sigs2(
vec![
ConcreteDataType::timestamp_second_datatype(),
ConcreteDataType::timestamp_millisecond_datatype(),
ConcreteDataType::timestamp_microsecond_datatype(),
ConcreteDataType::timestamp_nanosecond_datatype(),
],
vec![ConcreteDataType::string_datatype()],
)
}
fn eval(&self, _ctx: FunctionContext, columns: &[VectorRef]) -> Result<VectorRef> {
ensure!(
columns.len() == 2,
InvalidFuncArgsSnafu {
err_msg: format!(
"The length of the args is not correct, expect exactly 2, have: {}",
columns.len()
),
}
);
// TODO: maybe support epoch timestamp? https://github.com/GreptimeTeam/greptimedb/issues/3477
let ts = columns[0].data_type().as_timestamp().with_context(|| {
UnsupportedInputDataTypeSnafu {
function: NAME,
datatypes: columns.iter().map(|c| c.data_type()).collect::<Vec<_>>(),
}
})?;
let array = columns[0].to_arrow_array();
let times = match ts {
TimestampType::Second(_) => {
let vector = TimestampSecondVector::try_from_arrow_array(array).unwrap();
(0..vector.len())
.map(|i| convert_to_timestamp(&vector.get(i)))
.collect::<Vec<_>>()
}
TimestampType::Millisecond(_) => {
let vector = TimestampMillisecondVector::try_from_arrow_array(array).unwrap();
(0..vector.len())
.map(|i| convert_to_timestamp(&vector.get(i)))
.collect::<Vec<_>>()
}
TimestampType::Microsecond(_) => {
let vector = TimestampMicrosecondVector::try_from_arrow_array(array).unwrap();
(0..vector.len())
.map(|i| convert_to_timestamp(&vector.get(i)))
.collect::<Vec<_>>()
}
TimestampType::Nanosecond(_) => {
let vector = TimestampNanosecondVector::try_from_arrow_array(array).unwrap();
(0..vector.len())
.map(|i| convert_to_timestamp(&vector.get(i)))
.collect::<Vec<_>>()
}
};
let tzs = {
let array = columns[1].to_arrow_array();
let vector = StringVector::try_from_arrow_array(&array)
.ok()
.with_context(|| UnsupportedInputDataTypeSnafu {
function: NAME,
datatypes: columns.iter().map(|c| c.data_type()).collect::<Vec<_>>(),
})?;
(0..vector.len())
.map(|i| convert_to_timezone(&vector.get(i).to_string()))
.collect::<Vec<_>>()
};
let result = times
.iter()
.zip(tzs.iter())
.map(|(time, tz)| match (time, tz) {
(Some(time), _) => Some(time.to_timezone_aware_string(tz.as_ref())),
_ => None,
})
.collect::<Vec<Option<String>>>();
Ok(Arc::new(StringVector::from(result)))
}
}
#[cfg(test)]
mod tests {
use datatypes::scalars::ScalarVector;
use datatypes::timestamp::{
TimestampMicrosecond, TimestampMillisecond, TimestampNanosecond, TimestampSecond,
};
use datatypes::vectors::StringVector;
use super::*;
#[test]
fn test_timestamp_to_timezone() {
let f = ToTimezoneFunction;
assert_eq!("to_timezone", f.name());
let results = vec![
Some("1969-12-31 19:00:01"),
None,
Some("1970-01-01 03:00:01"),
None,
];
let times: Vec<Option<TimestampSecond>> = vec![
Some(TimestampSecond::new(1)),
None,
Some(TimestampSecond::new(1)),
None,
];
let ts_vector: TimestampSecondVector =
TimestampSecondVector::from_owned_iterator(times.into_iter());
let tzs = vec![Some("America/New_York"), None, Some("Europe/Moscow"), None];
let args: Vec<VectorRef> = vec![
Arc::new(ts_vector),
Arc::new(StringVector::from(tzs.clone())),
];
let vector = f.eval(FunctionContext::default(), &args).unwrap();
assert_eq!(4, vector.len());
let expect_times: VectorRef = Arc::new(StringVector::from(results));
assert_eq!(expect_times, vector);
let results = vec![
Some("1969-12-31 19:00:00.001"),
None,
Some("1970-01-01 03:00:00.001"),
None,
];
let times: Vec<Option<TimestampMillisecond>> = vec![
Some(TimestampMillisecond::new(1)),
None,
Some(TimestampMillisecond::new(1)),
None,
];
let ts_vector: TimestampMillisecondVector =
TimestampMillisecondVector::from_owned_iterator(times.into_iter());
let args: Vec<VectorRef> = vec![
Arc::new(ts_vector),
Arc::new(StringVector::from(tzs.clone())),
];
let vector = f.eval(FunctionContext::default(), &args).unwrap();
assert_eq!(4, vector.len());
let expect_times: VectorRef = Arc::new(StringVector::from(results));
assert_eq!(expect_times, vector);
let results = vec![
Some("1969-12-31 19:00:00.000001"),
None,
Some("1970-01-01 03:00:00.000001"),
None,
];
let times: Vec<Option<TimestampMicrosecond>> = vec![
Some(TimestampMicrosecond::new(1)),
None,
Some(TimestampMicrosecond::new(1)),
None,
];
let ts_vector: TimestampMicrosecondVector =
TimestampMicrosecondVector::from_owned_iterator(times.into_iter());
let args: Vec<VectorRef> = vec![
Arc::new(ts_vector),
Arc::new(StringVector::from(tzs.clone())),
];
let vector = f.eval(FunctionContext::default(), &args).unwrap();
assert_eq!(4, vector.len());
let expect_times: VectorRef = Arc::new(StringVector::from(results));
assert_eq!(expect_times, vector);
let results = vec![
Some("1969-12-31 19:00:00.000000001"),
None,
Some("1970-01-01 03:00:00.000000001"),
None,
];
let times: Vec<Option<TimestampNanosecond>> = vec![
Some(TimestampNanosecond::new(1)),
None,
Some(TimestampNanosecond::new(1)),
None,
];
let ts_vector: TimestampNanosecondVector =
TimestampNanosecondVector::from_owned_iterator(times.into_iter());
let args: Vec<VectorRef> = vec![
Arc::new(ts_vector),
Arc::new(StringVector::from(tzs.clone())),
];
let vector = f.eval(FunctionContext::default(), &args).unwrap();
assert_eq!(4, vector.len());
let expect_times: VectorRef = Arc::new(StringVector::from(results));
assert_eq!(expect_times, vector);
}
}

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use crate::handlers::{ProcedureServiceHandlerRef, TableMutationHandlerRef}; use crate::handlers::{MetaServiceHandlerRef, TableMutationHandlerRef};
/// Shared state for SQL functions. /// Shared state for SQL functions.
/// The handlers in state may be `None` in cli command-line or test cases. /// The handlers in state may be `None` in cli command-line or test cases.
@@ -20,104 +20,6 @@ use crate::handlers::{ProcedureServiceHandlerRef, TableMutationHandlerRef};
pub struct FunctionState { pub struct FunctionState {
// The table mutation handler // The table mutation handler
pub table_mutation_handler: Option<TableMutationHandlerRef>, pub table_mutation_handler: Option<TableMutationHandlerRef>,
// The procedure service handler // The meta service handler
pub procedure_service_handler: Option<ProcedureServiceHandlerRef>, pub meta_service_handler: Option<MetaServiceHandlerRef>,
}
impl FunctionState {
/// Create a mock [`FunctionState`] for test.
#[cfg(any(test, feature = "testing"))]
pub fn mock() -> Self {
use std::sync::Arc;
use api::v1::meta::ProcedureStatus;
use async_trait::async_trait;
use common_base::AffectedRows;
use common_meta::rpc::procedure::{MigrateRegionRequest, ProcedureStateResponse};
use common_query::error::Result;
use session::context::QueryContextRef;
use store_api::storage::RegionId;
use table::requests::{
CompactTableRequest, DeleteRequest, FlushTableRequest, InsertRequest,
};
use crate::handlers::{ProcedureServiceHandler, TableMutationHandler};
struct MockProcedureServiceHandler;
struct MockTableMutationHandler;
const ROWS: usize = 42;
#[async_trait]
impl ProcedureServiceHandler for MockProcedureServiceHandler {
async fn migrate_region(
&self,
_request: MigrateRegionRequest,
) -> Result<Option<String>> {
Ok(Some("test_pid".to_string()))
}
async fn query_procedure_state(&self, _pid: &str) -> Result<ProcedureStateResponse> {
Ok(ProcedureStateResponse {
status: ProcedureStatus::Done.into(),
error: "OK".to_string(),
..Default::default()
})
}
}
#[async_trait]
impl TableMutationHandler for MockTableMutationHandler {
async fn insert(
&self,
_request: InsertRequest,
_ctx: QueryContextRef,
) -> Result<AffectedRows> {
Ok(ROWS)
}
async fn delete(
&self,
_request: DeleteRequest,
_ctx: QueryContextRef,
) -> Result<AffectedRows> {
Ok(ROWS)
}
async fn flush(
&self,
_request: FlushTableRequest,
_ctx: QueryContextRef,
) -> Result<AffectedRows> {
Ok(ROWS)
}
async fn compact(
&self,
_request: CompactTableRequest,
_ctx: QueryContextRef,
) -> Result<AffectedRows> {
Ok(ROWS)
}
async fn flush_region(
&self,
_region_id: RegionId,
_ctx: QueryContextRef,
) -> Result<AffectedRows> {
Ok(ROWS)
}
async fn compact_region(
&self,
_region_id: RegionId,
_ctx: QueryContextRef,
) -> Result<AffectedRows> {
Ok(ROWS)
}
}
Self {
table_mutation_handler: Some(Arc::new(MockTableMutationHandler)),
procedure_service_handler: Some(Arc::new(MockProcedureServiceHandler)),
}
}
} }

View File

@@ -14,7 +14,6 @@
mod build; mod build;
mod database; mod database;
mod procedure_state;
mod timezone; mod timezone;
mod version; mod version;
@@ -22,7 +21,6 @@ use std::sync::Arc;
use build::BuildFunction; use build::BuildFunction;
use database::DatabaseFunction; use database::DatabaseFunction;
use procedure_state::ProcedureStateFunction;
use timezone::TimezoneFunction; use timezone::TimezoneFunction;
use version::VersionFunction; use version::VersionFunction;
@@ -36,6 +34,5 @@ impl SystemFunction {
registry.register(Arc::new(VersionFunction)); registry.register(Arc::new(VersionFunction));
registry.register(Arc::new(DatabaseFunction)); registry.register(Arc::new(DatabaseFunction));
registry.register(Arc::new(TimezoneFunction)); registry.register(Arc::new(TimezoneFunction));
registry.register(Arc::new(ProcedureStateFunction));
} }
} }

View File

@@ -22,7 +22,7 @@ use datatypes::vectors::{StringVector, VectorRef};
use crate::function::{Function, FunctionContext}; use crate::function::{Function, FunctionContext};
/// Generates build information /// Generates build information
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct BuildFunction; pub struct BuildFunction;
@@ -42,7 +42,11 @@ impl Function for BuildFunction {
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::uniform(0, vec![], Volatility::Immutable) Signature::uniform(
0,
vec![ConcreteDataType::string_datatype()],
Volatility::Immutable,
)
} }
fn eval(&self, _func_ctx: FunctionContext, _columns: &[VectorRef]) -> Result<VectorRef> { fn eval(&self, _func_ctx: FunctionContext, _columns: &[VectorRef]) -> Result<VectorRef> {
@@ -71,7 +75,7 @@ mod tests {
Signature { Signature {
type_signature: TypeSignature::Uniform(0, valid_types), type_signature: TypeSignature::Uniform(0, valid_types),
volatility: Volatility::Immutable volatility: Volatility::Immutable
} if valid_types.is_empty() } if valid_types == vec![ConcreteDataType::string_datatype()]
)); ));
let build_info = common_version::build_info().to_string(); let build_info = common_version::build_info().to_string();
let vector = build.eval(FunctionContext::default(), &[]).unwrap(); let vector = build.eval(FunctionContext::default(), &[]).unwrap();

View File

@@ -1,159 +0,0 @@
// Copyright 2023 Greptime Team
//
// Licensed 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.
use std::fmt;
use api::v1::meta::ProcedureStatus;
use common_macro::admin_fn;
use common_meta::rpc::procedure::ProcedureStateResponse;
use common_query::error::Error::ThreadJoin;
use common_query::error::{
InvalidFuncArgsSnafu, MissingProcedureServiceHandlerSnafu, Result,
UnsupportedInputDataTypeSnafu,
};
use common_query::prelude::{Signature, Volatility};
use common_telemetry::error;
use datatypes::prelude::*;
use datatypes::vectors::VectorRef;
use serde::Serialize;
use session::context::QueryContextRef;
use snafu::{ensure, Location, OptionExt};
use crate::ensure_greptime;
use crate::function::{Function, FunctionContext};
use crate::handlers::ProcedureServiceHandlerRef;
#[derive(Serialize)]
struct ProcedureStateJson {
status: String,
#[serde(skip_serializing_if = "Option::is_none")]
error: Option<String>,
}
/// A function to query procedure state by its id.
/// Such as `procedure_state(pid)`.
#[admin_fn(
name = "ProcedureStateFunction",
display_name = "procedure_state",
sig_fn = "signature",
ret = "string"
)]
pub(crate) async fn procedure_state(
procedure_service_handler: &ProcedureServiceHandlerRef,
_ctx: &QueryContextRef,
params: &[ValueRef<'_>],
) -> Result<Value> {
ensure!(
params.len() == 1,
InvalidFuncArgsSnafu {
err_msg: format!(
"The length of the args is not correct, expect 1, have: {}",
params.len()
),
}
);
let ValueRef::String(pid) = params[0] else {
return UnsupportedInputDataTypeSnafu {
function: "procedure_state",
datatypes: params.iter().map(|v| v.data_type()).collect::<Vec<_>>(),
}
.fail();
};
let ProcedureStateResponse { status, error, .. } =
procedure_service_handler.query_procedure_state(pid).await?;
let status = ProcedureStatus::try_from(status)
.map(|v| v.as_str_name())
.unwrap_or("Unknown");
let state = ProcedureStateJson {
status: status.to_string(),
error: if error.is_empty() { None } else { Some(error) },
};
let json = serde_json::to_string(&state).unwrap_or_default();
Ok(Value::from(json))
}
fn signature() -> Signature {
Signature::uniform(
1,
vec![ConcreteDataType::string_datatype()],
Volatility::Immutable,
)
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use common_query::prelude::TypeSignature;
use datatypes::vectors::StringVector;
use super::*;
#[test]
fn test_procedure_state_misc() {
let f = ProcedureStateFunction;
assert_eq!("procedure_state", f.name());
assert_eq!(
ConcreteDataType::string_datatype(),
f.return_type(&[]).unwrap()
);
assert!(matches!(f.signature(),
Signature {
type_signature: TypeSignature::Uniform(1, valid_types),
volatility: Volatility::Immutable
} if valid_types == vec![ConcreteDataType::string_datatype()]
));
}
#[test]
fn test_missing_procedure_service() {
let f = ProcedureStateFunction;
let args = vec!["pid"];
let args = args
.into_iter()
.map(|arg| Arc::new(StringVector::from_slice(&[arg])) as _)
.collect::<Vec<_>>();
let result = f.eval(FunctionContext::default(), &args).unwrap_err();
assert_eq!(
"Missing ProcedureServiceHandler, not expected",
result.to_string()
);
}
#[test]
fn test_procedure_state() {
let f = ProcedureStateFunction;
let args = vec!["pid"];
let args = args
.into_iter()
.map(|arg| Arc::new(StringVector::from_slice(&[arg])) as _)
.collect::<Vec<_>>();
let result = f.eval(FunctionContext::mock(), &args).unwrap();
let expect: VectorRef = Arc::new(StringVector::from(vec![
"{\"status\":\"Done\",\"error\":\"OK\"}",
]));
assert_eq!(expect, result);
}
}

View File

@@ -12,14 +12,10 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
mod flush_compact_region;
mod flush_compact_table;
mod migrate_region; mod migrate_region;
use std::sync::Arc; use std::sync::Arc;
use flush_compact_region::{CompactRegionFunction, FlushRegionFunction};
use flush_compact_table::{CompactTableFunction, FlushTableFunction};
use migrate_region::MigrateRegionFunction; use migrate_region::MigrateRegionFunction;
use crate::function_registry::FunctionRegistry; use crate::function_registry::FunctionRegistry;
@@ -31,9 +27,5 @@ impl TableFunction {
/// Register all table functions to [`FunctionRegistry`]. /// Register all table functions to [`FunctionRegistry`].
pub fn register(registry: &FunctionRegistry) { pub fn register(registry: &FunctionRegistry) {
registry.register(Arc::new(MigrateRegionFunction)); registry.register(Arc::new(MigrateRegionFunction));
registry.register(Arc::new(FlushRegionFunction));
registry.register(Arc::new(CompactRegionFunction));
registry.register(Arc::new(FlushTableFunction));
registry.register(Arc::new(CompactTableFunction));
} }
} }

View File

@@ -1,148 +0,0 @@
// Copyright 2023 Greptime Team
//
// Licensed 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.
use std::fmt;
use common_macro::admin_fn;
use common_query::error::Error::ThreadJoin;
use common_query::error::{
InvalidFuncArgsSnafu, MissingTableMutationHandlerSnafu, Result, UnsupportedInputDataTypeSnafu,
};
use common_query::prelude::{Signature, Volatility};
use common_telemetry::error;
use datatypes::prelude::*;
use datatypes::vectors::VectorRef;
use session::context::QueryContextRef;
use snafu::{ensure, Location, OptionExt};
use store_api::storage::RegionId;
use crate::ensure_greptime;
use crate::function::{Function, FunctionContext};
use crate::handlers::TableMutationHandlerRef;
use crate::helper::cast_u64;
macro_rules! define_region_function {
($name: expr, $display_name_str: expr, $display_name: ident) => {
/// A function to $display_name
#[admin_fn(name = $name, display_name = $display_name_str, sig_fn = "signature", ret = "uint64")]
pub(crate) async fn $display_name(
table_mutation_handler: &TableMutationHandlerRef,
query_ctx: &QueryContextRef,
params: &[ValueRef<'_>],
) -> Result<Value> {
ensure!(
params.len() == 1,
InvalidFuncArgsSnafu {
err_msg: format!(
"The length of the args is not correct, expect 1, have: {}",
params.len()
),
}
);
let Some(region_id) = cast_u64(&params[0])? else {
return UnsupportedInputDataTypeSnafu {
function: $display_name_str,
datatypes: params.iter().map(|v| v.data_type()).collect::<Vec<_>>(),
}
.fail();
};
let affected_rows = table_mutation_handler
.$display_name(RegionId::from_u64(region_id), query_ctx.clone())
.await?;
Ok(Value::from(affected_rows as u64))
}
};
}
define_region_function!("FlushRegionFunction", "flush_region", flush_region);
define_region_function!("CompactRegionFunction", "compact_region", compact_region);
fn signature() -> Signature {
Signature::uniform(1, ConcreteDataType::numerics(), Volatility::Immutable)
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use common_query::prelude::TypeSignature;
use datatypes::vectors::UInt64Vector;
use super::*;
macro_rules! define_region_function_test {
($name: ident, $func: ident) => {
paste::paste! {
#[test]
fn [<test_ $name _misc>]() {
let f = $func;
assert_eq!(stringify!($name), f.name());
assert_eq!(
ConcreteDataType::uint64_datatype(),
f.return_type(&[]).unwrap()
);
assert!(matches!(f.signature(),
Signature {
type_signature: TypeSignature::Uniform(1, valid_types),
volatility: Volatility::Immutable
} if valid_types == ConcreteDataType::numerics()));
}
#[test]
fn [<test_ $name _missing_table_mutation>]() {
let f = $func;
let args = vec![99];
let args = args
.into_iter()
.map(|arg| Arc::new(UInt64Vector::from_slice([arg])) as _)
.collect::<Vec<_>>();
let result = f.eval(FunctionContext::default(), &args).unwrap_err();
assert_eq!(
"Missing TableMutationHandler, not expected",
result.to_string()
);
}
#[test]
fn [<test_ $name>]() {
let f = $func;
let args = vec![99];
let args = args
.into_iter()
.map(|arg| Arc::new(UInt64Vector::from_slice([arg])) as _)
.collect::<Vec<_>>();
let result = f.eval(FunctionContext::mock(), &args).unwrap();
let expect: VectorRef = Arc::new(UInt64Vector::from_slice([42]));
assert_eq!(expect, result);
}
}
};
}
define_region_function_test!(flush_region, FlushRegionFunction);
define_region_function_test!(compact_region, CompactRegionFunction);
}

View File

@@ -1,178 +0,0 @@
// Copyright 2023 Greptime Team
//
// Licensed 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.
use std::fmt;
use common_error::ext::BoxedError;
use common_macro::admin_fn;
use common_query::error::Error::ThreadJoin;
use common_query::error::{
InvalidFuncArgsSnafu, MissingTableMutationHandlerSnafu, Result, TableMutationSnafu,
UnsupportedInputDataTypeSnafu,
};
use common_query::prelude::{Signature, Volatility};
use common_telemetry::error;
use datatypes::prelude::*;
use datatypes::vectors::VectorRef;
use session::context::QueryContextRef;
use session::table_name::table_name_to_full_name;
use snafu::{ensure, Location, OptionExt, ResultExt};
use table::requests::{CompactTableRequest, FlushTableRequest};
use crate::ensure_greptime;
use crate::function::{Function, FunctionContext};
use crate::handlers::TableMutationHandlerRef;
macro_rules! define_table_function {
($name: expr, $display_name_str: expr, $display_name: ident, $func: ident, $request: ident) => {
/// A function to $func table, such as `$display_name(table_name)`.
#[admin_fn(name = $name, display_name = $display_name_str, sig_fn = "signature", ret = "uint64")]
pub(crate) async fn $display_name(
table_mutation_handler: &TableMutationHandlerRef,
query_ctx: &QueryContextRef,
params: &[ValueRef<'_>],
) -> Result<Value> {
ensure!(
params.len() == 1,
InvalidFuncArgsSnafu {
err_msg: format!(
"The length of the args is not correct, expect 1, have: {}",
params.len()
),
}
);
let ValueRef::String(table_name) = params[0] else {
return UnsupportedInputDataTypeSnafu {
function: $display_name_str,
datatypes: params.iter().map(|v| v.data_type()).collect::<Vec<_>>(),
}
.fail();
};
let (catalog_name, schema_name, table_name) =
table_name_to_full_name(table_name, &query_ctx)
.map_err(BoxedError::new)
.context(TableMutationSnafu)?;
let affected_rows = table_mutation_handler
.$func(
$request {
catalog_name,
schema_name,
table_name,
},
query_ctx.clone(),
)
.await?;
Ok(Value::from(affected_rows as u64))
}
};
}
define_table_function!(
"FlushTableFunction",
"flush_table",
flush_table,
flush,
FlushTableRequest
);
define_table_function!(
"CompactTableFunction",
"compact_table",
compact_table,
compact,
CompactTableRequest
);
fn signature() -> Signature {
Signature::uniform(
1,
vec![ConcreteDataType::string_datatype()],
Volatility::Immutable,
)
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
use common_query::prelude::TypeSignature;
use datatypes::vectors::{StringVector, UInt64Vector};
use super::*;
macro_rules! define_table_function_test {
($name: ident, $func: ident) => {
paste::paste!{
#[test]
fn [<test_ $name _misc>]() {
let f = $func;
assert_eq!(stringify!($name), f.name());
assert_eq!(
ConcreteDataType::uint64_datatype(),
f.return_type(&[]).unwrap()
);
assert!(matches!(f.signature(),
Signature {
type_signature: TypeSignature::Uniform(1, valid_types),
volatility: Volatility::Immutable
} if valid_types == vec![ConcreteDataType::string_datatype()]));
}
#[test]
fn [<test_ $name _missing_table_mutation>]() {
let f = $func;
let args = vec!["test"];
let args = args
.into_iter()
.map(|arg| Arc::new(StringVector::from(vec![arg])) as _)
.collect::<Vec<_>>();
let result = f.eval(FunctionContext::default(), &args).unwrap_err();
assert_eq!(
"Missing TableMutationHandler, not expected",
result.to_string()
);
}
#[test]
fn [<test_ $name>]() {
let f = $func;
let args = vec!["test"];
let args = args
.into_iter()
.map(|arg| Arc::new(StringVector::from(vec![arg])) as _)
.collect::<Vec<_>>();
let result = f.eval(FunctionContext::mock(), &args).unwrap();
let expect: VectorRef = Arc::new(UInt64Vector::from_slice([42]));
assert_eq!(expect, result);
}
}
}
}
define_table_function_test!(flush_table, FlushTableFunction);
define_table_function_test!(compact_table, CompactTableFunction);
}

View File

@@ -15,25 +15,18 @@
use std::fmt::{self}; use std::fmt::{self};
use std::time::Duration; use std::time::Duration;
use common_macro::admin_fn;
use common_meta::rpc::procedure::MigrateRegionRequest;
use common_query::error::Error::ThreadJoin; use common_query::error::Error::ThreadJoin;
use common_query::error::{InvalidFuncArgsSnafu, MissingProcedureServiceHandlerSnafu, Result}; use common_query::error::{
InvalidFuncArgsSnafu, InvalidInputTypeSnafu, MissingTableMutationHandlerSnafu, Result,
};
use common_query::prelude::{Signature, TypeSignature, Volatility}; use common_query::prelude::{Signature, TypeSignature, Volatility};
use common_telemetry::logging::error; use common_telemetry::logging::error;
use datatypes::data_type::DataType; use datatypes::prelude::{ConcreteDataType, MutableVector, ScalarVectorBuilder};
use datatypes::prelude::ConcreteDataType; use datatypes::value::Value;
use datatypes::value::{Value, ValueRef}; use datatypes::vectors::{StringVectorBuilder, VectorRef};
use datatypes::vectors::VectorRef; use snafu::{Location, OptionExt, ResultExt};
use session::context::QueryContextRef;
use snafu::{Location, OptionExt};
use crate::ensure_greptime;
use crate::function::{Function, FunctionContext}; use crate::function::{Function, FunctionContext};
use crate::handlers::ProcedureServiceHandlerRef;
use crate::helper::cast_u64;
const DEFAULT_REPLAY_TIMEOUT_SECS: u64 = 10;
/// A function to migrate a region from source peer to target peer. /// A function to migrate a region from source peer to target peer.
/// Returns the submitted procedure id if success. Only available in cluster mode. /// Returns the submitted procedure id if success. Only available in cluster mode.
@@ -45,140 +38,138 @@ const DEFAULT_REPLAY_TIMEOUT_SECS: u64 = 10;
/// - `region_id`: the region id /// - `region_id`: the region id
/// - `from_peer`: the source peer id /// - `from_peer`: the source peer id
/// - `to_peer`: the target peer id /// - `to_peer`: the target peer id
#[admin_fn( #[derive(Clone, Debug, Default)]
name = "MigrateRegionFunction", pub struct MigrateRegionFunction;
display_name = "migrate_region",
sig_fn = "signature",
ret = "string"
)]
pub(crate) async fn migrate_region(
procedure_service_handler: &ProcedureServiceHandlerRef,
_ctx: &QueryContextRef,
params: &[ValueRef<'_>],
) -> Result<Value> {
let (region_id, from_peer, to_peer, replay_timeout) = match params.len() {
3 => {
let region_id = cast_u64(&params[0])?;
let from_peer = cast_u64(&params[1])?;
let to_peer = cast_u64(&params[2])?;
( const NAME: &str = "migrate_region";
region_id, const DEFAULT_REPLAY_TIMEOUT_SECS: u64 = 10;
from_peer,
to_peer,
Some(DEFAULT_REPLAY_TIMEOUT_SECS),
)
}
4 => { fn cast_u64_vector(vector: &VectorRef) -> Result<VectorRef> {
let region_id = cast_u64(&params[0])?; vector
let from_peer = cast_u64(&params[1])?; .cast(&ConcreteDataType::uint64_datatype())
let to_peer = cast_u64(&params[2])?; .context(InvalidInputTypeSnafu {
let replay_timeout = cast_u64(&params[3])?; err_msg: format!(
"Failed to cast input into uint64, actual type: {:#?}",
vector.data_type(),
),
})
}
(region_id, from_peer, to_peer, replay_timeout) impl Function for MigrateRegionFunction {
} fn name(&self) -> &str {
NAME
}
size => { fn return_type(&self, _input_types: &[ConcreteDataType]) -> Result<ConcreteDataType> {
return InvalidFuncArgsSnafu { Ok(ConcreteDataType::string_datatype())
err_msg: format!( }
"The length of the args is not correct, expect exactly 3 or 4, have: {}",
size fn signature(&self) -> Signature {
), Signature::one_of(
vec![
// migrate_region(region_id, from_peer, to_peer)
TypeSignature::Uniform(3, ConcreteDataType::numerics()),
// migrate_region(region_id, from_peer, to_peer, timeout(secs))
TypeSignature::Uniform(4, ConcreteDataType::numerics()),
],
Volatility::Immutable,
)
}
fn eval(&self, func_ctx: FunctionContext, columns: &[VectorRef]) -> Result<VectorRef> {
let (region_ids, from_peers, to_peers, replay_timeouts) = match columns.len() {
3 => {
let region_ids = cast_u64_vector(&columns[0])?;
let from_peers = cast_u64_vector(&columns[1])?;
let to_peers = cast_u64_vector(&columns[2])?;
(region_ids, from_peers, to_peers, None)
} }
.fail();
}
};
match (region_id, from_peer, to_peer, replay_timeout) { 4 => {
(Some(region_id), Some(from_peer), Some(to_peer), Some(replay_timeout)) => { let region_ids = cast_u64_vector(&columns[0])?;
let pid = procedure_service_handler let from_peers = cast_u64_vector(&columns[1])?;
.migrate_region(MigrateRegionRequest { let to_peers = cast_u64_vector(&columns[2])?;
region_id, let replay_timeouts = cast_u64_vector(&columns[3])?;
from_peer,
to_peer,
replay_timeout: Duration::from_secs(replay_timeout),
})
.await?;
match pid { (region_ids, from_peers, to_peers, Some(replay_timeouts))
Some(pid) => Ok(Value::from(pid)),
None => Ok(Value::Null),
} }
}
_ => Ok(Value::Null), size => {
return InvalidFuncArgsSnafu {
err_msg: format!(
"The length of the args is not correct, expect exactly 3 or 4, have: {}",
size
),
}
.fail();
}
};
std::thread::spawn(move || {
let len = region_ids.len();
let mut results = StringVectorBuilder::with_capacity(len);
for index in 0..len {
let region_id = region_ids.get(index);
let from_peer = from_peers.get(index);
let to_peer = to_peers.get(index);
let replay_timeout = match &replay_timeouts {
Some(replay_timeouts) => replay_timeouts.get(index),
None => Value::UInt64(DEFAULT_REPLAY_TIMEOUT_SECS),
};
match (region_id, from_peer, to_peer, replay_timeout) {
(
Value::UInt64(region_id),
Value::UInt64(from_peer),
Value::UInt64(to_peer),
Value::UInt64(replay_timeout),
) => {
let func_ctx = func_ctx.clone();
let pid = common_runtime::block_on_read(async move {
func_ctx
.state
.table_mutation_handler
.as_ref()
.context(MissingTableMutationHandlerSnafu)?
.migrate_region(
region_id,
from_peer,
to_peer,
Duration::from_secs(replay_timeout),
)
.await
})?;
results.push(Some(&pid));
}
_ => {
results.push(None);
}
}
}
Ok(results.to_vector())
})
.join()
.map_err(|e| {
error!(e; "Join thread error");
ThreadJoin {
location: Location::default(),
}
})?
} }
} }
fn signature() -> Signature { impl fmt::Display for MigrateRegionFunction {
Signature::one_of( fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
vec![ write!(f, "MIGRATE_REGION")
// migrate_region(region_id, from_peer, to_peer) }
TypeSignature::Uniform(3, ConcreteDataType::numerics()),
// migrate_region(region_id, from_peer, to_peer, timeout(secs))
TypeSignature::Uniform(4, ConcreteDataType::numerics()),
],
Volatility::Immutable,
)
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::sync::Arc; // FIXME(dennis): test in the following PR.
use common_query::prelude::TypeSignature;
use datatypes::vectors::{StringVector, UInt64Vector};
use super::*;
#[test]
fn test_migrate_region_misc() {
let f = MigrateRegionFunction;
assert_eq!("migrate_region", f.name());
assert_eq!(
ConcreteDataType::string_datatype(),
f.return_type(&[]).unwrap()
);
assert!(matches!(f.signature(),
Signature {
type_signature: TypeSignature::OneOf(sigs),
volatility: Volatility::Immutable
} if sigs.len() == 2));
}
#[test]
fn test_missing_procedure_service() {
let f = MigrateRegionFunction;
let args = vec![1, 1, 1];
let args = args
.into_iter()
.map(|arg| Arc::new(UInt64Vector::from_slice([arg])) as _)
.collect::<Vec<_>>();
let result = f.eval(FunctionContext::default(), &args).unwrap_err();
assert_eq!(
"Missing ProcedureServiceHandler, not expected",
result.to_string()
);
}
#[test]
fn test_migrate_region() {
let f = MigrateRegionFunction;
let args = vec![1, 1, 1];
let args = args
.into_iter()
.map(|arg| Arc::new(UInt64Vector::from_slice([arg])) as _)
.collect::<Vec<_>>();
let result = f.eval(FunctionContext::mock(), &args).unwrap();
let expect: VectorRef = Arc::new(StringVector::from(vec!["test_pid"]));
assert_eq!(expect, result);
}
} }

View File

@@ -4,9 +4,6 @@ version.workspace = true
edition.workspace = true edition.workspace = true
license.workspace = true license.workspace = true
[lints]
workspace = true
[dependencies] [dependencies]
async-trait.workspace = true async-trait.workspace = true
common-error.workspace = true common-error.workspace = true

View File

@@ -4,9 +4,6 @@ version.workspace = true
edition.workspace = true edition.workspace = true
license.workspace = true license.workspace = true
[lints]
workspace = true
[dependencies] [dependencies]
api.workspace = true api.workspace = true
async-trait.workspace = true async-trait.workspace = true

View File

@@ -4,9 +4,6 @@ version.workspace = true
edition.workspace = true edition.workspace = true
license.workspace = true license.workspace = true
[lints]
workspace = true
[dependencies] [dependencies]
api.workspace = true api.workspace = true
arrow-flight.workspace = true arrow-flight.workspace = true

View File

@@ -50,12 +50,8 @@ pub struct FlightEncoder {
impl Default for FlightEncoder { impl Default for FlightEncoder {
fn default() -> Self { fn default() -> Self {
let write_options = writer::IpcWriteOptions::default()
.try_with_compression(Some(arrow::ipc::CompressionType::LZ4_FRAME))
.unwrap();
Self { Self {
write_options, write_options: writer::IpcWriteOptions::default(),
data_gen: writer::IpcDataGenerator::default(), data_gen: writer::IpcDataGenerator::default(),
dictionary_tracker: writer::DictionaryTracker::new(false), dictionary_tracker: writer::DictionaryTracker::new(false),
} }

View File

@@ -7,9 +7,6 @@ license.workspace = true
[lib] [lib]
proc-macro = true proc-macro = true
[lints]
workspace = true
[dependencies] [dependencies]
proc-macro2 = "1.0.66" proc-macro2 = "1.0.66"
quote = "1.0" quote = "1.0"

View File

@@ -1,236 +0,0 @@
// Copyright 2023 Greptime Team
//
// Licensed 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.
use proc_macro::TokenStream;
use quote::quote;
use syn::spanned::Spanned;
use syn::{
parse_macro_input, Attribute, AttributeArgs, Ident, ItemFn, Signature, Type, TypePath,
TypeReference, Visibility,
};
use crate::utils::{extract_arg_map, extract_input_types, get_ident};
/// Internal util macro to early return on error.
macro_rules! ok {
($item:expr) => {
match $item {
Ok(item) => item,
Err(e) => return e.into_compile_error().into(),
}
};
}
/// Internal util macro to create an error.
macro_rules! error {
($span:expr, $msg: expr) => {
Err(syn::Error::new($span, $msg))
};
}
pub(crate) fn process_admin_fn(args: TokenStream, input: TokenStream) -> TokenStream {
let mut result = TokenStream::new();
// extract arg map
let arg_pairs = parse_macro_input!(args as AttributeArgs);
let arg_span = arg_pairs[0].span();
let arg_map = ok!(extract_arg_map(arg_pairs));
// decompose the fn block
let compute_fn = parse_macro_input!(input as ItemFn);
let ItemFn {
attrs,
vis,
sig,
block,
} = compute_fn;
// extract fn arg list
let Signature {
inputs,
ident: fn_name,
..
} = &sig;
let arg_types = ok!(extract_input_types(inputs));
if arg_types.len() < 2 {
ok!(error!(
sig.span(),
"Expect at least two argument for admin fn: (handler, query_ctx)"
));
}
let handler_type = ok!(extract_handler_type(&arg_types));
// build the struct and its impl block
// only do this when `display_name` is specified
if let Ok(display_name) = get_ident(&arg_map, "display_name", arg_span) {
let struct_code = build_struct(
attrs,
vis,
fn_name,
ok!(get_ident(&arg_map, "name", arg_span)),
ok!(get_ident(&arg_map, "sig_fn", arg_span)),
ok!(get_ident(&arg_map, "ret", arg_span)),
handler_type,
display_name,
);
result.extend(struct_code);
}
// preserve this fn
let input_fn_code: TokenStream = quote! {
#sig { #block }
}
.into();
result.extend(input_fn_code);
result
}
/// Retrieve the handler type, `ProcedureServiceHandlerRef` or `TableMutationHandlerRef`.
fn extract_handler_type(arg_types: &[Type]) -> Result<&Ident, syn::Error> {
match &arg_types[0] {
Type::Reference(TypeReference { elem, .. }) => match &**elem {
Type::Path(TypePath { path, .. }) => Ok(&path
.segments
.first()
.expect("Expected a reference of handler")
.ident),
other => {
error!(other.span(), "Expected a reference of handler")
}
},
other => {
error!(other.span(), "Expected a reference of handler")
}
}
}
/// Build the function struct
#[allow(clippy::too_many_arguments)]
fn build_struct(
attrs: Vec<Attribute>,
vis: Visibility,
fn_name: &Ident,
name: Ident,
sig_fn: Ident,
ret: Ident,
handler_type: &Ident,
display_name_ident: Ident,
) -> TokenStream {
let display_name = display_name_ident.to_string();
let ret = Ident::new(&format!("{ret}_datatype"), ret.span());
let uppcase_display_name = display_name.to_uppercase();
// Get the handler name in function state by the argument ident
let (handler, snafu_type) = match handler_type.to_string().as_str() {
"ProcedureServiceHandlerRef" => (
Ident::new("procedure_service_handler", handler_type.span()),
Ident::new("MissingProcedureServiceHandlerSnafu", handler_type.span()),
),
"TableMutationHandlerRef" => (
Ident::new("table_mutation_handler", handler_type.span()),
Ident::new("MissingTableMutationHandlerSnafu", handler_type.span()),
),
handler => ok!(error!(
handler_type.span(),
format!("Unknown handler type: {handler}")
)),
};
quote! {
#(#attrs)*
#[derive(Debug)]
#vis struct #name;
impl fmt::Display for #name {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, #uppcase_display_name)
}
}
impl Function for #name {
fn name(&self) -> &'static str {
#display_name
}
fn return_type(&self, _input_types: &[ConcreteDataType]) -> Result<ConcreteDataType> {
Ok(ConcreteDataType::#ret())
}
fn signature(&self) -> Signature {
#sig_fn()
}
fn eval(&self, func_ctx: FunctionContext, columns: &[VectorRef]) -> Result<VectorRef> {
// Ensure under the `greptime` catalog for security
ensure_greptime!(func_ctx);
let columns_num = columns.len();
let rows_num = if columns.is_empty() {
1
} else {
columns[0].len()
};
let columns = Vec::from(columns);
// TODO(dennis): DataFusion doesn't support async UDF currently
std::thread::spawn(move || {
let query_ctx = &func_ctx.query_ctx;
let handler = func_ctx
.state
.#handler
.as_ref()
.context(#snafu_type)?;
let mut builder = ConcreteDataType::#ret()
.create_mutable_vector(rows_num);
if columns_num == 0 {
let result = common_runtime::block_on_read(async move {
#fn_name(handler, query_ctx, &[]).await
})?;
builder.push_value_ref(result.as_value_ref());
} else {
for i in 0..rows_num {
let args: Vec<_> = columns.iter()
.map(|vector| vector.get_ref(i))
.collect();
let result = common_runtime::block_on_read(async move {
#fn_name(handler, query_ctx, &args).await
})?;
builder.push_value_ref(result.as_value_ref());
}
}
Ok(builder.to_vector())
})
.join()
.map_err(|e| {
error!(e; "Join thread error");
ThreadJoin {
location: Location::default(),
}
})?
}
}
}
.into()
}

View File

@@ -12,20 +12,17 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
mod admin_fn;
mod aggr_func; mod aggr_func;
mod print_caller; mod print_caller;
mod range_fn; mod range_fn;
mod stack_trace_debug; mod stack_trace_debug;
mod utils;
use aggr_func::{impl_aggr_func_type_store, impl_as_aggr_func_creator}; use aggr_func::{impl_aggr_func_type_store, impl_as_aggr_func_creator};
use print_caller::process_print_caller; use print_caller::process_print_caller;
use proc_macro::TokenStream; use proc_macro::TokenStream;
use range_fn::process_range_fn; use range_fn::process_range_fn;
use syn::{parse_macro_input, DeriveInput}; use syn::{parse_macro_input, DeriveInput};
use crate::admin_fn::process_admin_fn;
/// Make struct implemented trait [AggrFuncTypeStore], which is necessary when writing UDAF. /// Make struct implemented trait [AggrFuncTypeStore], which is necessary when writing UDAF.
/// This derive macro is expect to be used along with attribute macro [macro@as_aggr_func_creator]. /// This derive macro is expect to be used along with attribute macro [macro@as_aggr_func_creator].
#[proc_macro_derive(AggrFuncTypeStore)] #[proc_macro_derive(AggrFuncTypeStore)]
@@ -71,25 +68,6 @@ pub fn range_fn(args: TokenStream, input: TokenStream) -> TokenStream {
process_range_fn(args, input) process_range_fn(args, input)
} }
/// Attribute macro to convert a normal function to SQL administration function. The annotated function
/// should accept:
/// - `&ProcedureServiceHandlerRef` or `&TableMutationHandlerRef` as the first argument,
/// - `&QueryContextRef` as the second argument, and
/// - `&[ValueRef<'_>]` as the third argument which is SQL function input values in each row.
/// Return type must be `common_query::error::Result<Value>`.
///
/// # Example see `common/function/src/system/procedure_state.rs`.
///
/// # Arguments
/// - `name`: The name of the generated `Function` implementation.
/// - `ret`: The return type of the generated SQL function, it will be transformed into `ConcreteDataType::{ret}_datatype()` result.
/// - `display_name`: The display name of the generated SQL function.
/// - `sig_fn`: the function to returns `Signature` of generated `Function`.
#[proc_macro_attribute]
pub fn admin_fn(args: TokenStream, input: TokenStream) -> TokenStream {
process_admin_fn(args, input)
}
/// Attribute macro to print the caller to the annotated function. /// Attribute macro to print the caller to the annotated function.
/// The caller is printed as its filename and the call site line number. /// The caller is printed as its filename and the call site line number.
/// ///

View File

@@ -12,16 +12,20 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use std::collections::HashMap;
use proc_macro::TokenStream; use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::quote; use quote::quote;
use syn::punctuated::Punctuated;
use syn::spanned::Spanned; use syn::spanned::Spanned;
use syn::token::Comma;
use syn::{ use syn::{
parse_macro_input, Attribute, AttributeArgs, Ident, ItemFn, Signature, Type, TypeReference, parse_macro_input, Attribute, AttributeArgs, FnArg, Ident, ItemFn, Meta, MetaNameValue,
Visibility, NestedMeta, Signature, Type, TypeReference, Visibility,
}; };
use crate::utils::{extract_arg_map, extract_input_types, get_ident}; /// Internal util macro to early return on error.
macro_rules! ok { macro_rules! ok {
($item:expr) => { ($item:expr) => {
match $item { match $item {
@@ -85,6 +89,48 @@ pub(crate) fn process_range_fn(args: TokenStream, input: TokenStream) -> TokenSt
result result
} }
/// Extract a String <-> Ident map from the attribute args.
fn extract_arg_map(args: Vec<NestedMeta>) -> Result<HashMap<String, Ident>, syn::Error> {
args.into_iter()
.map(|meta| {
if let NestedMeta::Meta(Meta::NameValue(MetaNameValue { path, lit, .. })) = meta {
let name = path.get_ident().unwrap().to_string();
let ident = match lit {
syn::Lit::Str(lit_str) => lit_str.parse::<Ident>(),
_ => Err(syn::Error::new(
lit.span(),
"Unexpected attribute format. Expected `name = \"value\"`",
)),
}?;
Ok((name, ident))
} else {
Err(syn::Error::new(
meta.span(),
"Unexpected attribute format. Expected `name = \"value\"`",
))
}
})
.collect::<Result<HashMap<String, Ident>, syn::Error>>()
}
/// Helper function to get an Ident from the previous arg map.
fn get_ident(map: &HashMap<String, Ident>, key: &str, span: Span) -> Result<Ident, syn::Error> {
map.get(key)
.cloned()
.ok_or_else(|| syn::Error::new(span, format!("Expect attribute {key} but not found")))
}
/// Extract the argument list from the annotated function.
fn extract_input_types(inputs: &Punctuated<FnArg, Comma>) -> Result<Vec<Type>, syn::Error> {
inputs
.iter()
.map(|arg| match arg {
FnArg::Receiver(receiver) => Err(syn::Error::new(receiver.span(), "expected bool")),
FnArg::Typed(pat_type) => Ok(*pat_type.ty.clone()),
})
.collect()
}
fn build_struct( fn build_struct(
attrs: Vec<Attribute>, attrs: Vec<Attribute>,
vis: Visibility, vis: Visibility,
@@ -168,7 +214,7 @@ fn build_calc_fn(
#( let #range_array_names = RangeArray::try_new(extract_array(&input[#param_numbers])?.to_data().into())?; )* #( let #range_array_names = RangeArray::try_new(extract_array(&input[#param_numbers])?.to_data().into())?; )*
// TODO(ruihang): add ensure!() // TODO(ruihang): add ensure!()
let mut result_array = Vec::new(); let mut result_array = Vec::new();
for index in 0..#first_range_array_name.len(){ for index in 0..#first_range_array_name.len(){

View File

@@ -1,69 +0,0 @@
// Copyright 2023 Greptime Team
//
// Licensed 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.
use std::collections::HashMap;
use proc_macro2::Span;
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::token::Comma;
use syn::{FnArg, Ident, Meta, MetaNameValue, NestedMeta, Type};
/// Extract a String <-> Ident map from the attribute args.
pub(crate) fn extract_arg_map(args: Vec<NestedMeta>) -> Result<HashMap<String, Ident>, syn::Error> {
args.into_iter()
.map(|meta| {
if let NestedMeta::Meta(Meta::NameValue(MetaNameValue { path, lit, .. })) = meta {
let name = path.get_ident().unwrap().to_string();
let ident = match lit {
syn::Lit::Str(lit_str) => lit_str.parse::<Ident>(),
_ => Err(syn::Error::new(
lit.span(),
"Unexpected attribute format. Expected `name = \"value\"`",
)),
}?;
Ok((name, ident))
} else {
Err(syn::Error::new(
meta.span(),
"Unexpected attribute format. Expected `name = \"value\"`",
))
}
})
.collect::<Result<HashMap<String, Ident>, syn::Error>>()
}
/// Helper function to get an Ident from the previous arg map.
pub(crate) fn get_ident(
map: &HashMap<String, Ident>,
key: &str,
span: Span,
) -> Result<Ident, syn::Error> {
map.get(key)
.cloned()
.ok_or_else(|| syn::Error::new(span, format!("Expect attribute {key} but not found")))
}
/// Extract the argument list from the annotated function.
pub(crate) fn extract_input_types(
inputs: &Punctuated<FnArg, Comma>,
) -> Result<Vec<Type>, syn::Error> {
inputs
.iter()
.map(|arg| match arg {
FnArg::Receiver(receiver) => Err(syn::Error::new(receiver.span(), "expected bool")),
FnArg::Typed(pat_type) => Ok(*pat_type.ty.clone()),
})
.collect()
}

View File

@@ -4,9 +4,6 @@ version.workspace = true
edition.workspace = true edition.workspace = true
license.workspace = true license.workspace = true
[lints]
workspace = true
[dependencies] [dependencies]
common-error.workspace = true common-error.workspace = true
common-macro.workspace = true common-macro.workspace = true

View File

@@ -7,9 +7,6 @@ license.workspace = true
[features] [features]
testing = [] testing = []
[lints]
workspace = true
[dependencies] [dependencies]
api.workspace = true api.workspace = true
async-recursion = "1.0" async-recursion = "1.0"
@@ -18,13 +15,11 @@ async-trait.workspace = true
base64.workspace = true base64.workspace = true
bytes.workspace = true bytes.workspace = true
chrono.workspace = true chrono.workspace = true
common-base.workspace = true
common-catalog.workspace = true common-catalog.workspace = true
common-error.workspace = true common-error.workspace = true
common-grpc-expr.workspace = true common-grpc-expr.workspace = true
common-macro.workspace = true common-macro.workspace = true
common-procedure.workspace = true common-procedure.workspace = true
common-procedure-test.workspace = true
common-recordbatch.workspace = true common-recordbatch.workspace = true
common-runtime.workspace = true common-runtime.workspace = true
common-telemetry.workspace = true common-telemetry.workspace = true

View File

@@ -15,25 +15,23 @@
use std::sync::Arc; use std::sync::Arc;
use api::v1::region::{QueryRequest, RegionRequest}; use api::v1::region::{QueryRequest, RegionRequest};
pub use common_base::AffectedRows;
use common_recordbatch::SendableRecordBatchStream; use common_recordbatch::SendableRecordBatchStream;
use crate::error::Result; use crate::error::Result;
use crate::peer::Peer; use crate::peer::Peer;
/// The trait for handling requests to datanode. pub type AffectedRows = u64;
#[async_trait::async_trait] #[async_trait::async_trait]
pub trait Datanode: Send + Sync { pub trait Datanode: Send + Sync {
/// Handles DML, and DDL requests. /// Handles DML, and DDL requests.
async fn handle(&self, request: RegionRequest) -> Result<AffectedRows>; async fn handle(&self, request: RegionRequest) -> Result<AffectedRows>;
/// Handles query requests
async fn handle_query(&self, request: QueryRequest) -> Result<SendableRecordBatchStream>; async fn handle_query(&self, request: QueryRequest) -> Result<SendableRecordBatchStream>;
} }
pub type DatanodeRef = Arc<dyn Datanode>; pub type DatanodeRef = Arc<dyn Datanode>;
/// Datanode manager
#[async_trait::async_trait] #[async_trait::async_trait]
pub trait DatanodeManager: Send + Sync { pub trait DatanodeManager: Send + Sync {
/// Retrieves a target `datanode`. /// Retrieves a target `datanode`.

View File

@@ -18,7 +18,6 @@ use std::sync::Arc;
use common_telemetry::tracing_context::W3cTrace; use common_telemetry::tracing_context::W3cTrace;
use store_api::storage::{RegionNumber, TableId}; use store_api::storage::{RegionNumber, TableId};
use self::table_meta::TableMetadataAllocatorRef;
use crate::cache_invalidator::CacheInvalidatorRef; use crate::cache_invalidator::CacheInvalidatorRef;
use crate::datanode_manager::DatanodeManagerRef; use crate::datanode_manager::DatanodeManagerRef;
use crate::error::Result; use crate::error::Result;
@@ -26,7 +25,6 @@ use crate::key::table_route::TableRouteValue;
use crate::key::TableMetadataManagerRef; use crate::key::TableMetadataManagerRef;
use crate::region_keeper::MemoryRegionKeeperRef; use crate::region_keeper::MemoryRegionKeeperRef;
use crate::rpc::ddl::{SubmitDdlTaskRequest, SubmitDdlTaskResponse}; use crate::rpc::ddl::{SubmitDdlTaskRequest, SubmitDdlTaskResponse};
use crate::rpc::procedure::{MigrateRegionRequest, MigrateRegionResponse, ProcedureStateResponse};
pub mod alter_table; pub mod alter_table;
pub mod create_logical_tables; pub mod create_logical_tables;
@@ -34,10 +32,6 @@ pub mod create_table;
mod create_table_template; mod create_table_template;
pub mod drop_table; pub mod drop_table;
pub mod table_meta; pub mod table_meta;
#[cfg(any(test, feature = "testing"))]
pub mod test_util;
#[cfg(test)]
mod tests;
pub mod truncate_table; pub mod truncate_table;
pub mod utils; pub mod utils;
@@ -47,32 +41,16 @@ pub struct ExecutorContext {
pub tracing_context: Option<W3cTrace>, pub tracing_context: Option<W3cTrace>,
} }
/// The procedure executor that accepts ddl, region migration task etc.
#[async_trait::async_trait] #[async_trait::async_trait]
pub trait ProcedureExecutor: Send + Sync { pub trait DdlTaskExecutor: Send + Sync {
/// Submit a ddl task
async fn submit_ddl_task( async fn submit_ddl_task(
&self, &self,
ctx: &ExecutorContext, ctx: &ExecutorContext,
request: SubmitDdlTaskRequest, request: SubmitDdlTaskRequest,
) -> Result<SubmitDdlTaskResponse>; ) -> Result<SubmitDdlTaskResponse>;
/// Submit a region migration task
async fn migrate_region(
&self,
ctx: &ExecutorContext,
request: MigrateRegionRequest,
) -> Result<MigrateRegionResponse>;
/// Query the procedure state by its id
async fn query_procedure_state(
&self,
ctx: &ExecutorContext,
pid: &str,
) -> Result<ProcedureStateResponse>;
} }
pub type ProcedureExecutorRef = Arc<dyn ProcedureExecutor>; pub type DdlTaskExecutorRef = Arc<dyn DdlTaskExecutor>;
pub struct TableMetadataAllocatorContext { pub struct TableMetadataAllocatorContext {
pub cluster_id: u64, pub cluster_id: u64,
@@ -95,5 +73,4 @@ pub struct DdlContext {
pub cache_invalidator: CacheInvalidatorRef, pub cache_invalidator: CacheInvalidatorRef,
pub table_metadata_manager: TableMetadataManagerRef, pub table_metadata_manager: TableMetadataManagerRef,
pub memory_region_keeper: MemoryRegionKeeperRef, pub memory_region_keeper: MemoryRegionKeeperRef,
pub table_metadata_allocator: TableMetadataAllocatorRef,
} }

View File

@@ -40,7 +40,7 @@ use table::requests::AlterKind;
use table::table_reference::TableReference; use table::table_reference::TableReference;
use crate::cache_invalidator::Context; use crate::cache_invalidator::Context;
use crate::ddl::utils::add_peer_context_if_needed; use crate::ddl::utils::handle_operate_region_error;
use crate::ddl::DdlContext; use crate::ddl::DdlContext;
use crate::error::{self, ConvertAlterTableRequestSnafu, Error, InvalidProtoMsgSnafu, Result}; use crate::error::{self, ConvertAlterTableRequestSnafu, Error, InvalidProtoMsgSnafu, Result};
use crate::key::table_info::TableInfoValue; use crate::key::table_info::TableInfoValue;
@@ -226,7 +226,7 @@ impl AlterTableProcedure {
// The engine will throw this code when the schema version not match. // The engine will throw this code when the schema version not match.
// As this procedure has locked the table, the only reason for this error // As this procedure has locked the table, the only reason for this error
// is procedure is succeeded before and is retrying. // is procedure is succeeded before and is retrying.
return Err(add_peer_context_if_needed(datanode)(err)); return Err(handle_operate_region_error(datanode)(err));
} }
} }
Ok(()) Ok(())

View File

@@ -31,7 +31,7 @@ use strum::AsRefStr;
use table::metadata::{RawTableInfo, TableId}; use table::metadata::{RawTableInfo, TableId};
use crate::ddl::create_table_template::{build_template, CreateRequestBuilder}; use crate::ddl::create_table_template::{build_template, CreateRequestBuilder};
use crate::ddl::utils::{add_peer_context_if_needed, handle_retry_error, region_storage_path}; use crate::ddl::utils::{handle_operate_region_error, handle_retry_error, region_storage_path};
use crate::ddl::DdlContext; use crate::ddl::DdlContext;
use crate::error::{Result, TableAlreadyExistsSnafu}; use crate::error::{Result, TableAlreadyExistsSnafu};
use crate::key::table_name::TableNameKey; use crate::key::table_name::TableNameKey;
@@ -66,16 +66,7 @@ impl CreateLogicalTablesProcedure {
Ok(Self { context, creator }) Ok(Self { context, creator })
} }
/// On the prepares step, it performs: async fn on_prepare(&mut self) -> Result<Status> {
/// - Checks whether physical table exists.
/// - Checks whether logical tables exist.
/// - Allocates the table ids.
///
/// Abort(non-retry):
/// - The physical table does not exist.
/// - Failed to check whether tables exist.
/// - One of logical tables has existing, and the table creation task without setting `create_if_not_exists`.
pub(crate) async fn on_prepare(&mut self) -> Result<Status> {
let manager = &self.context.table_metadata_manager; let manager = &self.context.table_metadata_manager;
// Sets physical region numbers // Sets physical region numbers
@@ -89,7 +80,7 @@ impl CreateLogicalTablesProcedure {
.data .data
.set_physical_region_numbers(physical_region_numbers); .set_physical_region_numbers(physical_region_numbers);
// Checks if the tables exist // Checks if the tables exists
let table_name_keys = self let table_name_keys = self
.creator .creator
.data .data
@@ -105,9 +96,24 @@ impl CreateLogicalTablesProcedure {
.map(|x| x.map(|x| x.table_id())) .map(|x| x.map(|x| x.table_id()))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
// Validates the tasks // Sets table ids already exists
let tasks = &mut self.creator.data.tasks; self.creator
for (task, table_id) in tasks.iter().zip(already_exists_tables_ids.iter()) { .data
.set_table_ids_already_exists(already_exists_tables_ids);
// If all tables do not exists, we can create them directly.
if self.creator.data.is_all_tables_not_exists() {
self.creator.data.state = CreateTablesState::DatanodeCreateRegions;
return Ok(Status::executing(true));
}
// Filter out the tables that already exist.
let tasks = &self.creator.data.tasks;
let mut filtered_tasks = Vec::with_capacity(tasks.len());
for (task, table_id) in tasks
.iter()
.zip(self.creator.data.table_ids_already_exists().iter())
{
if table_id.is_some() { if table_id.is_some() {
// If a table already exists, we just ignore it. // If a table already exists, we just ignore it.
ensure!( ensure!(
@@ -118,34 +124,17 @@ impl CreateLogicalTablesProcedure {
); );
continue; continue;
} }
filtered_tasks.push(task.clone());
} }
// If all tables already exist, returns the table_ids. // Resets tasks
if already_exists_tables_ids.iter().all(Option::is_some) { self.creator.data.tasks = filtered_tasks;
return Ok(Status::done_with_output( if self.creator.data.tasks.is_empty() {
already_exists_tables_ids // If all tables already exist, we can skip the `DatanodeCreateRegions` stage.
.into_iter() self.creator.data.state = CreateTablesState::CreateMetadata;
.flatten() return Ok(Status::executing(true));
.collect::<Vec<_>>(),
));
} }
// Allocates table ids
for (task, table_id) in tasks.iter_mut().zip(already_exists_tables_ids.iter()) {
let table_id = if let Some(table_id) = table_id {
*table_id
} else {
self.context
.table_metadata_allocator
.allocate_table_id(task)
.await?
};
task.set_table_id(table_id);
}
self.creator
.data
.set_table_ids_already_exists(already_exists_tables_ids);
self.creator.data.state = CreateTablesState::DatanodeCreateRegions; self.creator.data.state = CreateTablesState::DatanodeCreateRegions;
Ok(Status::executing(true)) Ok(Status::executing(true))
} }
@@ -163,20 +152,17 @@ impl CreateLogicalTablesProcedure {
self.create_regions(region_routes).await self.create_regions(region_routes).await
} }
/// Creates table metadata
///
/// Abort(not-retry):
/// - Failed to create table metadata.
pub async fn on_create_metadata(&self) -> Result<Status> { pub async fn on_create_metadata(&self) -> Result<Status> {
let manager = &self.context.table_metadata_manager; let manager = &self.context.table_metadata_manager;
let physical_table_id = self.creator.data.physical_table_id(); let physical_table_id = self.creator.data.physical_table_id();
let remaining_tasks = self.creator.data.remaining_tasks(); let tables_data = self.creator.data.all_tables_data();
let num_tables = remaining_tasks.len(); let num_tables = tables_data.len();
if num_tables > 0 { if num_tables > 0 {
let chunk_size = manager.max_logical_tables_per_batch(); let chunk_size = manager.max_logical_tables_per_batch();
if num_tables > chunk_size { if num_tables > chunk_size {
let chunks = remaining_tasks let chunks = tables_data
.into_iter() .into_iter()
.chunks(chunk_size) .chunks(chunk_size)
.into_iter() .into_iter()
@@ -186,21 +172,11 @@ impl CreateLogicalTablesProcedure {
manager.create_logical_tables_metadata(chunk).await?; manager.create_logical_tables_metadata(chunk).await?;
} }
} else { } else {
manager manager.create_logical_tables_metadata(tables_data).await?;
.create_logical_tables_metadata(remaining_tasks)
.await?;
} }
} }
// The `table_id` MUST be collected after the [Prepare::Prepare], let table_ids = self.creator.data.real_table_ids();
// ensures the all `table_id`s have been allocated.
let table_ids = self
.creator
.data
.tasks
.iter()
.map(|task| task.table_info.ident.table_id)
.collect::<Vec<_>>();
info!("Created {num_tables} tables {table_ids:?} metadata for physical table {physical_table_id}"); info!("Created {num_tables} tables {table_ids:?} metadata for physical table {physical_table_id}");
@@ -262,10 +238,10 @@ impl CreateLogicalTablesProcedure {
body: Some(PbRegionRequest::Creates(creates)), body: Some(PbRegionRequest::Creates(creates)),
}; };
create_region_tasks.push(async move { create_region_tasks.push(async move {
requester if let Err(err) = requester.handle(request).await {
.handle(request) return Err(handle_operate_region_error(datanode)(err));
.await }
.map_err(add_peer_context_if_needed(datanode)) Ok(())
}); });
} }
@@ -334,13 +310,17 @@ impl TablesCreator {
tasks: Vec<CreateTableTask>, tasks: Vec<CreateTableTask>,
physical_table_id: TableId, physical_table_id: TableId,
) -> Self { ) -> Self {
let len = tasks.len(); let table_ids_from_tasks = tasks
.iter()
.map(|task| task.table_info.ident.table_id)
.collect::<Vec<_>>();
let len = table_ids_from_tasks.len();
Self { Self {
data: CreateTablesData { data: CreateTablesData {
cluster_id, cluster_id,
state: CreateTablesState::Prepare, state: CreateTablesState::Prepare,
tasks, tasks,
table_ids_from_tasks,
table_ids_already_exists: vec![None; len], table_ids_already_exists: vec![None; len],
physical_table_id, physical_table_id,
physical_region_numbers: vec![], physical_region_numbers: vec![],
@@ -354,6 +334,10 @@ pub struct CreateTablesData {
cluster_id: ClusterId, cluster_id: ClusterId,
state: CreateTablesState, state: CreateTablesState,
tasks: Vec<CreateTableTask>, tasks: Vec<CreateTableTask>,
table_ids_from_tasks: Vec<TableId>,
// Because the table_id is allocated before entering the distributed lock,
// it needs to recheck if the table exists when creating a table.
// If it does exist, then the table_id needs to be replaced with the existing one.
table_ids_already_exists: Vec<Option<TableId>>, table_ids_already_exists: Vec<Option<TableId>>,
physical_table_id: TableId, physical_table_id: TableId,
physical_region_numbers: Vec<RegionNumber>, physical_region_numbers: Vec<RegionNumber>,
@@ -376,6 +360,24 @@ impl CreateTablesData {
self.table_ids_already_exists = table_ids_already_exists; self.table_ids_already_exists = table_ids_already_exists;
} }
fn table_ids_already_exists(&self) -> &[Option<TableId>] {
&self.table_ids_already_exists
}
fn is_all_tables_not_exists(&self) -> bool {
self.table_ids_already_exists.iter().all(Option::is_none)
}
pub fn real_table_ids(&self) -> Vec<TableId> {
self.table_ids_from_tasks
.iter()
.zip(self.table_ids_already_exists.iter())
.map(|(table_id_from_task, table_id_already_exists)| {
table_id_already_exists.unwrap_or(*table_id_from_task)
})
.collect::<Vec<_>>()
}
fn all_create_table_exprs(&self) -> Vec<&CreateTableExpr> { fn all_create_table_exprs(&self) -> Vec<&CreateTableExpr> {
self.tasks self.tasks
.iter() .iter()
@@ -383,27 +385,18 @@ impl CreateTablesData {
.collect::<Vec<_>>() .collect::<Vec<_>>()
} }
/// Returns the remaining tasks. fn all_tables_data(&self) -> Vec<(RawTableInfo, TableRouteValue)> {
/// The length of tasks must be greater than 0.
fn remaining_tasks(&self) -> Vec<(RawTableInfo, TableRouteValue)> {
self.tasks self.tasks
.iter() .iter()
.zip(self.table_ids_already_exists.iter()) .map(|task| {
.flat_map(|(task, table_id)| { let table_info = task.table_info.clone();
if table_id.is_none() { let region_ids = self
let table_info = task.table_info.clone(); .physical_region_numbers
let region_ids = self .iter()
.physical_region_numbers .map(|region_number| RegionId::new(table_info.ident.table_id, *region_number))
.iter() .collect();
.map(|region_number| { let table_route = TableRouteValue::logical(self.physical_table_id, region_ids);
RegionId::new(table_info.ident.table_id, *region_number) (table_info, table_route)
})
.collect();
let table_route = TableRouteValue::logical(self.physical_table_id, region_ids);
Some((table_info, table_route))
} else {
None
}
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>()
} }

Some files were not shown because too many files have changed in this diff Show More