mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2026-09-06 13:38:59 +00:00
* feat(event): record procedure actor Signed-off-by: WenyXu <wenymedia@gmail.com> * test: handle streamed region migration output Signed-off-by: WenyXu <wenymedia@gmail.com> * test: cover procedure actors across SQL protocols Signed-off-by: WenyXu <wenymedia@gmail.com> --------- Signed-off-by: WenyXu <wenymedia@gmail.com>
802 lines
26 KiB
Rust
802 lines
26 KiB
Rust
// 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::sync::Arc;
|
|
#[cfg(feature = "enterprise")]
|
|
use std::time::Duration;
|
|
|
|
use api::v1::ddl_request::Expr as DdlExpr;
|
|
use api::v1::greptime_request::Request;
|
|
use api::v1::value::ValueData;
|
|
use api::v1::{
|
|
AddColumn, AddColumns, AlterTableExpr, ColumnDataType, ColumnDef, CreateTableExpr, DdlRequest,
|
|
Row, RowInsertRequest, RowInsertRequests, Rows, SemanticType, alter_table_expr,
|
|
};
|
|
use client::OutputData;
|
|
use common_catalog::consts::MITO_ENGINE;
|
|
use common_event_recorder::event_table::{
|
|
CATALOG_NAME_COLUMN, PAYLOAD_COLUMN, PHYSICAL_TABLE_ID_COLUMN, PROCEDURE_ID_COLUMN,
|
|
PROCEDURE_TRIGGER_COLUMN, SCHEMA_NAME_COLUMN, TABLE_ID_COLUMN, TABLE_NAME_COLUMN, TYPE_COLUMN,
|
|
};
|
|
#[cfg(feature = "enterprise")]
|
|
use common_event_recorder::{PersistentEventContext, TriggerReason};
|
|
#[cfg(feature = "enterprise")]
|
|
use common_procedure::ProcedureContext;
|
|
use common_test_util::temp_dir::create_temp_dir;
|
|
use frontend::instance::Instance;
|
|
use meta_srv::gc::GcSchedulerOptions;
|
|
use mito2::gc::GcConfig;
|
|
use servers::query_handler::grpc::GrpcQueryHandler;
|
|
use servers::query_handler::sql::SqlQueryHandler;
|
|
use session::context::{Channel, QueryContext};
|
|
use tests_integration::cluster::GreptimeDbClusterBuilder;
|
|
use tests_integration::test_util::setup_authenticated_grpc_database;
|
|
|
|
use crate::event_recorder_test_util::{
|
|
assert_eventually_eq, assert_procedure_actor, assert_single_event, find_eventually_string,
|
|
find_eventually_u32,
|
|
};
|
|
|
|
const EVENTS_TABLE: &str = "greptime_private.events";
|
|
const TABLE: &str = "table_ddl_events";
|
|
const PHYSICAL_TABLE: &str = "table_ddl_events_phy";
|
|
const LOGICAL_TABLE: &str = "table_ddl_events_logical";
|
|
const AUTO_TABLE: &str = "table_ddl_events_auto";
|
|
const AUTO_INFLUX_TABLE: &str = "table_ddl_events_auto_influx";
|
|
const GRPC_TABLE: &str = "table_ddl_events_grpc";
|
|
const PROCEDURE_ACTOR: &str = "procedure_actor";
|
|
const PROCEDURE_ACTOR_PASSWORD: &str = "procedure_actor_pwd";
|
|
|
|
#[tokio::test(flavor = "multi_thread")]
|
|
async fn test_table_ddl_procedure_events() {
|
|
common_telemetry::init_default_ut_logging();
|
|
|
|
// Arrange: use a distributed cluster to submit Undrop and Purge directly
|
|
// through the metasrv DDL manager and exercise logical-table task grouping.
|
|
let home_dir = create_temp_dir("table_ddl_procedure_events");
|
|
#[cfg_attr(not(feature = "enterprise"), allow(unused_mut))]
|
|
let mut gc_options = GcSchedulerOptions {
|
|
enable: true,
|
|
..Default::default()
|
|
};
|
|
// Soft drop is enterprise-only; non-enterprise builds run this test with
|
|
// plain GC and skip the undrop/purge assertions below.
|
|
#[cfg(feature = "enterprise")]
|
|
{
|
|
gc_options.experimental_soft_drop.enable = true;
|
|
gc_options.experimental_soft_drop.retention = Duration::from_secs(1);
|
|
}
|
|
let cluster = GreptimeDbClusterBuilder::new("table_ddl_procedure_events")
|
|
.await
|
|
.with_datanodes(1)
|
|
.with_metasrv_gc_config(gc_options)
|
|
.with_datanode_gc_config(GcConfig {
|
|
enable: true,
|
|
..Default::default()
|
|
})
|
|
.with_shared_home_dir(Arc::new(home_dir))
|
|
.build(true)
|
|
.await;
|
|
let frontend = cluster.fe_instance().clone();
|
|
|
|
auto_create_table(&frontend, AUTO_TABLE, Channel::Prometheus).await;
|
|
let auto_create_procedure_id =
|
|
submitted_procedure_id(&frontend, "create_table", AUTO_TABLE).await;
|
|
assert_event_context(
|
|
&frontend,
|
|
"create_table",
|
|
&auto_create_procedure_id,
|
|
"auto_create",
|
|
Some("prometheus"),
|
|
)
|
|
.await;
|
|
|
|
auto_create_table(&frontend, AUTO_INFLUX_TABLE, Channel::Influx).await;
|
|
let auto_influx_create_procedure_id =
|
|
submitted_procedure_id(&frontend, "create_table", AUTO_INFLUX_TABLE).await;
|
|
assert_event_context(
|
|
&frontend,
|
|
"create_table",
|
|
&auto_influx_create_procedure_id,
|
|
"auto_create",
|
|
Some("influx"),
|
|
)
|
|
.await;
|
|
|
|
// gRPC DDL bypasses the SQL statement executor, so it must set its own
|
|
// manual trigger reason before submitting the table procedure.
|
|
GrpcQueryHandler::do_query(
|
|
frontend.as_ref(),
|
|
Request::Ddl(DdlRequest {
|
|
expr: Some(DdlExpr::CreateTable(CreateTableExpr {
|
|
catalog_name: "greptime".to_string(),
|
|
schema_name: "public".to_string(),
|
|
table_name: GRPC_TABLE.to_string(),
|
|
column_defs: vec![
|
|
ColumnDef {
|
|
name: "host".to_string(),
|
|
data_type: ColumnDataType::String as i32,
|
|
is_nullable: true,
|
|
semantic_type: SemanticType::Tag as i32,
|
|
..Default::default()
|
|
},
|
|
ColumnDef {
|
|
name: "ts".to_string(),
|
|
data_type: ColumnDataType::TimestampMillisecond as i32,
|
|
is_nullable: false,
|
|
semantic_type: SemanticType::Timestamp as i32,
|
|
..Default::default()
|
|
},
|
|
],
|
|
time_index: "ts".to_string(),
|
|
engine: MITO_ENGINE.to_string(),
|
|
..Default::default()
|
|
})),
|
|
}),
|
|
Arc::new(QueryContext::with_channel(
|
|
"greptime",
|
|
"public",
|
|
Channel::Grpc,
|
|
)),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
let grpc_create_procedure_id =
|
|
submitted_procedure_id(&frontend, "create_table", GRPC_TABLE).await;
|
|
assert_procedure_actor(&frontend, &grpc_create_procedure_id, Some("greptime")).await;
|
|
assert_event_context(
|
|
&frontend,
|
|
"create_table",
|
|
&grpc_create_procedure_id,
|
|
"manual",
|
|
Some("grpc"),
|
|
)
|
|
.await;
|
|
GrpcQueryHandler::do_query(
|
|
frontend.as_ref(),
|
|
Request::Ddl(DdlRequest {
|
|
expr: Some(DdlExpr::AlterTable(AlterTableExpr {
|
|
catalog_name: "greptime".to_string(),
|
|
schema_name: "public".to_string(),
|
|
table_name: GRPC_TABLE.to_string(),
|
|
kind: Some(alter_table_expr::Kind::AddColumns(AddColumns {
|
|
add_columns: vec![AddColumn {
|
|
column_def: Some(ColumnDef {
|
|
name: "value".to_string(),
|
|
data_type: ColumnDataType::Float64 as i32,
|
|
is_nullable: true,
|
|
semantic_type: SemanticType::Field as i32,
|
|
..Default::default()
|
|
}),
|
|
..Default::default()
|
|
}],
|
|
})),
|
|
})),
|
|
}),
|
|
Arc::new(QueryContext::with_channel(
|
|
"greptime",
|
|
"public",
|
|
Channel::Grpc,
|
|
)),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
let grpc_alter_procedure_id =
|
|
submitted_procedure_id(&frontend, "alter_table", GRPC_TABLE).await;
|
|
assert_event_context(
|
|
&frontend,
|
|
"alter_table",
|
|
&grpc_alter_procedure_id,
|
|
"manual",
|
|
Some("grpc"),
|
|
)
|
|
.await;
|
|
|
|
// Act / Assert: Create Table retains its rich submitted row and records the
|
|
// created table ID when it completes.
|
|
let (actor_db, _actor_grpc_server) = setup_authenticated_grpc_database(
|
|
frontend.clone(),
|
|
PROCEDURE_ACTOR,
|
|
PROCEDURE_ACTOR_PASSWORD,
|
|
)
|
|
.await;
|
|
actor_db
|
|
.sql(format!(
|
|
"CREATE TABLE {TABLE} (host STRING PRIMARY KEY, ts TIMESTAMP TIME INDEX, val DOUBLE)"
|
|
))
|
|
.await
|
|
.unwrap();
|
|
let table_id = find_table_id(&frontend, TABLE).await;
|
|
let create_table_procedure_id = submitted_procedure_id(&frontend, "create_table", TABLE).await;
|
|
assert_procedure_actor(&frontend, &create_table_procedure_id, Some(PROCEDURE_ACTOR)).await;
|
|
assert_event_context(
|
|
&frontend,
|
|
"create_table",
|
|
&create_table_procedure_id,
|
|
"manual",
|
|
Some("grpc"),
|
|
)
|
|
.await;
|
|
assert_named_submitted_event(
|
|
&frontend,
|
|
"create_table",
|
|
&create_table_procedure_id,
|
|
"\
|
|
+--------------+------------------+
|
|
| type | name |
|
|
+--------------+------------------+
|
|
| create_table | table_ddl_events |
|
|
+--------------+------------------+",
|
|
)
|
|
.await;
|
|
assert_terminal_event(
|
|
&frontend,
|
|
"create_table",
|
|
&create_table_procedure_id,
|
|
"Succeeded",
|
|
Some(TABLE),
|
|
Some(table_id),
|
|
)
|
|
.await;
|
|
|
|
// Act / Assert: Alter and Truncate retain their submitted locators on terminal rows.
|
|
run_sql_with_context(
|
|
&frontend,
|
|
&format!("ALTER TABLE {TABLE} ADD COLUMN extra STRING"),
|
|
Arc::new(QueryContext::with_channel(
|
|
"greptime",
|
|
"public",
|
|
Channel::HttpSql,
|
|
)),
|
|
)
|
|
.await;
|
|
let alter_table_procedure_id = submitted_procedure_id(&frontend, "alter_table", TABLE).await;
|
|
assert_event_context(
|
|
&frontend,
|
|
"alter_table",
|
|
&alter_table_procedure_id,
|
|
"manual",
|
|
Some("httpsql"),
|
|
)
|
|
.await;
|
|
assert_named_submitted_event(
|
|
&frontend,
|
|
"alter_table",
|
|
&alter_table_procedure_id,
|
|
"\
|
|
+-------------+------------------+
|
|
| type | name |
|
|
+-------------+------------------+
|
|
| alter_table | table_ddl_events |
|
|
+-------------+------------------+",
|
|
)
|
|
.await;
|
|
assert_terminal_event(
|
|
&frontend,
|
|
"alter_table",
|
|
&alter_table_procedure_id,
|
|
"Succeeded",
|
|
Some(TABLE),
|
|
Some(table_id),
|
|
)
|
|
.await;
|
|
run_sql(
|
|
&frontend,
|
|
&format!("INSERT INTO {TABLE} VALUES ('a', 0, 1, 'b')"),
|
|
)
|
|
.await;
|
|
run_sql_with_context(
|
|
&frontend,
|
|
&format!("TRUNCATE TABLE {TABLE}"),
|
|
Arc::new(QueryContext::with_channel(
|
|
"greptime",
|
|
"public",
|
|
Channel::HttpSql,
|
|
)),
|
|
)
|
|
.await;
|
|
let truncate_table_procedure_id =
|
|
submitted_procedure_id(&frontend, "truncate_table", TABLE).await;
|
|
assert_event_context(
|
|
&frontend,
|
|
"truncate_table",
|
|
&truncate_table_procedure_id,
|
|
"manual",
|
|
Some("httpsql"),
|
|
)
|
|
.await;
|
|
assert_named_submitted_event(
|
|
&frontend,
|
|
"truncate_table",
|
|
&truncate_table_procedure_id,
|
|
"\
|
|
+----------------+------------------+
|
|
| type | name |
|
|
+----------------+------------------+
|
|
| truncate_table | table_ddl_events |
|
|
+----------------+------------------+",
|
|
)
|
|
.await;
|
|
assert_terminal_event(
|
|
&frontend,
|
|
"truncate_table",
|
|
&truncate_table_procedure_id,
|
|
"Succeeded",
|
|
Some(TABLE),
|
|
Some(table_id),
|
|
)
|
|
.await;
|
|
|
|
// Act / Assert: Drop records the table name, while Undrop and Purge use the
|
|
// table ID as their submitted locator.
|
|
run_sql_with_context(
|
|
&frontend,
|
|
&format!("DROP TABLE {TABLE}"),
|
|
Arc::new(QueryContext::with_channel(
|
|
"greptime",
|
|
"public",
|
|
Channel::HttpSql,
|
|
)),
|
|
)
|
|
.await;
|
|
let drop_table_procedure_id = submitted_procedure_id(&frontend, "drop_table", TABLE).await;
|
|
assert_event_context(
|
|
&frontend,
|
|
"drop_table",
|
|
&drop_table_procedure_id,
|
|
"manual",
|
|
Some("httpsql"),
|
|
)
|
|
.await;
|
|
assert_named_submitted_event(
|
|
&frontend,
|
|
"drop_table",
|
|
&drop_table_procedure_id,
|
|
"\
|
|
+------------+------------------+
|
|
| type | name |
|
|
+------------+------------------+
|
|
| drop_table | table_ddl_events |
|
|
+------------+------------------+",
|
|
)
|
|
.await;
|
|
assert_terminal_event(
|
|
&frontend,
|
|
"drop_table",
|
|
&drop_table_procedure_id,
|
|
"Succeeded",
|
|
Some(TABLE),
|
|
Some(table_id),
|
|
)
|
|
.await;
|
|
#[cfg(feature = "enterprise")]
|
|
{
|
|
let (undrop_table_procedure_id, _) = cluster
|
|
.metasrv
|
|
.ddl_manager()
|
|
.submit_undrop_table_task(
|
|
common_meta::rpc::ddl::UndropTableTask { table_id },
|
|
ProcedureContext::from_event_context(PersistentEventContext::new(
|
|
TriggerReason::Manual,
|
|
)),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
let undrop_table_procedure_id = undrop_table_procedure_id.to_string();
|
|
assert_event_context(
|
|
&frontend,
|
|
"undrop_table",
|
|
&undrop_table_procedure_id,
|
|
"manual",
|
|
None,
|
|
)
|
|
.await;
|
|
assert_id_submitted_event(
|
|
&frontend,
|
|
"undrop_table",
|
|
&undrop_table_procedure_id,
|
|
"\
|
|
+--------------+
|
|
| type |
|
|
+--------------+
|
|
| undrop_table |
|
|
+--------------+",
|
|
)
|
|
.await;
|
|
assert_terminal_event(
|
|
&frontend,
|
|
"undrop_table",
|
|
&undrop_table_procedure_id,
|
|
"Succeeded",
|
|
Some(TABLE),
|
|
Some(table_id),
|
|
)
|
|
.await;
|
|
run_sql(&frontend, &format!("DROP TABLE {TABLE}")).await;
|
|
let (purge_table_procedure_id, _) = cluster
|
|
.metasrv
|
|
.ddl_manager()
|
|
.submit_purge_dropped_table_task(
|
|
common_meta::rpc::ddl::PurgeDroppedTableTask { table_id },
|
|
ProcedureContext::from_event_context(PersistentEventContext::new(
|
|
TriggerReason::Manual,
|
|
)),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
let purge_table_procedure_id = purge_table_procedure_id.to_string();
|
|
assert_event_context(
|
|
&frontend,
|
|
"purge_dropped_table",
|
|
&purge_table_procedure_id,
|
|
"manual",
|
|
None,
|
|
)
|
|
.await;
|
|
assert_id_submitted_event(
|
|
&frontend,
|
|
"purge_dropped_table",
|
|
&purge_table_procedure_id,
|
|
"\
|
|
+---------------------+
|
|
| type |
|
|
+---------------------+
|
|
| purge_dropped_table |
|
|
+---------------------+",
|
|
)
|
|
.await;
|
|
assert_terminal_event(
|
|
&frontend,
|
|
"purge_dropped_table",
|
|
&purge_table_procedure_id,
|
|
"Succeeded",
|
|
Some(TABLE),
|
|
Some(table_id),
|
|
)
|
|
.await;
|
|
}
|
|
|
|
// Arrange / Act: metric logical-table DDL is executed through the distributed
|
|
// frontend, which groups logical DDL tasks for the meta procedure manager.
|
|
run_sql(
|
|
&frontend,
|
|
&format!(
|
|
"CREATE TABLE {PHYSICAL_TABLE} (ts TIMESTAMP TIME INDEX, val DOUBLE) ENGINE=metric WITH (\"physical_metric_table\" = \"\")"
|
|
),
|
|
)
|
|
.await;
|
|
let physical_table_id = find_table_id(&frontend, PHYSICAL_TABLE).await;
|
|
run_sql_with_context(
|
|
&frontend,
|
|
&format!(
|
|
"CREATE TABLE {LOGICAL_TABLE} (ts TIMESTAMP TIME INDEX, val DOUBLE, host STRING PRIMARY KEY) ENGINE=metric WITH (\"on_physical_table\" = \"{PHYSICAL_TABLE}\")"
|
|
),
|
|
Arc::new(QueryContext::with_channel(
|
|
"greptime",
|
|
"public",
|
|
Channel::HttpSql,
|
|
)),
|
|
)
|
|
.await;
|
|
let logical_table_id = find_table_id(&frontend, LOGICAL_TABLE).await;
|
|
let create_logical_tables_procedure_id =
|
|
submitted_procedure_id(&frontend, "create_logical_tables", LOGICAL_TABLE).await;
|
|
assert_event_context(
|
|
&frontend,
|
|
"create_logical_tables",
|
|
&create_logical_tables_procedure_id,
|
|
"manual",
|
|
Some("httpsql"),
|
|
)
|
|
.await;
|
|
|
|
// Assert: logical Create emits one submitted row per logical table and
|
|
// preserves the logical and physical table IDs on its terminal row.
|
|
assert_named_submitted_event(
|
|
&frontend,
|
|
"create_logical_tables",
|
|
&create_logical_tables_procedure_id,
|
|
"\
|
|
+-----------------------+--------------------------+
|
|
| type | name |
|
|
+-----------------------+--------------------------+
|
|
| create_logical_tables | table_ddl_events_logical |
|
|
+-----------------------+--------------------------+",
|
|
)
|
|
.await;
|
|
assert_logical_terminal_event(
|
|
&frontend,
|
|
"create_logical_tables",
|
|
&create_logical_tables_procedure_id,
|
|
logical_table_id,
|
|
physical_table_id,
|
|
"Succeeded",
|
|
"\
|
|
+-----------------------+--------------------------+
|
|
| type | name |
|
|
+-----------------------+--------------------------+
|
|
| create_logical_tables | table_ddl_events_logical |
|
|
+-----------------------+--------------------------+",
|
|
)
|
|
.await;
|
|
|
|
// Act / Assert: logical Alter preserves the one-row-per-logical-table submitted
|
|
// contract and retains the logical-table locator on its terminal row.
|
|
run_sql_with_context(
|
|
&frontend,
|
|
&format!("ALTER TABLE {LOGICAL_TABLE} ADD COLUMN rack STRING PRIMARY KEY"),
|
|
Arc::new(QueryContext::with_channel(
|
|
"greptime",
|
|
"public",
|
|
Channel::HttpSql,
|
|
)),
|
|
)
|
|
.await;
|
|
let alter_logical_tables_procedure_id =
|
|
submitted_procedure_id(&frontend, "alter_logical_tables", LOGICAL_TABLE).await;
|
|
assert_event_context(
|
|
&frontend,
|
|
"alter_logical_tables",
|
|
&alter_logical_tables_procedure_id,
|
|
"manual",
|
|
Some("httpsql"),
|
|
)
|
|
.await;
|
|
assert_named_submitted_event(
|
|
&frontend,
|
|
"alter_logical_tables",
|
|
&alter_logical_tables_procedure_id,
|
|
"\
|
|
+----------------------+--------------------------+
|
|
| type | name |
|
|
+----------------------+--------------------------+
|
|
| alter_logical_tables | table_ddl_events_logical |
|
|
+----------------------+--------------------------+",
|
|
)
|
|
.await;
|
|
assert_terminal_event(
|
|
&frontend,
|
|
"alter_logical_tables",
|
|
&alter_logical_tables_procedure_id,
|
|
"Succeeded",
|
|
Some(LOGICAL_TABLE),
|
|
None,
|
|
)
|
|
.await;
|
|
}
|
|
|
|
async fn run_sql(instance: &Arc<Instance>, sql: &str) {
|
|
run_sql_with_context(instance, sql, QueryContext::arc()).await;
|
|
}
|
|
|
|
async fn auto_create_table(instance: &Arc<Instance>, table_name: &str, channel: Channel) {
|
|
instance
|
|
.handle_row_inserts(
|
|
RowInsertRequests {
|
|
inserts: vec![RowInsertRequest {
|
|
table_name: table_name.to_string(),
|
|
rows: Some(Rows {
|
|
schema: vec![
|
|
api::v1::ColumnSchema {
|
|
column_name: "host".to_string(),
|
|
datatype: ColumnDataType::String as i32,
|
|
semantic_type: SemanticType::Tag as i32,
|
|
datatype_extension: None,
|
|
options: None,
|
|
},
|
|
api::v1::ColumnSchema {
|
|
column_name: "ts".to_string(),
|
|
datatype: ColumnDataType::TimestampMillisecond as i32,
|
|
semantic_type: SemanticType::Timestamp as i32,
|
|
datatype_extension: None,
|
|
options: None,
|
|
},
|
|
api::v1::ColumnSchema {
|
|
column_name: "val".to_string(),
|
|
datatype: ColumnDataType::Float64 as i32,
|
|
semantic_type: SemanticType::Field as i32,
|
|
datatype_extension: None,
|
|
options: None,
|
|
},
|
|
],
|
|
rows: vec![Row {
|
|
values: vec![
|
|
ValueData::StringValue("host".to_string()).into(),
|
|
ValueData::TimestampMillisecondValue(0).into(),
|
|
ValueData::F64Value(1.0).into(),
|
|
],
|
|
}],
|
|
}),
|
|
}],
|
|
},
|
|
Arc::new(QueryContext::with_channel("greptime", "public", channel)),
|
|
false,
|
|
false,
|
|
)
|
|
.await
|
|
.unwrap();
|
|
}
|
|
|
|
async fn run_sql_with_context(
|
|
instance: &Arc<Instance>,
|
|
sql: &str,
|
|
query_context: Arc<QueryContext>,
|
|
) {
|
|
let output = SqlQueryHandler::do_query(instance.as_ref(), sql, query_context)
|
|
.await
|
|
.remove(0)
|
|
.unwrap();
|
|
assert!(matches!(output.data, OutputData::AffectedRows(_)), "{sql}");
|
|
}
|
|
|
|
async fn assert_event_context(
|
|
instance: &Arc<Instance>,
|
|
event_type: &str,
|
|
procedure_id: &str,
|
|
reason: &str,
|
|
protocol: Option<&str>,
|
|
) {
|
|
let event_filter = format!(
|
|
"FROM {EVENTS_TABLE} WHERE {} = '{event_type}' AND {} = '{procedure_id}' AND json_get_string({}, 'type') = 'Submitted'",
|
|
TYPE_COLUMN.name(),
|
|
PROCEDURE_ID_COLUMN.name(),
|
|
PROCEDURE_TRIGGER_COLUMN.name(),
|
|
);
|
|
assert_single_event(
|
|
instance,
|
|
&format!("SELECT count(*) AS event_count {event_filter}"),
|
|
)
|
|
.await;
|
|
|
|
let actual = find_eventually_string(
|
|
instance,
|
|
&format!("SELECT json_to_string(event_context) AS event_context {event_filter}"),
|
|
"event_context",
|
|
)
|
|
.await;
|
|
let expected = match protocol {
|
|
Some(protocol) => format!(r#"{{"protocol":"{protocol}","reason":"{reason}"}}"#),
|
|
None => format!(r#"{{"reason":"{reason}"}}"#),
|
|
};
|
|
assert_eq!(expected, actual);
|
|
}
|
|
|
|
async fn find_table_id(instance: &Arc<Instance>, table_name: &str) -> u32 {
|
|
find_eventually_u32(
|
|
instance,
|
|
&format!(
|
|
"SELECT table_id FROM information_schema.tables WHERE table_catalog = 'greptime' AND table_schema = 'public' AND table_name = '{table_name}'"
|
|
),
|
|
"table_id",
|
|
)
|
|
.await
|
|
}
|
|
|
|
async fn submitted_procedure_id(
|
|
instance: &Arc<Instance>,
|
|
event_type: &str,
|
|
table_name: &str,
|
|
) -> String {
|
|
let query = format!(
|
|
"SELECT {} FROM {EVENTS_TABLE} WHERE {} = '{event_type}' AND {} = '{table_name}' AND json_path_match({}, '$.type == \"Submitted\"') AND json_path_match({}, '$.version == 1') ORDER BY timestamp DESC LIMIT 1",
|
|
PROCEDURE_ID_COLUMN.name(),
|
|
TYPE_COLUMN.name(),
|
|
TABLE_NAME_COLUMN.name(),
|
|
PROCEDURE_TRIGGER_COLUMN.name(),
|
|
PAYLOAD_COLUMN.name(),
|
|
);
|
|
find_eventually_string(instance, &query, PROCEDURE_ID_COLUMN.name()).await
|
|
}
|
|
|
|
async fn assert_named_submitted_event(
|
|
instance: &Arc<Instance>,
|
|
event_type: &str,
|
|
procedure_id: &str,
|
|
expected: &str,
|
|
) {
|
|
let query = format!(
|
|
"SELECT {}, {} AS name FROM {EVENTS_TABLE} WHERE {} = '{event_type}' AND {} = '{procedure_id}' AND json_path_match({}, '$.type == \"Submitted\"') AND json_path_match({}, '$.version == 1')",
|
|
TYPE_COLUMN.name(),
|
|
TABLE_NAME_COLUMN.name(),
|
|
TYPE_COLUMN.name(),
|
|
PROCEDURE_ID_COLUMN.name(),
|
|
PROCEDURE_TRIGGER_COLUMN.name(),
|
|
PAYLOAD_COLUMN.name(),
|
|
);
|
|
assert_eventually_eq(instance, &query, expected).await;
|
|
}
|
|
|
|
#[cfg(feature = "enterprise")]
|
|
async fn assert_id_submitted_event(
|
|
instance: &Arc<Instance>,
|
|
event_type: &str,
|
|
procedure_id: &str,
|
|
expected: &str,
|
|
) {
|
|
let query = format!(
|
|
"SELECT {} FROM {EVENTS_TABLE} WHERE {} = '{event_type}' AND {} = '{procedure_id}' AND json_path_match({}, '$.type == \"Submitted\"') AND json_path_match({}, '$.version == 1')",
|
|
TYPE_COLUMN.name(),
|
|
TYPE_COLUMN.name(),
|
|
PROCEDURE_ID_COLUMN.name(),
|
|
PROCEDURE_TRIGGER_COLUMN.name(),
|
|
PAYLOAD_COLUMN.name(),
|
|
);
|
|
assert_eventually_eq(instance, &query, expected).await;
|
|
}
|
|
|
|
async fn assert_terminal_event(
|
|
instance: &Arc<Instance>,
|
|
event_type: &str,
|
|
procedure_id: &str,
|
|
terminal_trigger: &str,
|
|
table_name: Option<&str>,
|
|
table_id: Option<u32>,
|
|
) {
|
|
let table_name_predicate = table_name.map_or_else(
|
|
|| format!("{} IS NULL", TABLE_NAME_COLUMN.name()),
|
|
|table_name| format!("{} = '{table_name}'", TABLE_NAME_COLUMN.name()),
|
|
);
|
|
let catalog_and_schema_predicate = table_name.map_or_else(
|
|
|| {
|
|
format!(
|
|
"{} IS NULL AND {} IS NULL",
|
|
CATALOG_NAME_COLUMN.name(),
|
|
SCHEMA_NAME_COLUMN.name(),
|
|
)
|
|
},
|
|
|_| {
|
|
format!(
|
|
"{} = 'greptime' AND {} = 'public'",
|
|
CATALOG_NAME_COLUMN.name(),
|
|
SCHEMA_NAME_COLUMN.name(),
|
|
)
|
|
},
|
|
);
|
|
let table_id_predicate = table_id.map_or_else(
|
|
|| format!("{} IS NULL", TABLE_ID_COLUMN.name()),
|
|
|table_id| format!("{} = {table_id}", TABLE_ID_COLUMN.name()),
|
|
);
|
|
let query = format!(
|
|
"SELECT count(*) AS event_count FROM {EVENTS_TABLE} WHERE {} = '{event_type}' AND {} = '{procedure_id}' AND json_path_match({}, '$.type == \"{terminal_trigger}\"') AND json_is_null({}) AND {catalog_and_schema_predicate} AND {table_name_predicate} AND {table_id_predicate}",
|
|
TYPE_COLUMN.name(),
|
|
PROCEDURE_ID_COLUMN.name(),
|
|
PROCEDURE_TRIGGER_COLUMN.name(),
|
|
PAYLOAD_COLUMN.name(),
|
|
);
|
|
assert_single_event(instance, &query).await;
|
|
}
|
|
|
|
async fn assert_logical_terminal_event(
|
|
instance: &Arc<Instance>,
|
|
event_type: &str,
|
|
procedure_id: &str,
|
|
table_id: u32,
|
|
physical_table_id: u32,
|
|
terminal_trigger: &str,
|
|
expected: &str,
|
|
) {
|
|
let query = format!(
|
|
"SELECT {}, {} AS name FROM {EVENTS_TABLE} WHERE {} = '{event_type}' AND {} = '{procedure_id}' AND json_path_match({}, '$.type == \"{terminal_trigger}\"') AND {} = {table_id} AND {} = {physical_table_id} AND json_is_null({})",
|
|
TYPE_COLUMN.name(),
|
|
TABLE_NAME_COLUMN.name(),
|
|
TYPE_COLUMN.name(),
|
|
PROCEDURE_ID_COLUMN.name(),
|
|
PROCEDURE_TRIGGER_COLUMN.name(),
|
|
TABLE_ID_COLUMN.name(),
|
|
PHYSICAL_TABLE_ID_COLUMN.name(),
|
|
PAYLOAD_COLUMN.name(),
|
|
);
|
|
assert_eventually_eq(instance, &query, expected).await;
|
|
}
|