Skip to main content

tests_integration/
grpc.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15mod 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        // Auto created table has only one region.
558        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        // Checks if the encoded vector values are as expected.
768        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        // Test auto create not existed table upon insertion.
1050        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        // Test auto add not existed column upon insertion.
1089        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    /// The declared-edge table's first gRPC write must create it with the
1440    /// canonical definition (never the request's shape), entering below the
1441    /// user-DDL guard that rejects generic CREATE against the reserved name.
1442    #[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    /// The system-defined table is created regardless of the auto-create hint:
1464    /// its creation is a system action, not user auto-create.
1465    #[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        // An ordinary table stays subject to the hint.
1491        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}