1mod flight;
16mod network;
17
18use api::v1::QueryRequest;
19use api::v1::greptime_request::Request;
20use api::v1::query_request::Query;
21use common_query::OutputData;
22use common_recordbatch::RecordBatches;
23use frontend::instance::Instance;
24use servers::query_handler::grpc::GrpcQueryHandler;
25use session::context::QueryContext;
26
27#[allow(unused)]
28async fn query_and_expect(instance: &Instance, sql: &str, expected: &str) {
29 let request = Request::Query(QueryRequest {
30 query: Some(Query::Sql(sql.to_string())),
31 });
32 let output = GrpcQueryHandler::do_query(instance, request, QueryContext::arc())
33 .await
34 .unwrap();
35 let OutputData::Stream(stream) = output.data else {
36 unreachable!()
37 };
38 let recordbatches = RecordBatches::try_collect(stream).await.unwrap();
39 let actual = recordbatches.pretty_print().unwrap();
40 assert_eq!(actual, expected, "actual: {}", actual);
41}
42
43#[cfg(test)]
44mod test {
45 use std::collections::HashMap;
46 use std::sync::Arc;
47
48 use api::v1::column::Values;
49 use api::v1::column_data_type_extension::TypeExt;
50 use api::v1::ddl_request::Expr as DdlExpr;
51 use api::v1::greptime_request::Request;
52 use api::v1::query_request::Query;
53 use api::v1::region::QueryRequest as RegionQueryRequest;
54 use api::v1::{
55 AddColumn, AddColumns, AlterTableExpr, Column, ColumnDataType, ColumnDataTypeExtension,
56 ColumnDef, CreateDatabaseExpr, CreateTableExpr, DdlRequest, DeleteRequest, DeleteRequests,
57 DropTableExpr, InsertIntoPlan, InsertRequest, InsertRequests, QueryRequest, SemanticType,
58 VectorTypeExtension, alter_table_expr,
59 };
60 use auth::{
61 DefaultPermissionChecker, Identity, Password, PermissionCheckerRef, UserProvider,
62 static_user_provider_from_option,
63 };
64 use client::OutputData;
65 use common_base::Plugins;
66 use common_catalog::consts::{
67 DEFAULT_CATALOG_NAME, DEFAULT_PRIVATE_SCHEMA_NAME, MITO_ENGINE,
68 SEMANTIC_RELATIONSHIPS_DECLARED_TABLE_NAME,
69 };
70 use common_meta::rpc::router::region_distribution;
71 use common_query::Output;
72 use common_query::logical_plan::breakup_insert_plan;
73 use common_recordbatch::RecordBatches;
74 use frontend::instance::Instance;
75 use query::parser::QueryLanguageParser;
76 use query::query_engine::DefaultSerializer;
77 use rstest::rstest;
78 use rstest_reuse::apply;
79 use servers::query_handler::grpc::GrpcQueryHandler;
80 use session::context::{QueryContext, QueryContextBuilder};
81 use store_api::mito_engine_options::TWCS_TIME_WINDOW;
82 use store_api::storage::RegionId;
83 use substrait::{DFLogicalSubstraitConvertor, SubstraitPlan};
84
85 use super::*;
86 use crate::standalone::GreptimeDbStandaloneBuilder;
87 use crate::test_util::execute_sql_and_expect;
88 use crate::tests;
89 use crate::tests::MockDistributedInstance;
90 use crate::tests::test_util::{MockInstance, both_instances_cases, distributed, standalone};
91
92 #[tokio::test(flavor = "multi_thread")]
93 async fn test_distributed_handle_ddl_request() {
94 common_telemetry::init_default_ut_logging();
95 let instance =
96 tests::create_distributed_instance("test_distributed_handle_ddl_request").await;
97
98 test_handle_ddl_request(instance.frontend().as_ref()).await;
99
100 verify_table_is_dropped(&instance).await;
101 }
102
103 #[tokio::test(flavor = "multi_thread")]
104 async fn test_standalone_handle_ddl_request() {
105 let standalone = GreptimeDbStandaloneBuilder::new("test_standalone_handle_ddl_request")
106 .build()
107 .await;
108 let instance = standalone.fe_instance();
109
110 test_handle_ddl_request(instance.as_ref()).await;
111 }
112
113 #[tokio::test(flavor = "multi_thread")]
114 async fn test_distributed_handle_multi_ddl_request() {
115 common_telemetry::init_default_ut_logging();
116 let instance =
117 tests::create_distributed_instance("test_distributed_handle_multi_ddl_request").await;
118
119 test_handle_multi_ddl_request(instance.frontend().as_ref()).await;
120
121 verify_table_is_dropped(&instance).await;
122 }
123
124 #[tokio::test(flavor = "multi_thread")]
125 async fn test_standalone_handle_multi_ddl_request() {
126 let standalone =
127 GreptimeDbStandaloneBuilder::new("test_standalone_handle_multi_ddl_request")
128 .build()
129 .await;
130 let instance = standalone.fe_instance();
131
132 test_handle_multi_ddl_request(instance.as_ref()).await;
133 }
134
135 async fn query(instance: &Instance, request: Request) -> Output {
136 GrpcQueryHandler::do_query(instance, request, QueryContext::arc())
137 .await
138 .unwrap()
139 }
140
141 #[tokio::test(flavor = "multi_thread")]
142 async fn test_grpc_insert_into_plan_rejects_readonly_user() {
143 let plugins = Plugins::new();
144 plugins.insert::<PermissionCheckerRef>(DefaultPermissionChecker::arc());
145
146 let standalone =
147 GreptimeDbStandaloneBuilder::new("test_grpc_insert_into_plan_rejects_readonly_user")
148 .with_plugin(plugins)
149 .build()
150 .await;
151 let instance = standalone.fe_instance();
152 let table_name = "grpc_insert_into_plan_auth";
153
154 create_table(
155 instance,
156 format!("CREATE TABLE {table_name} (host STRING, val DOUBLE, ts TIMESTAMP TIME INDEX)"),
157 )
158 .await;
159
160 let stmt = QueryLanguageParser::parse_sql(
161 &format!("INSERT INTO {table_name} VALUES ('readonly-bypass', 42.0, 1000)"),
162 &QueryContext::arc(),
163 )
164 .unwrap();
165 let plan = instance
166 .statement_executor()
167 .plan(&stmt, QueryContext::arc())
168 .await
169 .unwrap();
170 let (table_name, insert_plan) = breakup_insert_plan(&plan, "greptime", "public").unwrap();
171 let logical_plan = DFLogicalSubstraitConvertor
172 .encode(&insert_plan, DefaultSerializer)
173 .unwrap()
174 .to_vec();
175
176 let request = Request::Query(QueryRequest {
177 query: Some(Query::InsertIntoPlan(InsertIntoPlan {
178 table_name: Some(table_name),
179 logical_plan,
180 })),
181 });
182 let ctx = QueryContext::arc();
183 let provider =
184 static_user_provider_from_option("static_user_provider:cmd:readonly:ro=readonly_pwd")
185 .unwrap();
186 let readonly_user = provider
187 .authenticate(
188 Identity::UserId("readonly", None),
189 Password::PlainText("readonly_pwd".to_string().into()),
190 )
191 .await
192 .unwrap();
193 ctx.set_current_user(readonly_user);
194
195 let err = GrpcQueryHandler::do_query(instance.as_ref(), request, ctx)
196 .await
197 .unwrap_err();
198 let err_msg = format!("{err:?}");
199 assert!(
200 err_msg.contains("not authorized"),
201 "unexpected error: {err_msg}"
202 );
203
204 query_and_expect(
205 instance,
206 "SELECT count(*) FROM grpc_insert_into_plan_auth",
207 "\
208+----------+
209| count(*) |
210+----------+
211| 0 |
212+----------+",
213 )
214 .await;
215 }
216
217 async fn test_handle_multi_ddl_request(instance: &Instance) {
218 let request = Request::Ddl(DdlRequest {
219 expr: Some(DdlExpr::CreateDatabase(CreateDatabaseExpr {
220 catalog_name: "greptime".to_string(),
221 schema_name: "database_created_through_grpc".to_string(),
222 create_if_not_exists: true,
223 options: Default::default(),
224 })),
225 });
226 let output = query(instance, request).await;
227 assert!(matches!(output.data, OutputData::AffectedRows(1)));
228
229 let request = Request::Ddl(DdlRequest {
230 expr: Some(DdlExpr::CreateTable(CreateTableExpr {
231 catalog_name: "greptime".to_string(),
232 schema_name: "database_created_through_grpc".to_string(),
233 table_name: "table_created_through_grpc".to_string(),
234 column_defs: vec![
235 ColumnDef {
236 name: "a".to_string(),
237 data_type: ColumnDataType::String as _,
238 is_nullable: true,
239 default_constraint: vec![],
240 semantic_type: SemanticType::Field as i32,
241 ..Default::default()
242 },
243 ColumnDef {
244 name: "ts".to_string(),
245 data_type: ColumnDataType::TimestampMillisecond as _,
246 is_nullable: false,
247 default_constraint: vec![],
248 semantic_type: SemanticType::Timestamp as i32,
249 ..Default::default()
250 },
251 ],
252 time_index: "ts".to_string(),
253 engine: MITO_ENGINE.to_string(),
254 ..Default::default()
255 })),
256 });
257 let output = query(instance, request).await;
258 assert!(matches!(output.data, OutputData::AffectedRows(0)));
259
260 let request = Request::Ddl(DdlRequest {
261 expr: Some(DdlExpr::AlterTable(AlterTableExpr {
262 catalog_name: "greptime".to_string(),
263 schema_name: "database_created_through_grpc".to_string(),
264 table_name: "table_created_through_grpc".to_string(),
265 kind: Some(alter_table_expr::Kind::AddColumns(AddColumns {
266 add_columns: vec![
267 AddColumn {
268 column_def: Some(ColumnDef {
269 name: "b".to_string(),
270 data_type: ColumnDataType::Int32 as _,
271 is_nullable: true,
272 default_constraint: vec![],
273 semantic_type: SemanticType::Field as i32,
274 ..Default::default()
275 }),
276 location: None,
277 add_if_not_exists: true,
278 },
279 AddColumn {
280 column_def: Some(ColumnDef {
281 name: "a".to_string(),
282 data_type: ColumnDataType::String as _,
283 is_nullable: true,
284 default_constraint: vec![],
285 semantic_type: SemanticType::Field as i32,
286 ..Default::default()
287 }),
288 location: None,
289 add_if_not_exists: true,
290 },
291 ],
292 })),
293 })),
294 });
295 let output = query(instance, request).await;
296 assert!(matches!(output.data, OutputData::AffectedRows(0)));
297
298 let request = Request::Ddl(DdlRequest {
299 expr: Some(DdlExpr::AlterTable(AlterTableExpr {
300 catalog_name: "greptime".to_string(),
301 schema_name: "database_created_through_grpc".to_string(),
302 table_name: "table_created_through_grpc".to_string(),
303 kind: Some(alter_table_expr::Kind::AddColumns(AddColumns {
304 add_columns: vec![
305 AddColumn {
306 column_def: Some(ColumnDef {
307 name: "c".to_string(),
308 data_type: ColumnDataType::Int32 as _,
309 is_nullable: true,
310 default_constraint: vec![],
311 semantic_type: SemanticType::Field as i32,
312 ..Default::default()
313 }),
314 location: None,
315 add_if_not_exists: true,
316 },
317 AddColumn {
318 column_def: Some(ColumnDef {
319 name: "d".to_string(),
320 data_type: ColumnDataType::Int32 as _,
321 is_nullable: true,
322 default_constraint: vec![],
323 semantic_type: SemanticType::Field as i32,
324 ..Default::default()
325 }),
326 location: None,
327 add_if_not_exists: true,
328 },
329 ],
330 })),
331 })),
332 });
333 let output = query(instance, request).await;
334 assert!(matches!(output.data, OutputData::AffectedRows(0)));
335
336 let request = Request::Query(QueryRequest {
337 query: Some(Query::Sql("INSERT INTO database_created_through_grpc.table_created_through_grpc (a, b, c, d, ts) VALUES ('s', 1, 1, 1, 1672816466000)".to_string()))
338 });
339 let output = query(instance, request).await;
340 assert!(matches!(output.data, OutputData::AffectedRows(1)));
341
342 let sql = "SELECT ts, a, b FROM database_created_through_grpc.table_created_through_grpc";
343 let expected = "\
344+---------------------+---+---+
345| ts | a | b |
346+---------------------+---+---+
347| 2023-01-04T07:14:26 | s | 1 |
348+---------------------+---+---+";
349 query_and_expect(instance, sql, expected).await;
350
351 let request = Request::Ddl(DdlRequest {
352 expr: Some(DdlExpr::DropTable(DropTableExpr {
353 catalog_name: "greptime".to_string(),
354 schema_name: "database_created_through_grpc".to_string(),
355 table_name: "table_created_through_grpc".to_string(),
356 ..Default::default()
357 })),
358 });
359 let output = query(instance, request).await;
360 assert!(matches!(output.data, OutputData::AffectedRows(0)));
361 }
362
363 async fn test_handle_ddl_request(instance: &Instance) {
364 let request = Request::Ddl(DdlRequest {
365 expr: Some(DdlExpr::CreateDatabase(CreateDatabaseExpr {
366 catalog_name: "greptime".to_string(),
367 schema_name: "database_created_through_grpc".to_string(),
368 create_if_not_exists: true,
369 options: Default::default(),
370 })),
371 });
372 let output = query(instance, request).await;
373 assert!(matches!(output.data, OutputData::AffectedRows(1)));
374
375 let request = Request::Ddl(DdlRequest {
376 expr: Some(DdlExpr::CreateTable(CreateTableExpr {
377 catalog_name: "greptime".to_string(),
378 schema_name: "database_created_through_grpc".to_string(),
379 table_name: "table_created_through_grpc".to_string(),
380 column_defs: vec![
381 ColumnDef {
382 name: "a".to_string(),
383 data_type: ColumnDataType::String as _,
384 is_nullable: true,
385 default_constraint: vec![],
386 semantic_type: SemanticType::Field as i32,
387 ..Default::default()
388 },
389 ColumnDef {
390 name: "ts".to_string(),
391 data_type: ColumnDataType::TimestampMillisecond as _,
392 is_nullable: false,
393 default_constraint: vec![],
394 semantic_type: SemanticType::Timestamp as i32,
395 ..Default::default()
396 },
397 ],
398 time_index: "ts".to_string(),
399 engine: MITO_ENGINE.to_string(),
400 ..Default::default()
401 })),
402 });
403 let output = query(instance, request).await;
404 assert!(matches!(output.data, OutputData::AffectedRows(0)));
405
406 let request = Request::Ddl(DdlRequest {
407 expr: Some(DdlExpr::AlterTable(AlterTableExpr {
408 catalog_name: "greptime".to_string(),
409 schema_name: "database_created_through_grpc".to_string(),
410 table_name: "table_created_through_grpc".to_string(),
411 kind: Some(alter_table_expr::Kind::AddColumns(AddColumns {
412 add_columns: vec![AddColumn {
413 column_def: Some(ColumnDef {
414 name: "b".to_string(),
415 data_type: ColumnDataType::Int32 as _,
416 is_nullable: true,
417 default_constraint: vec![],
418 semantic_type: SemanticType::Field as i32,
419 ..Default::default()
420 }),
421 location: None,
422 add_if_not_exists: false,
423 }],
424 })),
425 })),
426 });
427 let output = query(instance, request).await;
428 assert!(matches!(output.data, OutputData::AffectedRows(0)));
429
430 let request = Request::Query(QueryRequest {
431 query: Some(Query::Sql("INSERT INTO database_created_through_grpc.table_created_through_grpc (a, b, ts) VALUES ('s', 1, 1672816466000)".to_string()))
432 });
433 let output = query(instance, request).await;
434 assert!(matches!(output.data, OutputData::AffectedRows(1)));
435
436 let sql = "SELECT ts, a, b FROM database_created_through_grpc.table_created_through_grpc";
437 let expected = "\
438+---------------------+---+---+
439| ts | a | b |
440+---------------------+---+---+
441| 2023-01-04T07:14:26 | s | 1 |
442+---------------------+---+---+";
443 query_and_expect(instance, sql, expected).await;
444
445 let request = Request::Ddl(DdlRequest {
446 expr: Some(DdlExpr::DropTable(DropTableExpr {
447 catalog_name: "greptime".to_string(),
448 schema_name: "database_created_through_grpc".to_string(),
449 table_name: "table_created_through_grpc".to_string(),
450 ..Default::default()
451 })),
452 });
453 let output = query(instance, request).await;
454 assert!(matches!(output.data, OutputData::AffectedRows(0)));
455 }
456
457 async fn verify_table_is_dropped(instance: &MockDistributedInstance) {
458 assert!(
459 instance
460 .frontend()
461 .catalog_manager()
462 .table(
463 "greptime",
464 "database_created_through_grpc",
465 "table_created_through_grpc",
466 None,
467 )
468 .await
469 .unwrap()
470 .is_none()
471 );
472 }
473
474 #[tokio::test(flavor = "multi_thread")]
475 async fn test_distributed_insert_delete_and_query() {
476 common_telemetry::init_default_ut_logging();
477
478 let instance =
479 tests::create_distributed_instance("test_distributed_insert_delete_and_query").await;
480 let frontend = instance.frontend();
481 let frontend = frontend.as_ref();
482
483 let table_name = "my_dist_table";
484 let sql = format!(
485 r"
486CREATE TABLE {table_name} (
487 a INT,
488 b STRING,
489 c JSON,
490 d VECTOR(3),
491 ts TIMESTAMP,
492 TIME INDEX (ts),
493 PRIMARY KEY (a, b, c)
494) PARTITION ON COLUMNS(a) (
495 a < 10,
496 a >= 10 AND a < 20,
497 a >= 20 AND a < 50,
498 a >= 50
499)"
500 );
501 create_table(frontend, sql).await;
502
503 test_insert_delete_and_query_on_existing_table(frontend, table_name).await;
504
505 verify_data_distribution(
506 &instance,
507 table_name,
508 HashMap::from([
509 (
510 0u32,
511 "\
512+---------------------+---+-------------------+
513| ts | a | b |
514+---------------------+---+-------------------+
515| 2023-01-01T07:26:12 | 1 | ts: 1672557972000 |
516| 2023-01-01T07:26:15 | 4 | ts: 1672557975000 |
517| 2023-01-01T07:26:16 | 5 | ts: 1672557976000 |
518| 2023-01-01T07:26:17 | | ts: 1672557977000 |
519+---------------------+---+-------------------+",
520 ),
521 (
522 1u32,
523 "\
524+---------------------+----+-------------------+
525| ts | a | b |
526+---------------------+----+-------------------+
527| 2023-01-01T07:26:18 | 11 | ts: 1672557978000 |
528+---------------------+----+-------------------+",
529 ),
530 (
531 2u32,
532 "\
533+---------------------+----+-------------------+
534| ts | a | b |
535+---------------------+----+-------------------+
536| 2023-01-01T07:26:20 | 20 | ts: 1672557980000 |
537| 2023-01-01T07:26:21 | 21 | ts: 1672557981000 |
538| 2023-01-01T07:26:23 | 23 | ts: 1672557983000 |
539+---------------------+----+-------------------+",
540 ),
541 (
542 3u32,
543 "\
544+---------------------+----+-------------------+
545| ts | a | b |
546+---------------------+----+-------------------+
547| 2023-01-01T07:26:24 | 50 | ts: 1672557984000 |
548| 2023-01-01T07:26:25 | 51 | ts: 1672557985000 |
549+---------------------+----+-------------------+",
550 ),
551 ]),
552 )
553 .await;
554
555 test_insert_delete_and_query_on_auto_created_table(frontend).await;
556
557 verify_data_distribution(
559 &instance,
560 "auto_created_table",
561 HashMap::from([(
562 0u32,
563 "\
564+---------------------+---+---+
565| ts | a | b |
566+---------------------+---+---+
567| 2023-01-01T07:26:16 | | |
568| 2023-01-01T07:26:17 | 6 | |
569| 2023-01-01T07:26:18 | | x |
570| 2023-01-01T07:26:20 | | z |
571+---------------------+---+---+",
572 )]),
573 )
574 .await;
575 }
576
577 #[tokio::test(flavor = "multi_thread")]
578 async fn test_standalone_insert_and_query() {
579 common_telemetry::init_default_ut_logging();
580 let standalone = GreptimeDbStandaloneBuilder::new("test_standalone_insert_and_query")
581 .build()
582 .await;
583 let instance = standalone.fe_instance();
584
585 let table_name = "my_table";
586 let sql = format!(
587 "CREATE TABLE {table_name} (a INT, b STRING, c JSON, ts TIMESTAMP, TIME INDEX (ts), PRIMARY KEY (a, b, c))"
588 );
589 create_table(instance, sql).await;
590
591 test_insert_delete_and_query_on_existing_table(instance, table_name).await;
592
593 test_insert_delete_and_query_on_auto_created_table(instance).await
594 }
595
596 async fn create_table(frontend: &Instance, sql: String) {
597 let request = Request::Query(QueryRequest {
598 query: Some(Query::Sql(sql)),
599 });
600 let output = query(frontend, request).await;
601 assert!(matches!(output.data, OutputData::AffectedRows(0)));
602 }
603
604 async fn test_insert_delete_and_query_on_existing_table(instance: &Instance, table_name: &str) {
605 let timestamp_millisecond_values = vec![
606 1672557972000,
607 1672557973000,
608 1672557974000,
609 1672557975000,
610 1672557976000,
611 1672557977000,
612 1672557978000,
613 1672557979000,
614 1672557980000,
615 1672557981000,
616 1672557982000,
617 1672557983000,
618 1672557984000,
619 1672557985000,
620 1672557986000,
621 1672557987000,
622 ];
623 let json_strings = vec![
624 r#"{ "id": 1, "name": "Alice", "age": 30, "active": true }"#.to_string(),
625 r#"{ "id": 2, "name": "Bob", "balance": 1234.56, "active": false }"#.to_string(),
626 r#"{ "id": 3, "tags": ["rust", "testing", "json"], "age": 28 }"#.to_string(),
627 r#"{ "id": 4, "metadata": { "created_at": "2024-10-30T12:00:00Z", "status": "inactive" } }"#.to_string(),
628 r#"{ "id": 5, "name": null, "phone": "+1234567890" }"#.to_string(),
629 r#"{ "id": 6, "height": 5.9, "weight": 72.5, "active": true }"#.to_string(),
630 r#"{ "id": 7, "languages": ["English", "Spanish"], "age": 29 }"#.to_string(),
631 r#"{ "id": 8, "contact": { "email": "hank@example.com", "phone": "+0987654321" } }"#.to_string(),
632 r#"{ "id": 9, "preferences": { "notifications": true, "theme": "dark" } }"#.to_string(),
633 r#"{ "id": 10, "scores": [88, 92, 76], "active": false }"#.to_string(),
634 r#"{ "id": 11, "birthday": "1996-07-20", "location": { "city": "New York", "zip": "10001" } }"#.to_string(),
635 r#"{ "id": 12, "subscription": { "type": "premium", "expires": "2025-01-01" } }"#.to_string(),
636 r#"{ "id": 13, "settings": { "volume": 0.8, "brightness": 0.6 }, "active": true }"#.to_string(),
637 r#"{ "id": 14, "notes": ["first note", "second note"], "priority": 1 }"#.to_string(),
638 r#"{ "id": 15, "transactions": [{ "amount": 500, "date": "2024-01-01" }, { "amount": -200, "date": "2024-02-01" }] }"#.to_string(),
639 r#"{ "id": 16, "transactions": [{ "amount": 500, "date": "2024-01-01" }] }"#.to_string(),
640 ];
641 let vector_values = [
642 [1.0f32, 2.0, 3.0],
643 [4.0, 5.0, 6.0],
644 [7.0, 8.0, 9.0],
645 [10.0, 11.0, 12.0],
646 [13.0, 14.0, 15.0],
647 [16.0, 17.0, 18.0],
648 [19.0, 20.0, 21.0],
649 [22.0, 23.0, 24.0],
650 [25.0, 26.0, 27.0],
651 [28.0, 29.0, 30.0],
652 [31.0, 32.0, 33.0],
653 [34.0, 35.0, 36.0],
654 [37.0, 38.0, 39.0],
655 [40.0, 41.0, 42.0],
656 [43.0, 44.0, 45.0],
657 [46.0, 47.0, 48.0],
658 ]
659 .iter()
660 .map(|x| x.iter().flat_map(|&f| f.to_le_bytes()).collect::<Vec<u8>>())
661 .collect::<Vec<_>>();
662
663 let insert = InsertRequest {
664 table_name: table_name.to_string(),
665 columns: vec![
666 Column {
667 column_name: "a".to_string(),
668 values: Some(Values {
669 i32_values: vec![1, 2, 3, 4, 5, 11, 12, 20, 21, 22, 23, 50, 51, 52, 53],
670 ..Default::default()
671 }),
672 null_mask: vec![32, 0],
673 semantic_type: SemanticType::Tag as i32,
674 datatype: ColumnDataType::Int32 as i32,
675 ..Default::default()
676 },
677 Column {
678 column_name: "b".to_string(),
679 values: Some(Values {
680 string_values: timestamp_millisecond_values
681 .iter()
682 .map(|x| format!("ts: {x}"))
683 .collect(),
684 ..Default::default()
685 }),
686 semantic_type: SemanticType::Tag as i32,
687 datatype: ColumnDataType::String as i32,
688 ..Default::default()
689 },
690 Column {
691 column_name: "c".to_string(),
692 values: Some(Values {
693 string_values: json_strings,
694 ..Default::default()
695 }),
696 semantic_type: SemanticType::Tag as i32,
697 datatype: ColumnDataType::Json as i32,
698 ..Default::default()
699 },
700 Column {
701 column_name: "d".to_string(),
702 values: Some(Values {
703 binary_values: vector_values.clone(),
704 ..Default::default()
705 }),
706 semantic_type: SemanticType::Field as i32,
707 datatype: ColumnDataType::Vector as i32,
708 datatype_extension: Some(ColumnDataTypeExtension {
709 type_ext: Some(TypeExt::VectorType(VectorTypeExtension { dim: 3 })),
710 }),
711 ..Default::default()
712 },
713 Column {
714 column_name: "ts".to_string(),
715 values: Some(Values {
716 timestamp_millisecond_values,
717 ..Default::default()
718 }),
719 semantic_type: SemanticType::Timestamp as i32,
720 datatype: ColumnDataType::TimestampMillisecond as i32,
721 ..Default::default()
722 },
723 ],
724 row_count: 16,
725 };
726 let output = query(
727 instance,
728 Request::Inserts(InsertRequests {
729 inserts: vec![insert],
730 }),
731 )
732 .await;
733 assert!(matches!(output.data, OutputData::AffectedRows(16)));
734
735 let request = Request::Query(QueryRequest {
736 query: Some(Query::Sql(format!(
737 "SELECT ts, a, b, json_to_string(c) as c, d FROM {table_name} ORDER BY ts"
738 ))),
739 });
740 let output = query(instance, request.clone()).await;
741 let OutputData::Stream(stream) = output.data else {
742 unreachable!()
743 };
744 let recordbatches = RecordBatches::try_collect(stream).await.unwrap();
745 let expected = r#"+---------------------+----+-------------------+---------------------------------------------------------------------------------------------------+--------------------------+
746| ts | a | b | c | d |
747+---------------------+----+-------------------+---------------------------------------------------------------------------------------------------+--------------------------+
748| 2023-01-01T07:26:12 | 1 | ts: 1672557972000 | {"active":true,"age":30,"id":1,"name":"Alice"} | 0000803f0000004000004040 |
749| 2023-01-01T07:26:13 | 2 | ts: 1672557973000 | {"active":false,"balance":1234.56,"id":2,"name":"Bob"} | 000080400000a0400000c040 |
750| 2023-01-01T07:26:14 | 3 | ts: 1672557974000 | {"age":28,"id":3,"tags":["rust","testing","json"]} | 0000e0400000004100001041 |
751| 2023-01-01T07:26:15 | 4 | ts: 1672557975000 | {"id":4,"metadata":{"created_at":"2024-10-30T12:00:00Z","status":"inactive"}} | 000020410000304100004041 |
752| 2023-01-01T07:26:16 | 5 | ts: 1672557976000 | {"id":5,"name":null,"phone":"+1234567890"} | 000050410000604100007041 |
753| 2023-01-01T07:26:17 | | ts: 1672557977000 | {"active":true,"height":5.9,"id":6,"weight":72.5} | 000080410000884100009041 |
754| 2023-01-01T07:26:18 | 11 | ts: 1672557978000 | {"age":29,"id":7,"languages":["English","Spanish"]} | 000098410000a0410000a841 |
755| 2023-01-01T07:26:19 | 12 | ts: 1672557979000 | {"contact":{"email":"hank@example.com","phone":"+0987654321"},"id":8} | 0000b0410000b8410000c041 |
756| 2023-01-01T07:26:20 | 20 | ts: 1672557980000 | {"id":9,"preferences":{"notifications":true,"theme":"dark"}} | 0000c8410000d0410000d841 |
757| 2023-01-01T07:26:21 | 21 | ts: 1672557981000 | {"active":false,"id":10,"scores":[88,92,76]} | 0000e0410000e8410000f041 |
758| 2023-01-01T07:26:22 | 22 | ts: 1672557982000 | {"birthday":"1996-07-20","id":11,"location":{"city":"New York","zip":"10001"}} | 0000f8410000004200000442 |
759| 2023-01-01T07:26:23 | 23 | ts: 1672557983000 | {"id":12,"subscription":{"expires":"2025-01-01","type":"premium"}} | 0000084200000c4200001042 |
760| 2023-01-01T07:26:24 | 50 | ts: 1672557984000 | {"active":true,"id":13,"settings":{"brightness":0.6,"volume":0.8}} | 000014420000184200001c42 |
761| 2023-01-01T07:26:25 | 51 | ts: 1672557985000 | {"id":14,"notes":["first note","second note"],"priority":1} | 000020420000244200002842 |
762| 2023-01-01T07:26:26 | 52 | ts: 1672557986000 | {"id":15,"transactions":[{"amount":500,"date":"2024-01-01"},{"amount":-200,"date":"2024-02-01"}]} | 00002c420000304200003442 |
763| 2023-01-01T07:26:27 | 53 | ts: 1672557987000 | {"id":16,"transactions":[{"amount":500,"date":"2024-01-01"}]} | 0000384200003c4200004042 |
764+---------------------+----+-------------------+---------------------------------------------------------------------------------------------------+--------------------------+"#;
765 similar_asserts::assert_eq!(recordbatches.pretty_print().unwrap(), expected);
766
767 let hex_repr_of_vector_values = vector_values.iter().map(hex::encode).collect::<Vec<_>>();
769 assert_eq!(
770 hex_repr_of_vector_values,
771 vec![
772 "0000803f0000004000004040",
773 "000080400000a0400000c040",
774 "0000e0400000004100001041",
775 "000020410000304100004041",
776 "000050410000604100007041",
777 "000080410000884100009041",
778 "000098410000a0410000a841",
779 "0000b0410000b8410000c041",
780 "0000c8410000d0410000d841",
781 "0000e0410000e8410000f041",
782 "0000f8410000004200000442",
783 "0000084200000c4200001042",
784 "000014420000184200001c42",
785 "000020420000244200002842",
786 "00002c420000304200003442",
787 "0000384200003c4200004042",
788 ]
789 );
790
791 let new_grpc_delete_request = |a, b, c, d, ts, row_count| DeleteRequest {
792 table_name: table_name.to_string(),
793 key_columns: vec![
794 Column {
795 column_name: "a".to_string(),
796 semantic_type: SemanticType::Tag as i32,
797 values: Some(Values {
798 i32_values: a,
799 ..Default::default()
800 }),
801 datatype: ColumnDataType::Int32 as i32,
802 ..Default::default()
803 },
804 Column {
805 column_name: "b".to_string(),
806 semantic_type: SemanticType::Tag as i32,
807 values: Some(Values {
808 string_values: b,
809 ..Default::default()
810 }),
811 datatype: ColumnDataType::String as i32,
812 ..Default::default()
813 },
814 Column {
815 column_name: "c".to_string(),
816 values: Some(Values {
817 string_values: c,
818 ..Default::default()
819 }),
820 semantic_type: SemanticType::Tag as i32,
821 datatype: ColumnDataType::Json as i32,
822 ..Default::default()
823 },
824 Column {
825 column_name: "d".to_string(),
826 values: Some(Values {
827 binary_values: d,
828 ..Default::default()
829 }),
830 semantic_type: SemanticType::Field as i32,
831 datatype: ColumnDataType::Vector as i32,
832 datatype_extension: Some(ColumnDataTypeExtension {
833 type_ext: Some(TypeExt::VectorType(VectorTypeExtension { dim: 3 })),
834 }),
835 ..Default::default()
836 },
837 Column {
838 column_name: "ts".to_string(),
839 semantic_type: SemanticType::Timestamp as i32,
840 values: Some(Values {
841 timestamp_millisecond_values: ts,
842 ..Default::default()
843 }),
844 datatype: ColumnDataType::TimestampMillisecond as i32,
845 ..Default::default()
846 },
847 ],
848 row_count,
849 };
850 let delete1 = new_grpc_delete_request(
851 vec![2, 12, 22, 52],
852 vec![
853 "ts: 1672557973000".to_string(),
854 "ts: 1672557979000".to_string(),
855 "ts: 1672557982000".to_string(),
856 "ts: 1672557986000".to_string(),
857 ],
858 vec![
859 r#"{ "id": 2, "name": "Bob", "balance": 1234.56, "active": false }"#.to_string(),
860 r#"{ "id": 8, "contact": { "email": "hank@example.com", "phone": "+0987654321" } }"#.to_string(),
861 r#"{ "id": 11, "birthday": "1996-07-20", "location": { "city": "New York", "zip": "10001" } }"#.to_string(),
862 r#"{ "id": 15, "transactions": [{ "amount": 500, "date": "2024-01-01" }, { "amount": -200, "date": "2024-02-01" }] }"#.to_string(),
863 ],
864 vec![
865 [4.0f32, 5.0, 6.0].iter().flat_map(|f| f.to_le_bytes()).collect::<Vec<u8>>(),
866 [22.0f32, 23.0, 24.0].iter().flat_map(|f| f.to_le_bytes()).collect::<Vec<u8>>(),
867 [31.0f32, 32.0, 33.0].iter().flat_map(|f| f.to_le_bytes()).collect::<Vec<u8>>(),
868 [43.0f32, 44.0, 45.0].iter().flat_map(|f| f.to_le_bytes()).collect::<Vec<u8>>(),
869 ],
870 vec![1672557973000, 1672557979000, 1672557982000, 1672557986000],
871 4,
872 );
873 let delete2 = new_grpc_delete_request(
874 vec![3, 53],
875 vec![
876 "ts: 1672557974000".to_string(),
877 "ts: 1672557987000".to_string(),
878 ],
879 vec![
880 r#"{ "id": 3, "tags": ["rust", "testing", "json"], "age": 28 }"#.to_string(),
881 r#"{ "id": 16, "transactions": [{ "amount": 500, "date": "2024-01-01" }] }"#
882 .to_string(),
883 ],
884 vec![
885 [7.0f32, 8.0, 9.0]
886 .iter()
887 .flat_map(|f| f.to_le_bytes())
888 .collect::<Vec<u8>>(),
889 [46.0f32, 47.0, 48.0]
890 .iter()
891 .flat_map(|f| f.to_le_bytes())
892 .collect::<Vec<u8>>(),
893 ],
894 vec![1672557974000, 1672557987000],
895 2,
896 );
897 let output = query(
898 instance,
899 Request::Deletes(DeleteRequests {
900 deletes: vec![delete1, delete2],
901 }),
902 )
903 .await;
904 assert!(matches!(output.data, OutputData::AffectedRows(6)));
905
906 let output = query(instance, request).await;
907 let OutputData::Stream(stream) = output.data else {
908 unreachable!()
909 };
910 let recordbatches = RecordBatches::try_collect(stream).await.unwrap();
911 let expected = r#"+---------------------+----+-------------------+-------------------------------------------------------------------------------+--------------------------+
912| ts | a | b | c | d |
913+---------------------+----+-------------------+-------------------------------------------------------------------------------+--------------------------+
914| 2023-01-01T07:26:12 | 1 | ts: 1672557972000 | {"active":true,"age":30,"id":1,"name":"Alice"} | 0000803f0000004000004040 |
915| 2023-01-01T07:26:15 | 4 | ts: 1672557975000 | {"id":4,"metadata":{"created_at":"2024-10-30T12:00:00Z","status":"inactive"}} | 000020410000304100004041 |
916| 2023-01-01T07:26:16 | 5 | ts: 1672557976000 | {"id":5,"name":null,"phone":"+1234567890"} | 000050410000604100007041 |
917| 2023-01-01T07:26:17 | | ts: 1672557977000 | {"active":true,"height":5.9,"id":6,"weight":72.5} | 000080410000884100009041 |
918| 2023-01-01T07:26:18 | 11 | ts: 1672557978000 | {"age":29,"id":7,"languages":["English","Spanish"]} | 000098410000a0410000a841 |
919| 2023-01-01T07:26:20 | 20 | ts: 1672557980000 | {"id":9,"preferences":{"notifications":true,"theme":"dark"}} | 0000c8410000d0410000d841 |
920| 2023-01-01T07:26:21 | 21 | ts: 1672557981000 | {"active":false,"id":10,"scores":[88,92,76]} | 0000e0410000e8410000f041 |
921| 2023-01-01T07:26:23 | 23 | ts: 1672557983000 | {"id":12,"subscription":{"expires":"2025-01-01","type":"premium"}} | 0000084200000c4200001042 |
922| 2023-01-01T07:26:24 | 50 | ts: 1672557984000 | {"active":true,"id":13,"settings":{"brightness":0.6,"volume":0.8}} | 000014420000184200001c42 |
923| 2023-01-01T07:26:25 | 51 | ts: 1672557985000 | {"id":14,"notes":["first note","second note"],"priority":1} | 000020420000244200002842 |
924+---------------------+----+-------------------+-------------------------------------------------------------------------------+--------------------------+"#;
925 similar_asserts::assert_eq!(recordbatches.pretty_print().unwrap(), expected);
926 }
927
928 async fn verify_data_distribution(
929 instance: &MockDistributedInstance,
930 table_name: &str,
931 expected_distribution: HashMap<u32, &str>,
932 ) {
933 let table = instance
934 .frontend()
935 .catalog_manager()
936 .table("greptime", "public", table_name, None)
937 .await
938 .unwrap()
939 .unwrap();
940 let table_id = table.table_info().ident.table_id;
941 let table_route_value = instance
942 .table_metadata_manager()
943 .table_route_manager()
944 .table_route_storage()
945 .get(table_id)
946 .await
947 .unwrap()
948 .unwrap();
949
950 let region_to_dn_map = region_distribution(
951 table_route_value
952 .region_routes()
953 .expect("physical table route"),
954 )
955 .iter()
956 .map(|(k, v)| (v.leader_regions[0], *k))
957 .collect::<HashMap<u32, u64>>();
958 assert!(region_to_dn_map.len() <= instance.datanodes().len());
959
960 let stmt = QueryLanguageParser::parse_sql(
961 &format!("SELECT ts, a, b FROM {table_name} ORDER BY ts"),
962 &QueryContext::arc(),
963 )
964 .unwrap();
965 let plan = instance
966 .frontend()
967 .statement_executor()
968 .plan(&stmt, QueryContext::arc())
969 .await
970 .unwrap();
971 let plan = DFLogicalSubstraitConvertor
972 .encode(&plan, DefaultSerializer)
973 .unwrap();
974
975 for (region, dn) in region_to_dn_map.iter() {
976 let region_server = instance.datanodes().get(dn).unwrap().region_server();
977
978 let region_id = RegionId::new(table_id, *region);
979
980 let stream = region_server
981 .handle_remote_read(
982 RegionQueryRequest {
983 region_id: region_id.as_u64(),
984 plan: plan.to_vec(),
985 ..Default::default()
986 },
987 QueryContext::arc(),
988 )
989 .await
990 .unwrap();
991
992 let recordbatches = RecordBatches::try_collect(stream).await.unwrap();
993 let actual = recordbatches.pretty_print().unwrap();
994
995 let expected = expected_distribution.get(region).unwrap();
996 assert_eq!(&actual, expected);
997 }
998 }
999
1000 async fn test_insert_delete_and_query_on_auto_created_table(instance: &Instance) {
1001 let insert = InsertRequest {
1002 table_name: "auto_created_table".to_string(),
1003 columns: vec![
1004 Column {
1005 column_name: "a".to_string(),
1006 values: Some(Values {
1007 i32_values: vec![4, 6],
1008 ..Default::default()
1009 }),
1010 null_mask: vec![2],
1011 semantic_type: SemanticType::Field as i32,
1012 datatype: ColumnDataType::Int32 as i32,
1013 ..Default::default()
1014 },
1015 Column {
1016 column_name: "c".to_string(),
1017 values: Some(Values {
1018 string_values: vec![
1019 r#"{ "id": 1, "name": "Alice", "age": 30, "active": true }"#
1020 .to_string(),
1021 r#"{ "id": 2, "name": "Bob", "balance": 1234.56, "active": false }"#
1022 .to_string(),
1023 ],
1024 ..Default::default()
1025 }),
1026 null_mask: vec![2],
1027 semantic_type: SemanticType::Field as i32,
1028 datatype: ColumnDataType::Json as i32,
1029 ..Default::default()
1030 },
1031 Column {
1032 column_name: "ts".to_string(),
1033 values: Some(Values {
1034 timestamp_millisecond_values: vec![
1035 1672557975000,
1036 1672557976000,
1037 1672557977000,
1038 ],
1039 ..Default::default()
1040 }),
1041 semantic_type: SemanticType::Timestamp as i32,
1042 datatype: ColumnDataType::TimestampMillisecond as i32,
1043 ..Default::default()
1044 },
1045 ],
1046 row_count: 3,
1047 };
1048
1049 let request = Request::Inserts(InsertRequests {
1051 inserts: vec![insert],
1052 });
1053 let output = query(instance, request).await;
1054 assert!(matches!(output.data, OutputData::AffectedRows(3)));
1055
1056 let insert = InsertRequest {
1057 table_name: "auto_created_table".to_string(),
1058 columns: vec![
1059 Column {
1060 column_name: "b".to_string(),
1061 values: Some(Values {
1062 string_values: vec!["x".to_string(), "z".to_string()],
1063 ..Default::default()
1064 }),
1065 null_mask: vec![2],
1066 semantic_type: SemanticType::Field as i32,
1067 datatype: ColumnDataType::String as i32,
1068 ..Default::default()
1069 },
1070 Column {
1071 column_name: "ts".to_string(),
1072 values: Some(Values {
1073 timestamp_millisecond_values: vec![
1074 1672557978000,
1075 1672557979000,
1076 1672557980000,
1077 ],
1078 ..Default::default()
1079 }),
1080 semantic_type: SemanticType::Timestamp as i32,
1081 datatype: ColumnDataType::TimestampMillisecond as i32,
1082 ..Default::default()
1083 },
1084 ],
1085 row_count: 3,
1086 };
1087
1088 let request = Request::Inserts(InsertRequests {
1090 inserts: vec![insert],
1091 });
1092 let output = query(instance, request).await;
1093 assert!(matches!(output.data, OutputData::AffectedRows(3)));
1094
1095 let request = Request::Query(QueryRequest {
1096 query: Some(Query::Sql(
1097 "SELECT ts, a, b, json_to_string(c) as c FROM auto_created_table order by ts"
1098 .to_string(),
1099 )),
1100 });
1101 let output = query(instance, request.clone()).await;
1102 let OutputData::Stream(stream) = output.data else {
1103 unreachable!()
1104 };
1105 let recordbatches = RecordBatches::try_collect(stream).await.unwrap();
1106 let expected = r#"+---------------------+---+---+--------------------------------------------------------+
1107| ts | a | b | c |
1108+---------------------+---+---+--------------------------------------------------------+
1109| 2023-01-01T07:26:15 | 4 | | {"active":true,"age":30,"id":1,"name":"Alice"} |
1110| 2023-01-01T07:26:16 | | | |
1111| 2023-01-01T07:26:17 | 6 | | {"active":false,"balance":1234.56,"id":2,"name":"Bob"} |
1112| 2023-01-01T07:26:18 | | x | |
1113| 2023-01-01T07:26:19 | | | |
1114| 2023-01-01T07:26:20 | | z | |
1115+---------------------+---+---+--------------------------------------------------------+"#;
1116 similar_asserts::assert_eq!(recordbatches.pretty_print().unwrap(), expected);
1117
1118 let delete = DeleteRequest {
1119 table_name: "auto_created_table".to_string(),
1120 key_columns: vec![Column {
1121 column_name: "ts".to_string(),
1122 values: Some(Values {
1123 timestamp_millisecond_values: vec![1672557975000, 1672557979000],
1124 ..Default::default()
1125 }),
1126 semantic_type: SemanticType::Timestamp as i32,
1127 datatype: ColumnDataType::TimestampMillisecond as i32,
1128 ..Default::default()
1129 }],
1130 row_count: 2,
1131 };
1132
1133 let output = query(
1134 instance,
1135 Request::Deletes(DeleteRequests {
1136 deletes: vec![delete],
1137 }),
1138 )
1139 .await;
1140 assert!(matches!(output.data, OutputData::AffectedRows(2)));
1141
1142 let output = query(instance, request).await;
1143 let OutputData::Stream(stream) = output.data else {
1144 unreachable!()
1145 };
1146 let recordbatches = RecordBatches::try_collect(stream).await.unwrap();
1147 let expected = r#"+---------------------+---+---+--------------------------------------------------------+
1148| ts | a | b | c |
1149+---------------------+---+---+--------------------------------------------------------+
1150| 2023-01-01T07:26:16 | | | |
1151| 2023-01-01T07:26:17 | 6 | | {"active":false,"balance":1234.56,"id":2,"name":"Bob"} |
1152| 2023-01-01T07:26:18 | | x | |
1153| 2023-01-01T07:26:20 | | z | |
1154+---------------------+---+---+--------------------------------------------------------+"#;
1155 similar_asserts::assert_eq!(recordbatches.pretty_print().unwrap(), expected);
1156 }
1157
1158 #[tokio::test(flavor = "multi_thread")]
1159 async fn test_promql_query() {
1160 let standalone = GreptimeDbStandaloneBuilder::new("test_standalone_promql_query")
1161 .build()
1162 .await;
1163 let instance = standalone.fe_instance();
1164
1165 let table_name = "my_table";
1166 let sql = format!(
1167 "CREATE TABLE {table_name} (h string, a double, ts TIMESTAMP, TIME INDEX (ts), PRIMARY KEY(h))"
1168 );
1169 create_table(instance, sql).await;
1170
1171 let insert = InsertRequest {
1172 table_name: table_name.to_string(),
1173 columns: vec![
1174 Column {
1175 column_name: "h".to_string(),
1176 values: Some(Values {
1177 string_values: vec![
1178 "t".to_string(),
1179 "t".to_string(),
1180 "t".to_string(),
1181 "t".to_string(),
1182 "t".to_string(),
1183 "t".to_string(),
1184 "t".to_string(),
1185 "t".to_string(),
1186 ],
1187 ..Default::default()
1188 }),
1189 semantic_type: SemanticType::Tag as i32,
1190 datatype: ColumnDataType::String as i32,
1191 ..Default::default()
1192 },
1193 Column {
1194 column_name: "a".to_string(),
1195 values: Some(Values {
1196 f64_values: vec![1f64, 11f64, 20f64, 22f64, 50f64, 55f64, 99f64],
1197 ..Default::default()
1198 }),
1199 null_mask: vec![4],
1200 semantic_type: SemanticType::Field as i32,
1201 datatype: ColumnDataType::Float64 as i32,
1202 ..Default::default()
1203 },
1204 Column {
1205 column_name: "ts".to_string(),
1206 values: Some(Values {
1207 timestamp_millisecond_values: vec![
1208 1672557972000,
1209 1672557973000,
1210 1672557974000,
1211 1672557975000,
1212 1672557976000,
1213 1672557977000,
1214 1672557978000,
1215 1672557979000,
1216 ],
1217 ..Default::default()
1218 }),
1219 semantic_type: SemanticType::Timestamp as i32,
1220 datatype: ColumnDataType::TimestampMillisecond as i32,
1221 ..Default::default()
1222 },
1223 ],
1224 row_count: 8,
1225 };
1226
1227 let request = Request::Inserts(InsertRequests {
1228 inserts: vec![insert],
1229 });
1230 let output = query(instance, request).await;
1231 assert!(matches!(output.data, OutputData::AffectedRows(8)));
1232
1233 let request = Request::Query(QueryRequest {
1234 query: Some(Query::PromRangeQuery(api::v1::PromRangeQuery {
1235 query: "my_table".to_owned(),
1236 start: "1672557973".to_owned(),
1237 end: "1672557978".to_owned(),
1238 step: "1s".to_owned(),
1239 lookback: "5m".to_string(),
1240 })),
1241 });
1242 let output = query(instance, request).await;
1243 let OutputData::Stream(stream) = output.data else {
1244 unreachable!()
1245 };
1246 let recordbatches = RecordBatches::try_collect(stream).await.unwrap();
1247 let expected = "\
1248+---+------+---------------------+
1249| h | a | ts |
1250+---+------+---------------------+
1251| t | 11.0 | 2023-01-01T07:26:13 |
1252| t | | 2023-01-01T07:26:14 |
1253| t | 20.0 | 2023-01-01T07:26:15 |
1254| t | 22.0 | 2023-01-01T07:26:16 |
1255| t | 50.0 | 2023-01-01T07:26:17 |
1256| t | 55.0 | 2023-01-01T07:26:18 |
1257+---+------+---------------------+";
1258 assert_eq!(recordbatches.pretty_print().unwrap(), expected);
1259 }
1260
1261 #[apply(both_instances_cases)]
1262 async fn test_extra_external_table_options(instance: Arc<dyn MockInstance>) {
1263 common_telemetry::init_default_ut_logging();
1264 let frontend = instance.frontend();
1265 let instance = frontend.as_ref();
1266
1267 let insert = InsertRequest {
1268 table_name: "auto_created_table".to_string(),
1269 columns: vec![
1270 Column {
1271 column_name: "a".to_string(),
1272 values: Some(Values {
1273 i32_values: vec![4, 6],
1274 ..Default::default()
1275 }),
1276 null_mask: vec![2],
1277 semantic_type: SemanticType::Field as i32,
1278 datatype: ColumnDataType::Int32 as i32,
1279 ..Default::default()
1280 },
1281 Column {
1282 column_name: "c".to_string(),
1283 values: Some(Values {
1284 string_values: vec![
1285 r#"{ "id": 1, "name": "Alice", "age": 30, "active": true }"#
1286 .to_string(),
1287 r#"{ "id": 2, "name": "Bob", "balance": 1234.56, "active": false }"#
1288 .to_string(),
1289 ],
1290 ..Default::default()
1291 }),
1292 null_mask: vec![2],
1293 semantic_type: SemanticType::Field as i32,
1294 datatype: ColumnDataType::Json as i32,
1295 ..Default::default()
1296 },
1297 Column {
1298 column_name: "ts".to_string(),
1299 values: Some(Values {
1300 timestamp_millisecond_values: vec![
1301 1672557975000,
1302 1672557976000,
1303 1672557977000,
1304 ],
1305 ..Default::default()
1306 }),
1307 semantic_type: SemanticType::Timestamp as i32,
1308 datatype: ColumnDataType::TimestampMillisecond as i32,
1309 ..Default::default()
1310 },
1311 ],
1312 row_count: 3,
1313 };
1314 let request = Request::Inserts(InsertRequests {
1315 inserts: vec![insert],
1316 });
1317
1318 let ctx = Arc::new(
1319 QueryContextBuilder::default()
1320 .set_extension(TWCS_TIME_WINDOW.to_string(), "1d".to_string())
1321 .build(),
1322 );
1323 let output = GrpcQueryHandler::do_query(instance, request, ctx)
1324 .await
1325 .unwrap();
1326 assert!(matches!(output.data, OutputData::AffectedRows(3)));
1327
1328 let sql = "show create table auto_created_table";
1329 let expected = r#"+--------------------+---------------------------------------------------+
1330| Table | Create Table |
1331+--------------------+---------------------------------------------------+
1332| auto_created_table | CREATE TABLE IF NOT EXISTS "auto_created_table" ( |
1333| | "a" INT NULL, |
1334| | "c" JSON NULL, |
1335| | "ts" TIMESTAMP(3) NOT NULL, |
1336| | TIME INDEX ("ts") |
1337| | ) |
1338| | |
1339| | ENGINE=mito |
1340| | WITH( |
1341| | 'comment' = 'Created on insertion', |
1342| | 'compaction.twcs.time_window' = '1d', |
1343| | 'compaction.type' = 'twcs' |
1344| | ) |
1345+--------------------+---------------------------------------------------+"#;
1346 execute_sql_and_expect(&frontend, sql, expected).await;
1347 }
1348
1349 fn declared_relationships_row_insert() -> Request {
1350 use api::v1::value::ValueData;
1351 use api::v1::{ColumnSchema, Row, RowInsertRequest, RowInsertRequests, Rows, Value};
1352
1353 let string_column = |name: &str, semantic: SemanticType| ColumnSchema {
1354 column_name: name.to_string(),
1355 datatype: ColumnDataType::String as i32,
1356 semantic_type: semantic as i32,
1357 ..Default::default()
1358 };
1359 let string_value = |value: &str| Value {
1360 value_data: Some(ValueData::StringValue(value.to_string())),
1361 };
1362 let mut schema = vec![ColumnSchema {
1363 column_name: "observed_at".to_string(),
1364 datatype: ColumnDataType::TimestampMillisecond as i32,
1365 semantic_type: SemanticType::Timestamp as i32,
1366 ..Default::default()
1367 }];
1368 let mut values = vec![Value {
1369 value_data: Some(ValueData::TimestampMillisecondValue(1000)),
1370 }];
1371 for (tag, value) in [
1372 ("src_type", "service"),
1373 ("src_id", "frontend"),
1374 ("rel_type", "depends_on"),
1375 ("dst_type", "service"),
1376 ("dst_id", "users-db"),
1377 ("provenance", "declared"),
1378 ("scope", ""),
1379 ("generation_id", ""),
1380 ] {
1381 schema.push(string_column(tag, SemanticType::Tag));
1382 values.push(string_value(value));
1383 }
1384
1385 Request::RowInserts(RowInsertRequests {
1386 inserts: vec![RowInsertRequest {
1387 table_name: SEMANTIC_RELATIONSHIPS_DECLARED_TABLE_NAME.to_string(),
1388 rows: Some(Rows {
1389 schema,
1390 rows: vec![Row { values }],
1391 }),
1392 }],
1393 })
1394 }
1395
1396 async fn assert_declared_relationships_table_is_canonical(instance: &Instance) {
1397 let table = instance
1398 .catalog_manager()
1399 .table(
1400 DEFAULT_CATALOG_NAME,
1401 DEFAULT_PRIVATE_SCHEMA_NAME,
1402 SEMANTIC_RELATIONSHIPS_DECLARED_TABLE_NAME,
1403 None,
1404 )
1405 .await
1406 .unwrap()
1407 .expect("declared-edge table must exist");
1408 let table_info = table.table_info();
1409 assert_eq!(table_info.meta.schema.column_schemas().len(), 20);
1410 assert_eq!(
1411 table_info
1412 .meta
1413 .schema
1414 .timestamp_column()
1415 .map(|c| c.name.as_str()),
1416 Some("observed_at")
1417 );
1418 let primary_keys: Vec<&str> = table_info
1419 .meta
1420 .primary_key_indices
1421 .iter()
1422 .map(|idx| table_info.meta.schema.column_schemas()[*idx].name.as_str())
1423 .collect();
1424 assert_eq!(
1425 primary_keys,
1426 [
1427 "src_type",
1428 "src_id",
1429 "rel_type",
1430 "dst_type",
1431 "dst_id",
1432 "provenance",
1433 "scope",
1434 "generation_id",
1435 ]
1436 );
1437 }
1438
1439 #[tokio::test(flavor = "multi_thread")]
1443 async fn test_grpc_first_write_creates_declared_relationships_table() {
1444 common_telemetry::init_default_ut_logging();
1445 let standalone = GreptimeDbStandaloneBuilder::new("test_grpc_declared_first_write")
1446 .build()
1447 .await;
1448 let instance = standalone.fe_instance();
1449
1450 let ctx = Arc::new(QueryContext::with(
1451 DEFAULT_CATALOG_NAME,
1452 DEFAULT_PRIVATE_SCHEMA_NAME,
1453 ));
1454 let output =
1455 GrpcQueryHandler::do_query(instance.as_ref(), declared_relationships_row_insert(), ctx)
1456 .await
1457 .unwrap();
1458 assert!(matches!(output.data, OutputData::AffectedRows(1)));
1459
1460 assert_declared_relationships_table_is_canonical(instance).await;
1461 }
1462
1463 #[tokio::test(flavor = "multi_thread")]
1466 async fn test_grpc_declared_relationships_bypasses_auto_create_hint() {
1467 use api::v1::value::ValueData;
1468 use api::v1::{ColumnSchema, Row, RowInsertRequest, RowInsertRequests, Rows, Value};
1469
1470 common_telemetry::init_default_ut_logging();
1471 let standalone = GreptimeDbStandaloneBuilder::new("test_grpc_declared_no_auto_create")
1472 .build()
1473 .await;
1474 let instance = standalone.fe_instance();
1475
1476 let mut ctx = QueryContext::with(DEFAULT_CATALOG_NAME, DEFAULT_PRIVATE_SCHEMA_NAME);
1477 ctx.set_extension(table::requests::AUTO_CREATE_TABLE_KEY, "false");
1478 let ctx = Arc::new(ctx);
1479
1480 let output = GrpcQueryHandler::do_query(
1481 instance.as_ref(),
1482 declared_relationships_row_insert(),
1483 ctx.clone(),
1484 )
1485 .await
1486 .unwrap();
1487 assert!(matches!(output.data, OutputData::AffectedRows(1)));
1488 assert_declared_relationships_table_is_canonical(instance).await;
1489
1490 let request = Request::RowInserts(RowInsertRequests {
1492 inserts: vec![RowInsertRequest {
1493 table_name: "ordinary_table".to_string(),
1494 rows: Some(Rows {
1495 schema: vec![ColumnSchema {
1496 column_name: "ts".to_string(),
1497 datatype: ColumnDataType::TimestampMillisecond as i32,
1498 semantic_type: SemanticType::Timestamp as i32,
1499 ..Default::default()
1500 }],
1501 rows: vec![Row {
1502 values: vec![Value {
1503 value_data: Some(ValueData::TimestampMillisecondValue(1000)),
1504 }],
1505 }],
1506 }),
1507 }],
1508 });
1509 let err = GrpcQueryHandler::do_query(instance.as_ref(), request, ctx)
1510 .await
1511 .unwrap_err();
1512 let msg = common_error::ext::ErrorExt::output_msg(&err);
1513 assert!(msg.contains("auto_create_table"), "unexpected error: {msg}");
1514 }
1515}