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