Skip to main content

tests_integration/
test_util.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
15use std::collections::BTreeMap;
16#[cfg(test)]
17use std::collections::HashMap;
18use std::env;
19use std::fmt::Display;
20use std::net::SocketAddr;
21use std::sync::Arc;
22use std::time::Duration;
23
24use api::v1::Basic;
25use auth::{
26    DefaultPermissionChecker, PermissionCheckerRef, UserProviderRef, user_provider_from_option,
27};
28use axum::Router;
29use catalog::kvbackend::KvBackendCatalogManager;
30use client::{Client, Database};
31use common_base::Plugins;
32use common_catalog::consts::MIN_USER_TABLE_ID;
33use common_config::Configurable;
34use common_event_recorder::EventRecorderOptions;
35#[cfg(test)]
36use common_meta::DatanodeId;
37use common_meta::key::TableMetadataManager;
38use common_meta::key::catalog_name::CatalogNameKey;
39use common_meta::key::schema_name::SchemaNameKey;
40use common_meta::kv_backend::KvBackendRef;
41use common_query::Output;
42use common_runtime::runtime::BuilderBuild;
43use common_runtime::{Builder as RuntimeBuilder, Runtime};
44use common_test_util::ports;
45use common_test_util::temp_dir::{TempDir, create_temp_dir};
46use common_wal::config::DatanodeWalConfig;
47use datanode::config::{DatanodeOptions, StorageConfig};
48#[cfg(test)]
49use datanode::datanode::Datanode;
50use frontend::instance::Instance;
51use frontend::service_config::{MysqlOptions, PostgresOptions};
52#[cfg(test)]
53use meta_srv::metasrv::Metasrv;
54use mito2::engine::MitoEngine;
55use mito2::gc::GcConfig;
56use object_store::config::{
57    AzblobConfig, FileConfig, GcsConfig, ObjectStoreConfig, OssConfig, S3Config,
58};
59use object_store::services::{Azblob, Gcs, Oss, S3};
60use object_store::test_util::TempFolder;
61use object_store::{AzblobConnection, GcsConnection, ObjectStore, OssConnection, S3Connection};
62use servers::batcher::logical_table::LogicalTablePendingRowsBatcher;
63use servers::grpc::builder::GrpcServerBuilder;
64use servers::grpc::greptime_handler::GreptimeRequestHandler;
65use servers::grpc::{FlightCompression, GrpcOptions, GrpcServer, GrpcServerConfig};
66use servers::http::{HttpOptions, HttpServerBuilder};
67use servers::metrics_handler::MetricsHandler;
68use servers::mysql::server::{MysqlServer, MysqlSpawnConfig, MysqlSpawnRef};
69use servers::otel_arrow::OtelArrowServiceHandler;
70use servers::postgres::PostgresServer;
71use servers::prom_remote_write::validation::PromValidationMode;
72use servers::query_handler::sql::SqlQueryHandler;
73use servers::request_memory_limiter::ServerMemoryLimiter;
74use servers::server::Server;
75use servers::tls::ReloadableTlsServerConfig;
76use session::context::QueryContext;
77use store_api::metric_engine_consts::METRIC_METADATA_REGION_GROUP;
78use store_api::region_engine::RegionEngine;
79use store_api::region_request::{RegionFlushRequest, RegionRequest};
80use store_api::storage::RegionId;
81
82use crate::cluster::{GreptimeDbCluster, GreptimeDbClusterBuilder};
83use crate::standalone::{GreptimeDbStandalone, GreptimeDbStandaloneBuilder};
84
85/// Maps `(node_id, region_id)` to `(flushed_sequence, flushed_entry_id)`.
86pub type WalSnapshot = BTreeMap<(u64, RegionId), (u64, u64)>;
87
88/// Flush user data regions before observing their persisted WAL watermarks.
89/// Metadata regions write WAL independently and are excluded from this check.
90async fn flush_and_snapshot_region_wal(engine: &MitoEngine) -> WalSnapshot {
91    let mut snapshot = BTreeMap::new();
92    for region in engine.regions() {
93        let id = region.region_id();
94        if id.table_id() < MIN_USER_TABLE_ID || id.region_group() == METRIC_METADATA_REGION_GROUP {
95            continue;
96        }
97        engine
98            .handle_request(id, RegionRequest::Flush(RegionFlushRequest::default()))
99            .await
100            .unwrap();
101        let statistic = engine.region_statistic(id).unwrap();
102        snapshot.insert(
103            (0, id),
104            (
105                region.flushed_sequence(),
106                statistic.manifest.data_flushed_entry_id(),
107            ),
108        );
109    }
110    snapshot
111}
112
113/// Require a real write, then check WAL policy independently of row deduplication.
114pub fn assert_wal_delta(before: &WalSnapshot, after: &WalSnapshot, skip_wal: bool) {
115    assert_eq!(
116        before.keys().collect::<Vec<_>>(),
117        after.keys().collect::<Vec<_>>(),
118        "warm up table creation before taking the snapshot"
119    );
120    let mut written = 0;
121    for (id, &(flushed_sequence, flushed_entry_id)) in after {
122        let (previous_flushed_sequence, previous_flushed_entry_id) = before[id];
123        if flushed_sequence > previous_flushed_sequence {
124            written += 1;
125            if skip_wal {
126                assert_eq!(
127                    flushed_entry_id, previous_flushed_entry_id,
128                    "region {id:?} wrote WAL despite the request policy"
129                );
130            } else {
131                assert!(
132                    flushed_entry_id > previous_flushed_entry_id,
133                    "region {id:?} did not write WAL"
134                );
135            }
136        }
137    }
138    assert!(
139        written > 0,
140        "request must reach a data region, not merely return success"
141    );
142}
143
144/// A test instance backed by embedded storage or a distributed cluster.
145pub enum MockInstanceImpl {
146    Standalone(GreptimeDbStandalone),
147    Distributed(GreptimeDbCluster),
148}
149
150impl MockInstanceImpl {
151    /// Returns the metasrv of a distributed instance.
152    ///
153    /// # Panics
154    /// Panics if this is a standalone instance.
155    #[cfg(test)]
156    pub(crate) fn metasrv(&self) -> &Arc<Metasrv> {
157        match self {
158            Self::Standalone(_) => unreachable!(),
159            Self::Distributed(instance) => &instance.metasrv,
160        }
161    }
162
163    /// Returns the datanodes of a distributed instance.
164    ///
165    /// # Panics
166    /// Panics if this is a standalone instance.
167    #[cfg(test)]
168    pub(crate) fn datanodes(&self) -> &HashMap<DatanodeId, Datanode> {
169        match self {
170            Self::Standalone(_) => unreachable!(),
171            Self::Distributed(instance) => &instance.datanode_instances,
172        }
173    }
174
175    /// Creates a standalone instance or a three-datanode cluster using local storage.
176    pub async fn new(name: &str, distributed: bool) -> Self {
177        let name = format!(
178            "{name}_{}",
179            if distributed {
180                "distributed"
181            } else {
182                "standalone"
183            }
184        );
185        if distributed {
186            // The repository cluster harness uses real tonic/protobuf services
187            // over duplex transports between FE and datanodes, not direct calls.
188            Self::Distributed(
189                GreptimeDbClusterBuilder::new(&name)
190                    .await
191                    .with_datanodes(3)
192                    .build(false)
193                    .await,
194            )
195        } else {
196            Self::Standalone(GreptimeDbStandaloneBuilder::new(&name).build().await)
197        }
198    }
199
200    /// Returns the frontend instance.
201    pub fn frontend(&self) -> Arc<Instance> {
202        match self {
203            Self::Standalone(instance) => instance.fe_instance().clone(),
204            Self::Distributed(cluster) => cluster.fe_instance().clone(),
205        }
206    }
207
208    /// Flushes user data regions and returns their write and WAL watermarks.
209    pub async fn flush_and_snapshot_wal(&self) -> WalSnapshot {
210        match self {
211            Self::Standalone(instance) => {
212                flush_and_snapshot_region_wal(&instance.mito_engine).await
213            }
214            Self::Distributed(cluster) => {
215                let mut result = BTreeMap::new();
216                for (node_id, datanode) in &cluster.datanode_instances {
217                    let engine = datanode.region_server().mito_engine().unwrap();
218                    result.extend(
219                        flush_and_snapshot_region_wal(&engine)
220                            .await
221                            .into_iter()
222                            .map(|((_, region_id), watermarks)| {
223                                ((*node_id, region_id), watermarks)
224                            }),
225                    );
226                }
227                result
228            }
229        }
230    }
231
232    /// Shuts down cluster services and removes the test storage.
233    pub async fn shutdown(&mut self) {
234        match self {
235            Self::Standalone(instance) => instance.guard.remove_all().await,
236            Self::Distributed(cluster) => {
237                cluster.metasrv.shutdown().await.unwrap();
238                for datanode in cluster.datanode_instances.values_mut() {
239                    datanode.shutdown().await.unwrap();
240                }
241                for guard in &mut cluster.guards {
242                    guard.remove_all().await;
243                }
244            }
245        }
246    }
247}
248
249pub const PEER_PLACEHOLDER_ADDR: &str = "127.0.0.1:3001";
250
251#[derive(Debug, Clone, Copy, Eq, PartialEq)]
252pub enum StorageType {
253    S3,
254    S3WithCache,
255    File,
256    Oss,
257    Azblob,
258    Gcs,
259}
260
261impl Display for StorageType {
262    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
263        match self {
264            StorageType::S3 => write!(f, "S3"),
265            StorageType::S3WithCache => write!(f, "S3"),
266            StorageType::File => write!(f, "File"),
267            StorageType::Oss => write!(f, "Oss"),
268            StorageType::Azblob => write!(f, "Azblob"),
269            StorageType::Gcs => write!(f, "Gcs"),
270        }
271    }
272}
273
274impl StorageType {
275    pub fn build_storage_types_based_on_env() -> Vec<StorageType> {
276        let mut storage_types = Vec::with_capacity(4);
277        storage_types.push(StorageType::File);
278        if let Ok(bucket) = env::var("GT_S3_BUCKET")
279            && !bucket.is_empty()
280        {
281            storage_types.push(StorageType::S3);
282        }
283        if env::var("GT_OSS_BUCKET").is_ok() {
284            storage_types.push(StorageType::Oss);
285        }
286        if env::var("GT_AZBLOB_CONTAINER").is_ok() {
287            storage_types.push(StorageType::Azblob);
288        }
289        if env::var("GT_GCS_BUCKET").is_ok() {
290            storage_types.push(StorageType::Gcs);
291        }
292        storage_types
293    }
294
295    pub fn test_on(&self) -> bool {
296        let _ = dotenv::dotenv();
297
298        match self {
299            StorageType::File => true, // always test file
300            StorageType::S3 | StorageType::S3WithCache => {
301                if let Ok(b) = env::var("GT_S3_BUCKET") {
302                    !b.is_empty()
303                } else {
304                    false
305                }
306            }
307            StorageType::Oss => {
308                if let Ok(b) = env::var("GT_OSS_BUCKET") {
309                    !b.is_empty()
310                } else {
311                    false
312                }
313            }
314            StorageType::Azblob => {
315                if let Ok(b) = env::var("GT_AZBLOB_CONTAINER") {
316                    !b.is_empty()
317                } else {
318                    false
319                }
320            }
321            StorageType::Gcs => {
322                if let Ok(b) = env::var("GT_GCS_BUCKET") {
323                    !b.is_empty()
324                } else {
325                    false
326                }
327            }
328        }
329    }
330}
331
332/// Event recorder options for tests.
333///
334/// The production flush interval is 5s, and the event tests interleave "run a
335/// DDL, wait for its event, run the next DDL", so each barrier costs a full
336/// window. A short interval removes that wait without changing what is asserted.
337pub fn test_event_recorder_options() -> EventRecorderOptions {
338    EventRecorderOptions {
339        flush_interval: Duration::from_millis(100),
340        ..Default::default()
341    }
342}
343
344fn s3_test_config() -> S3Config {
345    S3Config {
346        connection: S3Connection {
347            root: uuid::Uuid::new_v4().to_string(),
348            access_key_id: env::var("GT_S3_ACCESS_KEY_ID").unwrap().into(),
349            secret_access_key: env::var("GT_S3_ACCESS_KEY").unwrap().into(),
350            bucket: env::var("GT_S3_BUCKET").unwrap(),
351            region: Some(env::var("GT_S3_REGION").unwrap()),
352            endpoint: env::var("GT_S3_ENDPOINT_URL").ok(),
353            ..Default::default()
354        },
355        ..Default::default()
356    }
357}
358
359pub fn get_test_store_config(store_type: &StorageType) -> (ObjectStoreConfig, TempDirGuard) {
360    let _ = dotenv::dotenv();
361
362    match store_type {
363        StorageType::Gcs => {
364            let gcs_config = GcsConfig {
365                connection: GcsConnection {
366                    root: uuid::Uuid::new_v4().to_string(),
367                    bucket: env::var("GT_GCS_BUCKET").unwrap(),
368                    scope: env::var("GT_GCS_SCOPE").unwrap(),
369                    credential_path: env::var("GT_GCS_CREDENTIAL_PATH").unwrap().into(),
370                    credential: env::var("GT_GCS_CREDENTIAL").unwrap().into(),
371                    endpoint: env::var("GT_GCS_ENDPOINT").unwrap_or_default(),
372                },
373                ..Default::default()
374            };
375
376            let builder = Gcs::from(&gcs_config.connection);
377            let config = ObjectStoreConfig::Gcs(gcs_config);
378            let store = ObjectStore::new(builder).unwrap();
379            (config, TempDirGuard::remote(TempFolder::new(&store, "/")))
380        }
381        StorageType::Azblob => {
382            let azblob_config = AzblobConfig {
383                connection: AzblobConnection {
384                    root: uuid::Uuid::new_v4().to_string(),
385                    container: env::var("GT_AZBLOB_CONTAINER").unwrap(),
386                    account_name: env::var("GT_AZBLOB_ACCOUNT_NAME").unwrap().into(),
387                    account_key: env::var("GT_AZBLOB_ACCOUNT_KEY").unwrap().into(),
388                    endpoint: env::var("GT_AZBLOB_ENDPOINT").unwrap(),
389                    ..Default::default()
390                },
391                ..Default::default()
392            };
393
394            let builder = Azblob::from(&azblob_config.connection);
395            let config = ObjectStoreConfig::Azblob(azblob_config);
396            let store = ObjectStore::new(builder).unwrap();
397            (config, TempDirGuard::remote(TempFolder::new(&store, "/")))
398        }
399        StorageType::Oss => {
400            let oss_config = OssConfig {
401                connection: OssConnection {
402                    root: uuid::Uuid::new_v4().to_string(),
403                    access_key_id: env::var("GT_OSS_ACCESS_KEY_ID").unwrap().into(),
404                    access_key_secret: env::var("GT_OSS_ACCESS_KEY").unwrap().into(),
405                    bucket: env::var("GT_OSS_BUCKET").unwrap(),
406                    endpoint: env::var("GT_OSS_ENDPOINT").unwrap(),
407                },
408                ..Default::default()
409            };
410
411            let builder = Oss::from(&oss_config.connection);
412            let config = ObjectStoreConfig::Oss(oss_config);
413            let store = ObjectStore::new(builder).unwrap();
414            (config, TempDirGuard::remote(TempFolder::new(&store, "/")))
415        }
416        StorageType::S3 | StorageType::S3WithCache => {
417            let mut s3_config = s3_test_config();
418
419            // The datanode wipes `<cache_path>/cache/object/read` on startup, so a
420            // path shared between concurrently running tests lets a starting test
421            // delete the read cache of a running one.
422            let cache_dir = if *store_type == StorageType::S3WithCache {
423                let dir = create_temp_dir("gt_s3_read_cache");
424                s3_config.cache.cache_path = dir.path().to_string_lossy().to_string();
425                Some(dir)
426            } else {
427                s3_config.cache.enable_read_cache = false;
428                None
429            };
430
431            let builder = S3::from(&s3_config.connection);
432            let config = ObjectStoreConfig::S3(s3_config);
433            let store = ObjectStore::new(builder).unwrap();
434            (
435                config,
436                TempDirGuard {
437                    remote: Some(TempFolder::new(&store, "/")),
438                    local_cache: cache_dir,
439                },
440            )
441        }
442        StorageType::File => (
443            ObjectStoreConfig::File(FileConfig {}),
444            TempDirGuard::default(),
445        ),
446    }
447}
448
449#[derive(Default)]
450pub struct TempDirGuard {
451    /// Prefix to wipe from the remote object store, absent for the file backend.
452    remote: Option<TempFolder>,
453    /// Local read cache directory. Only held so it is removed when the guard drops.
454    #[allow(dead_code)]
455    local_cache: Option<TempDir>,
456}
457
458impl TempDirGuard {
459    fn remote(folder: TempFolder) -> Self {
460        Self {
461            remote: Some(folder),
462            local_cache: None,
463        }
464    }
465}
466
467pub struct TestGuard {
468    pub home_guard: FileDirGuard,
469    pub storage_guards: Vec<StorageGuard>,
470}
471
472pub struct FileDirGuard {
473    pub temp_dir: TempDir,
474}
475
476impl FileDirGuard {
477    pub fn new(temp_dir: TempDir) -> Self {
478        Self { temp_dir }
479    }
480}
481
482pub struct StorageGuard(pub TempDirGuard);
483
484impl TestGuard {
485    pub async fn remove_all(&mut self) {
486        for storage_guard in self.storage_guards.iter_mut() {
487            if let Some(guard) = &mut storage_guard.0.remote {
488                guard.remove_all().await.unwrap()
489            }
490        }
491    }
492}
493
494impl Drop for TestGuard {
495    fn drop(&mut self) {
496        let (tx, rx) = std::sync::mpsc::channel();
497
498        let guards = std::mem::take(&mut self.storage_guards);
499        common_runtime::spawn_global(async move {
500            let mut errors = vec![];
501            for guard in guards {
502                if let Some(remote) = guard.0.remote
503                    && let Err(e) = remote.remove_all().await
504                {
505                    errors.push(e);
506                }
507            }
508            if errors.is_empty() {
509                tx.send(Ok(())).unwrap();
510            } else {
511                tx.send(Err(errors)).unwrap();
512            }
513        });
514        rx.recv().unwrap().unwrap_or_else(|e| panic!("{:?}", e));
515    }
516}
517
518pub fn create_tmp_dir_and_datanode_opts(
519    default_store_type: StorageType,
520    store_provider_types: Vec<StorageType>,
521    name: &str,
522    wal_config: DatanodeWalConfig,
523    gc_config: GcConfig,
524) -> (DatanodeOptions, TestGuard) {
525    let home_tmp_dir = create_temp_dir(&format!("gt_data_{name}"));
526    let home_dir = home_tmp_dir.path().to_str().unwrap().to_string();
527
528    // Excludes the default object store.
529    let mut store_providers = Vec::with_capacity(store_provider_types.len());
530    // Includes the default object store.
531    let mut storage_guards = Vec::with_capacity(store_provider_types.len() + 1);
532
533    let (default_store, data_tmp_dir) = get_test_store_config(&default_store_type);
534    storage_guards.push(StorageGuard(data_tmp_dir));
535
536    for store_type in store_provider_types {
537        let (store, data_tmp_dir) = get_test_store_config(&store_type);
538        store_providers.push(store);
539        storage_guards.push(StorageGuard(data_tmp_dir))
540    }
541    let opts = create_datanode_opts(
542        default_store,
543        store_providers,
544        home_dir,
545        wal_config,
546        gc_config,
547    );
548
549    (
550        opts,
551        TestGuard {
552            home_guard: FileDirGuard::new(home_tmp_dir),
553            storage_guards,
554        },
555    )
556}
557
558pub(crate) fn create_datanode_opts(
559    default_store: ObjectStoreConfig,
560    providers: Vec<ObjectStoreConfig>,
561    home_dir: String,
562    wal_config: DatanodeWalConfig,
563    gc_config: GcConfig,
564) -> DatanodeOptions {
565    let region_engine = DatanodeOptions::default()
566        .region_engine
567        .into_iter()
568        .map(|mut v| {
569            if let datanode::config::RegionEngineConfig::Mito(mito_config) = &mut v {
570                mito_config.gc = gc_config.clone();
571            }
572            v
573        })
574        .collect();
575    DatanodeOptions {
576        node_id: Some(0),
577        require_lease_before_startup: true,
578        storage: StorageConfig {
579            data_home: home_dir,
580            copy_root: None,
581            providers,
582            store: default_store,
583        },
584        grpc: GrpcOptions::default()
585            .with_bind_addr(PEER_PLACEHOLDER_ADDR)
586            .with_server_addr(PEER_PLACEHOLDER_ADDR),
587        wal: wal_config,
588        region_engine,
589        ..Default::default()
590    }
591}
592
593pub(crate) async fn create_test_table(instance: &Instance, table_name: &str) {
594    let sql = format!(
595        r#"
596CREATE TABLE IF NOT EXISTS {table_name} (
597    host String NOT NULL PRIMARY KEY,
598    cpu DOUBLE NULL,
599    memory DOUBLE NULL,
600    ts TIMESTAMP NOT NULL TIME INDEX,
601)
602"#
603    );
604
605    let result = instance.do_query(&sql, QueryContext::arc()).await;
606    let _ = result.first().unwrap().as_ref().unwrap();
607}
608
609async fn setup_standalone_instance(
610    test_name: &str,
611    store_type: StorageType,
612) -> GreptimeDbStandalone {
613    GreptimeDbStandaloneBuilder::new(test_name)
614        .with_default_store_type(store_type)
615        .build()
616        .await
617}
618
619async fn setup_standalone_instance_with_slow_query_threshold(
620    test_name: &str,
621    store_type: StorageType,
622    slow_query_threshold: std::time::Duration,
623) -> GreptimeDbStandalone {
624    GreptimeDbStandaloneBuilder::new(test_name)
625        .with_default_store_type(store_type)
626        .with_slow_query_threshold(slow_query_threshold)
627        .build()
628        .await
629}
630
631async fn setup_standalone_instance_with_plugins(
632    test_name: &str,
633    store_type: StorageType,
634    plugins: Plugins,
635) -> GreptimeDbStandalone {
636    GreptimeDbStandaloneBuilder::new(test_name)
637        .with_default_store_type(store_type)
638        .with_plugin(plugins)
639        .build()
640        .await
641}
642
643async fn setup_standalone_instance_with_plugins_and_slow_query_threshold(
644    test_name: &str,
645    store_type: StorageType,
646    plugins: Plugins,
647    slow_query_threshold: std::time::Duration,
648) -> GreptimeDbStandalone {
649    GreptimeDbStandaloneBuilder::new(test_name)
650        .with_default_store_type(store_type)
651        .with_plugin(plugins)
652        .with_slow_query_threshold(slow_query_threshold)
653        .build()
654        .await
655}
656
657pub async fn setup_test_http_app(store_type: StorageType, name: &str) -> (Router, TestGuard) {
658    let instance = setup_standalone_instance(name, store_type).await;
659
660    let http_opts = HttpOptions {
661        addr: format!("127.0.0.1:{}", ports::get_port()),
662        ..Default::default()
663    };
664    let http_server = HttpServerBuilder::new(http_opts)
665        .with_sql_handler(instance.fe_instance().clone())
666        .with_logs_handler(instance.fe_instance().clone())
667        .with_metrics_handler(MetricsHandler)
668        .with_greptime_config_options(instance.opts.datanode_options().to_toml().unwrap())
669        .build();
670    (
671        http_server.build(http_server.make_app()).unwrap(),
672        instance.guard,
673    )
674}
675
676pub async fn setup_test_http_app_with_frontend(
677    store_type: StorageType,
678    name: &str,
679) -> (Router, TestGuard) {
680    setup_test_http_app_with_frontend_and_user_provider(store_type, name, None).await
681}
682
683pub async fn setup_test_http_app_with_frontend_and_slow_query_threshold(
684    store_type: StorageType,
685    name: &str,
686    slow_query_threshold: std::time::Duration,
687) -> (Router, TestGuard) {
688    let instance =
689        setup_standalone_instance_with_slow_query_threshold(name, store_type, slow_query_threshold)
690            .await;
691
692    create_test_table(instance.fe_instance(), "demo").await;
693
694    let http_opts = HttpOptions {
695        addr: format!("127.0.0.1:{}", ports::get_port()),
696        ..Default::default()
697    };
698
699    let http_server = HttpServerBuilder::new(http_opts)
700        .with_sql_handler(instance.fe_instance().clone())
701        .with_log_ingest_handler(instance.fe_instance().clone(), None, None)
702        .with_logs_handler(instance.fe_instance().clone())
703        .with_influxdb_handler(instance.fe_instance().clone())
704        .with_otlp_handler(instance.fe_instance().clone(), true)
705        .with_jaeger_handler(instance.fe_instance().clone())
706        .with_greptime_config_options(instance.opts.to_toml().unwrap())
707        .build();
708
709    let app = http_server.build(http_server.make_app()).unwrap();
710    (app, instance.guard)
711}
712
713pub async fn setup_test_http_app_with_frontend_and_user_provider(
714    store_type: StorageType,
715    name: &str,
716    user_provider: Option<UserProviderRef>,
717) -> (Router, TestGuard) {
718    setup_test_http_app_with_frontend_and_custom_options(
719        store_type,
720        name,
721        user_provider,
722        None,
723        None,
724    )
725    .await
726}
727
728pub async fn setup_test_http_app_with_frontend_and_custom_options(
729    store_type: StorageType,
730    name: &str,
731    user_provider: Option<UserProviderRef>,
732    http_opts: Option<HttpOptions>,
733    memory_limiter: Option<ServerMemoryLimiter>,
734) -> (Router, TestGuard) {
735    let plugins = Plugins::new();
736    if let Some(user_provider) = user_provider.clone() {
737        plugins.insert::<UserProviderRef>(user_provider.clone());
738        plugins.insert::<PermissionCheckerRef>(DefaultPermissionChecker::arc());
739    }
740
741    let instance = setup_standalone_instance_with_plugins(name, store_type, plugins).await;
742
743    create_test_table(instance.fe_instance(), "demo").await;
744
745    let http_opts = http_opts.unwrap_or_else(|| HttpOptions {
746        addr: format!("127.0.0.1:{}", ports::get_port()),
747        ..Default::default()
748    });
749
750    // The memory limiter must be wired before the handlers: routes capture
751    // the builder's limiter at wiring time (mirrors `frontend::server`).
752    let mut http_server = HttpServerBuilder::new(http_opts);
753    if let Some(limiter) = memory_limiter {
754        http_server = http_server.with_memory_limiter(limiter);
755    }
756    let mut http_server = http_server
757        .with_sql_handler(instance.fe_instance().clone())
758        .with_log_ingest_handler(instance.fe_instance().clone(), None, None)
759        .with_logs_handler(instance.fe_instance().clone())
760        .with_influxdb_handler(instance.fe_instance().clone())
761        .with_otlp_handler(instance.fe_instance().clone(), true)
762        .with_prometheus_handler(instance.fe_instance().clone())
763        .with_jaeger_handler(instance.fe_instance().clone())
764        .with_dashboard_handler(instance.fe_instance().clone())
765        .with_greptime_config_options(instance.opts.to_toml().unwrap());
766
767    if let Some(user_provider) = user_provider {
768        http_server = http_server.with_user_provider(user_provider);
769    }
770
771    let http_server = http_server.build();
772
773    let app = http_server.build(http_server.make_app()).unwrap();
774    (app, instance.guard)
775}
776
777async fn run_sql(sql: &str, instance: &GreptimeDbStandalone) {
778    let result = instance
779        .fe_instance()
780        .do_query(sql, QueryContext::arc())
781        .await;
782    let _ = result.first().unwrap().as_ref().unwrap();
783}
784
785pub async fn setup_test_prom_app_with_frontend(
786    store_type: StorageType,
787    name: &str,
788) -> (Router, TestGuard) {
789    setup_test_prom_app_with_frontend_inner(store_type, name, false, None).await
790}
791
792/// Like [`setup_test_prom_app_with_frontend`] but enables the pending-rows batcher,
793/// so Prometheus remote write goes through the batched (metric-engine) path instead
794/// of the direct `PromStoreProtocolHandler::write` path.
795pub async fn setup_test_prom_app_with_frontend_batched(
796    store_type: StorageType,
797    name: &str,
798) -> (Router, TestGuard) {
799    setup_test_prom_app_with_frontend_inner(store_type, name, true, None).await
800}
801
802/// Like [`setup_test_prom_app_with_frontend`] but wires a shared request-memory
803/// limiter into the server, mirroring production deployments that configure
804/// `max_in_flight_write_bytes`.
805pub async fn setup_test_prom_app_with_frontend_and_memory_limiter(
806    store_type: StorageType,
807    name: &str,
808    memory_limiter: Option<ServerMemoryLimiter>,
809) -> (Router, TestGuard) {
810    setup_test_prom_app_with_frontend_inner(store_type, name, false, memory_limiter).await
811}
812
813async fn setup_test_prom_app_with_frontend_inner(
814    store_type: StorageType,
815    name: &str,
816    enable_batcher: bool,
817    memory_limiter: Option<ServerMemoryLimiter>,
818) -> (Router, TestGuard) {
819    unsafe {
820        std::env::set_var("TZ", "UTC");
821    }
822
823    let instance = setup_standalone_instance(name, store_type).await;
824
825    // build physical table
826    let sql = "CREATE TABLE phy (ts timestamp time index, val double, host string primary key) engine=metric with ('physical_metric_table' = '')";
827    run_sql(sql, &instance).await;
828    let sql = "CREATE TABLE phy_ns (ts timestamp(0) time index, val double, host string primary key) engine=metric with ('physical_metric_table' = '')";
829    run_sql(sql, &instance).await;
830    // build metric tables
831    let sql = "CREATE TABLE demo (ts timestamp time index, val double, host string primary key) engine=metric with ('on_physical_table' = 'phy')";
832    run_sql(sql, &instance).await;
833    let sql = "CREATE TABLE demo_metrics (ts timestamp time index, val double, idc string primary key) engine=metric with ('on_physical_table' = 'phy')";
834    run_sql(sql, &instance).await;
835    let sql = "CREATE TABLE multi_labels (ts timestamp(0) time index, val double, idc string, env string, host string, primary key (idc, env, host)) engine=metric with ('on_physical_table' = 'phy_ns')";
836    run_sql(sql, &instance).await;
837
838    // insert rows
839    let sql = "INSERT INTO demo(host, val, ts) VALUES ('host1', 1.1, 0), ('host2', 2.1, 600000)";
840    run_sql(sql, &instance).await;
841    let sql =
842        "INSERT INTO demo_metrics(idc, val, ts) VALUES ('idc1', 1.1, 0), ('idc2', 2.1, 600000)";
843    run_sql(sql, &instance).await;
844    // insert a row with empty label
845    let sql = "INSERT INTO demo_metrics(val, ts) VALUES (1.1, 0)";
846    run_sql(sql, &instance).await;
847
848    // insert rows to multi_labels
849    let sql = "INSERT INTO multi_labels(idc, env, host, val, ts) VALUES ('idc1', 'dev', 'host1', 1.1, 0), ('idc1', 'dev', 'host2', 2.1, 0), ('idc2', 'dev', 'host1', 1.1, 0), ('idc2', 'test', 'host3', 2.1, 0)";
850    run_sql(sql, &instance).await;
851
852    // build physical table
853    let sql = "CREATE TABLE phy2 (ts timestamp(9) time index, val double, host string primary key) engine=metric with ('physical_metric_table' = '')";
854    run_sql(sql, &instance).await;
855    let sql = "CREATE TABLE demo_metrics_with_nanos(ts timestamp(9) time index, val double, idc string primary key) engine=metric with ('on_physical_table' = 'phy2')";
856    run_sql(sql, &instance).await;
857    let sql = "INSERT INTO demo_metrics_with_nanos(idc, val, ts) VALUES ('idc1', 1.1, 0)";
858    run_sql(sql, &instance).await;
859
860    // a mito table with non-prometheus compatible values
861    let sql = "CREATE TABLE mito (ts timestamp(9) time index, val double, host bigint primary key) engine=mito";
862    run_sql(sql, &instance).await;
863    let sql = "INSERT INTO mito(host, val, ts) VALUES (1, 1.1, 0)";
864    run_sql(sql, &instance).await;
865
866    let http_server = build_test_prom_server(
867        instance.fe_instance().clone(),
868        enable_batcher,
869        memory_limiter,
870    )
871    .with_greptime_config_options(instance.opts.datanode_options().to_toml().unwrap())
872    .build();
873    let app = http_server.build(http_server.make_app()).unwrap();
874    (app, instance.guard)
875}
876
877/// Builds Prometheus HTTP routes for either a standalone or distributed frontend.
878/// `memory_limiter` must be set before the handlers are wired (handlers capture
879/// the limiter at wiring time, mirroring production in `frontend::server`).
880pub fn build_test_prom_server(
881    frontend_ref: Arc<Instance>,
882    enable_batcher: bool,
883    memory_limiter: Option<ServerMemoryLimiter>,
884) -> HttpServerBuilder {
885    let http_opts = HttpOptions {
886        addr: format!("127.0.0.1:{}", ports::get_port()),
887        ..Default::default()
888    };
889    // Mirror the production wiring at `frontend::server`: build the batcher from the
890    // instance's managers. A short flush interval keeps the test responsive.
891    let pending_rows_batcher = if enable_batcher {
892        LogicalTablePendingRowsBatcher::try_new(
893            frontend_ref.partition_manager().clone(),
894            frontend_ref.node_manager().clone(),
895            frontend_ref.inserter().write_dispatcher().clone(),
896            frontend_ref.catalog_manager().clone(),
897            frontend_ref.table_flownode_set_cache().clone(),
898            true,
899            frontend_ref.clone(),
900            Duration::from_millis(50),
901            1000,
902            4,
903            64,
904            64,
905            std::num::NonZeroUsize::new(1024).unwrap(),
906        )
907    } else {
908        None
909    };
910    assert_eq!(pending_rows_batcher.is_some(), enable_batcher);
911    let mut builder = HttpServerBuilder::new(http_opts)
912        .with_sql_handler(frontend_ref.clone())
913        .with_logs_handler(frontend_ref.clone());
914    if let Some(limiter) = memory_limiter {
915        builder = builder.with_memory_limiter(limiter);
916    }
917    builder
918        .with_prom_handler(
919            frontend_ref.clone(),
920            Some(frontend_ref.clone()),
921            true,
922            PromValidationMode::Strict,
923            pending_rows_batcher,
924        )
925        .with_prometheus_handler(frontend_ref)
926}
927
928pub async fn setup_grpc_server(
929    store_type: StorageType,
930    name: &str,
931) -> (GreptimeDbStandalone, Arc<GrpcServer>) {
932    setup_grpc_server_with(store_type, name, None, None, None).await
933}
934
935pub async fn setup_grpc_server_with_user_provider(
936    store_type: StorageType,
937    name: &str,
938    user_provider: Option<UserProviderRef>,
939) -> (GreptimeDbStandalone, Arc<GrpcServer>) {
940    setup_grpc_server_with(store_type, name, user_provider, None, None).await
941}
942
943/// Sets up a gRPC server backed by a standalone instance whose frontend has auto
944/// table creation disabled, for testing the server-side global switch.
945pub async fn setup_grpc_server_with_auto_create_table_disabled(
946    store_type: StorageType,
947    name: &str,
948) -> (GreptimeDbStandalone, Arc<GrpcServer>) {
949    let instance = GreptimeDbStandaloneBuilder::new(name)
950        .with_default_store_type(store_type)
951        .with_auto_create_table(false)
952        .build()
953        .await;
954    setup_grpc_server_for_instance(instance, None, None, None).await
955}
956
957pub async fn setup_grpc_server_with(
958    store_type: StorageType,
959    name: &str,
960    user_provider: Option<UserProviderRef>,
961    grpc_config: Option<GrpcServerConfig>,
962    memory_limiter: Option<servers::request_memory_limiter::ServerMemoryLimiter>,
963) -> (GreptimeDbStandalone, Arc<GrpcServer>) {
964    let instance = setup_standalone_instance(name, store_type).await;
965    setup_grpc_server_for_instance(instance, user_provider, grpc_config, memory_limiter).await
966}
967
968/// Builds and starts a gRPC server on top of an already-constructed standalone
969/// instance. This is the shared core behind the `setup_grpc_server_*` helpers.
970async fn setup_grpc_server_for_instance(
971    instance: GreptimeDbStandalone,
972    user_provider: Option<UserProviderRef>,
973    grpc_config: Option<GrpcServerConfig>,
974    memory_limiter: Option<servers::request_memory_limiter::ServerMemoryLimiter>,
975) -> (GreptimeDbStandalone, Arc<GrpcServer>) {
976    let grpc_server = setup_grpc_server_for_frontend_instance_with(
977        instance.fe_instance().clone(),
978        user_provider,
979        grpc_config,
980        memory_limiter,
981    )
982    .await;
983    (instance, grpc_server)
984}
985
986/// Builds and starts an authenticated gRPC server on an existing frontend instance.
987pub async fn setup_grpc_server_for_frontend_instance(
988    instance: Arc<Instance>,
989    user_provider: Option<UserProviderRef>,
990) -> Arc<GrpcServer> {
991    setup_grpc_server_for_frontend_instance_with(instance, user_provider, None, None).await
992}
993
994/// Starts a gRPC server backed by `instance` and returns a client authenticated
995/// as the supplied test user. The server handle keeps the listener alive.
996pub async fn setup_authenticated_grpc_database(
997    instance: Arc<Instance>,
998    username: &str,
999    password: &str,
1000) -> (Database, Arc<GrpcServer>) {
1001    let user_provider =
1002        user_provider_from_option(&format!("static_user_provider:cmd:{username}={password}"))
1003            .unwrap();
1004    let grpc_server = setup_grpc_server_for_frontend_instance(instance, Some(user_provider)).await;
1005    let grpc_addr = grpc_server.bind_addr().unwrap().to_string();
1006    let mut database = Database::new_with_dbname(
1007        "greptime-public",
1008        Client::with_query_and_control_managers(
1009            Default::default(),
1010            Default::default(),
1011            vec![grpc_addr],
1012        ),
1013    );
1014    database.set_auth(api::v1::auth_header::AuthScheme::Basic(Basic {
1015        username: username.to_string(),
1016        password: password.to_string(),
1017    }));
1018    (database, grpc_server)
1019}
1020
1021async fn setup_grpc_server_for_frontend_instance_with(
1022    fe_instance_ref: Arc<Instance>,
1023    user_provider: Option<UserProviderRef>,
1024    grpc_config: Option<GrpcServerConfig>,
1025    memory_limiter: Option<servers::request_memory_limiter::ServerMemoryLimiter>,
1026) -> Arc<GrpcServer> {
1027    let runtime: Runtime = RuntimeBuilder::default()
1028        .worker_threads(2)
1029        .thread_name("grpc-handlers")
1030        .build()
1031        .unwrap();
1032
1033    let greptime_request_handler = GreptimeRequestHandler::new(
1034        fe_instance_ref.clone(),
1035        user_provider.clone(),
1036        Some(runtime.clone()),
1037        FlightCompression::default(),
1038    );
1039
1040    let flight_handler = Arc::new(greptime_request_handler.clone());
1041
1042    let grpc_config = grpc_config.unwrap_or_default();
1043    let mut grpc_builder = GrpcServerBuilder::new(grpc_config.clone(), runtime);
1044
1045    if let Some(limiter) = memory_limiter {
1046        grpc_builder = grpc_builder.with_memory_limiter(limiter);
1047    }
1048
1049    let grpc_builder = grpc_builder
1050        .database_handler(greptime_request_handler)
1051        .flight_handler(flight_handler)
1052        .prometheus_handler(fe_instance_ref.clone(), user_provider.clone())
1053        .otel_arrow_handler(OtelArrowServiceHandler::new(fe_instance_ref, user_provider))
1054        .with_tls_config(grpc_config.tls)
1055        .unwrap();
1056
1057    let mut grpc_server = grpc_builder.build();
1058
1059    let fe_grpc_addr = "127.0.0.1:0".parse::<SocketAddr>().unwrap();
1060    grpc_server.start(fe_grpc_addr).await.unwrap();
1061
1062    Arc::new(grpc_server)
1063}
1064
1065pub async fn setup_mysql_server(
1066    store_type: StorageType,
1067    name: &str,
1068) -> (TestGuard, Arc<Box<dyn Server>>) {
1069    setup_mysql_server_with_user_provider(store_type, name, None).await
1070}
1071
1072pub async fn setup_mysql_server_with_slow_query_threshold(
1073    store_type: StorageType,
1074    name: &str,
1075    slow_query_threshold: std::time::Duration,
1076) -> (TestGuard, Arc<Box<dyn Server>>) {
1077    let plugins = Plugins::new();
1078    let instance = setup_standalone_instance_with_plugins_and_slow_query_threshold(
1079        name,
1080        store_type,
1081        plugins,
1082        slow_query_threshold,
1083    )
1084    .await;
1085
1086    let runtime = RuntimeBuilder::default()
1087        .worker_threads(2)
1088        .thread_name("mysql-runtime")
1089        .build()
1090        .unwrap();
1091
1092    let fe_mysql_addr = format!("127.0.0.1:{}", ports::get_port());
1093
1094    let fe_instance_ref = instance.fe_instance().clone();
1095    let opts = MysqlOptions {
1096        addr: fe_mysql_addr.clone(),
1097        ..Default::default()
1098    };
1099    let mut mysql_server = MysqlServer::create_server(
1100        runtime,
1101        Arc::new(MysqlSpawnRef::new(fe_instance_ref, None)),
1102        Arc::new(MysqlSpawnConfig::new(
1103            false,
1104            Arc::new(
1105                ReloadableTlsServerConfig::try_new(opts.tls.clone())
1106                    .expect("Failed to load certificates and keys"),
1107            ),
1108            0,
1109            opts.reject_no_database.unwrap_or(false),
1110            opts.prepared_stmt_cache_size,
1111        )),
1112        None,
1113    );
1114
1115    mysql_server
1116        .start(fe_mysql_addr.parse::<SocketAddr>().unwrap())
1117        .await
1118        .unwrap();
1119
1120    (instance.guard, Arc::new(mysql_server))
1121}
1122
1123pub async fn setup_mysql_server_with_user_provider(
1124    store_type: StorageType,
1125    name: &str,
1126    user_provider: Option<UserProviderRef>,
1127) -> (TestGuard, Arc<Box<dyn Server>>) {
1128    let plugins = Plugins::new();
1129    if let Some(user_provider) = user_provider.clone() {
1130        plugins.insert::<UserProviderRef>(user_provider.clone());
1131        plugins.insert::<PermissionCheckerRef>(DefaultPermissionChecker::arc());
1132    }
1133
1134    let instance = setup_standalone_instance_with_plugins(name, store_type, plugins).await;
1135
1136    let runtime = RuntimeBuilder::default()
1137        .worker_threads(2)
1138        .thread_name("mysql-runtime")
1139        .build()
1140        .unwrap();
1141
1142    let fe_mysql_addr = format!("127.0.0.1:{}", ports::get_port());
1143
1144    let fe_instance_ref = instance.fe_instance().clone();
1145    let opts = MysqlOptions {
1146        addr: fe_mysql_addr.clone(),
1147        ..Default::default()
1148    };
1149    let mut mysql_server = MysqlServer::create_server(
1150        runtime,
1151        Arc::new(MysqlSpawnRef::new(fe_instance_ref, user_provider)),
1152        Arc::new(MysqlSpawnConfig::new(
1153            false,
1154            Arc::new(
1155                ReloadableTlsServerConfig::try_new(opts.tls.clone())
1156                    .expect("Failed to load certificates and keys"),
1157            ),
1158            0,
1159            opts.reject_no_database.unwrap_or(false),
1160            opts.prepared_stmt_cache_size,
1161        )),
1162        None,
1163    );
1164
1165    mysql_server
1166        .start(fe_mysql_addr.parse::<SocketAddr>().unwrap())
1167        .await
1168        .unwrap();
1169
1170    (instance.guard, Arc::new(mysql_server))
1171}
1172
1173pub async fn setup_pg_server(
1174    store_type: StorageType,
1175    name: &str,
1176) -> (TestGuard, Arc<Box<dyn Server>>) {
1177    setup_pg_server_with_user_provider(store_type, name, None).await
1178}
1179
1180pub async fn setup_pg_server_with_slow_query_threshold(
1181    store_type: StorageType,
1182    name: &str,
1183    slow_query_threshold: std::time::Duration,
1184) -> (TestGuard, Arc<Box<dyn Server>>) {
1185    let instance =
1186        setup_standalone_instance_with_slow_query_threshold(name, store_type, slow_query_threshold)
1187            .await;
1188
1189    let runtime = RuntimeBuilder::default()
1190        .worker_threads(2)
1191        .thread_name("pg-runtime")
1192        .build()
1193        .unwrap();
1194
1195    let fe_pg_addr = format!("127.0.0.1:{}", ports::get_port());
1196
1197    let fe_instance_ref = instance.fe_instance().clone();
1198    let opts = PostgresOptions {
1199        addr: fe_pg_addr.clone(),
1200        ..Default::default()
1201    };
1202    let tls_server_config = Arc::new(
1203        ReloadableTlsServerConfig::try_new(opts.tls.clone())
1204            .expect("Failed to load certificates and keys"),
1205    );
1206
1207    let mut pg_server = Box::new(PostgresServer::new(
1208        fe_instance_ref,
1209        opts.tls.should_force_tls(),
1210        tls_server_config,
1211        0,
1212        runtime,
1213        None,
1214        None,
1215    ));
1216
1217    pg_server
1218        .start(fe_pg_addr.parse::<SocketAddr>().unwrap())
1219        .await
1220        .unwrap();
1221
1222    (instance.guard, Arc::new(pg_server))
1223}
1224
1225pub async fn setup_pg_server_with_user_provider(
1226    store_type: StorageType,
1227    name: &str,
1228    user_provider: Option<UserProviderRef>,
1229) -> (TestGuard, Arc<Box<dyn Server>>) {
1230    let instance = setup_standalone_instance(name, store_type).await;
1231
1232    let runtime = RuntimeBuilder::default()
1233        .worker_threads(2)
1234        .thread_name("pg-runtime")
1235        .build()
1236        .unwrap();
1237
1238    let fe_pg_addr = format!("127.0.0.1:{}", ports::get_port());
1239
1240    let fe_instance_ref = instance.fe_instance().clone();
1241    let opts = PostgresOptions {
1242        addr: fe_pg_addr.clone(),
1243        ..Default::default()
1244    };
1245    let tls_server_config = Arc::new(
1246        ReloadableTlsServerConfig::try_new(opts.tls.clone())
1247            .expect("Failed to load certificates and keys"),
1248    );
1249
1250    let mut pg_server = Box::new(PostgresServer::new(
1251        fe_instance_ref,
1252        opts.tls.should_force_tls(),
1253        tls_server_config,
1254        0,
1255        runtime,
1256        user_provider,
1257        None,
1258    ));
1259
1260    pg_server
1261        .start(fe_pg_addr.parse::<SocketAddr>().unwrap())
1262        .await
1263        .unwrap();
1264
1265    (instance.guard, Arc::new(pg_server))
1266}
1267
1268/// Sets up a standalone instance with both a Prometheus remote-write HTTP app
1269/// (native histograms enabled) and a Postgres server attached, so native
1270/// histogram data written via remote-write can be queried over the Postgres
1271/// protocol.
1272pub async fn setup_pg_server_with_prom_native_histogram(
1273    store_type: StorageType,
1274    name: &str,
1275) -> (TestGuard, Router, Arc<Box<dyn Server>>) {
1276    unsafe {
1277        std::env::set_var("TZ", "UTC");
1278    }
1279
1280    let instance = setup_standalone_instance(name, store_type).await;
1281
1282    // Prometheus remote-write HTTP app with native histograms enabled.
1283    let http_server = build_test_prom_server(instance.fe_instance().clone(), false, None)
1284        .with_greptime_config_options(instance.opts.datanode_options().to_toml().unwrap())
1285        .build();
1286    let app = http_server.build(http_server.make_app()).unwrap();
1287
1288    // Postgres server on the same instance.
1289    let runtime = RuntimeBuilder::default()
1290        .worker_threads(2)
1291        .thread_name("pg-runtime")
1292        .build()
1293        .unwrap();
1294
1295    let fe_pg_addr = format!("127.0.0.1:{}", ports::get_port());
1296    let opts = PostgresOptions {
1297        addr: fe_pg_addr.clone(),
1298        ..Default::default()
1299    };
1300    let tls_server_config = Arc::new(
1301        ReloadableTlsServerConfig::try_new(opts.tls.clone())
1302            .expect("Failed to load certificates and keys"),
1303    );
1304
1305    let mut pg_server = Box::new(PostgresServer::new(
1306        instance.fe_instance().clone(),
1307        opts.tls.should_force_tls(),
1308        tls_server_config,
1309        0,
1310        runtime,
1311        None,
1312        None,
1313    ));
1314
1315    pg_server
1316        .start(fe_pg_addr.parse::<SocketAddr>().unwrap())
1317        .await
1318        .unwrap();
1319
1320    (instance.guard, app, Arc::new(pg_server))
1321}
1322
1323pub(crate) async fn prepare_another_catalog_and_schema(instance: &Instance) {
1324    let catalog_manager = instance
1325        .catalog_manager()
1326        .as_any()
1327        .downcast_ref::<KvBackendCatalogManager>()
1328        .unwrap();
1329
1330    let table_metadata_manager = catalog_manager.table_metadata_manager_ref();
1331    prepare_another_catalog_and_schema_with_manager(table_metadata_manager).await;
1332}
1333
1334pub(crate) async fn prepare_another_catalog_and_schema_with_kv_backend(kv_backend: KvBackendRef) {
1335    let table_metadata_manager = TableMetadataManager::new(kv_backend);
1336    prepare_another_catalog_and_schema_with_manager(&table_metadata_manager).await;
1337}
1338
1339async fn prepare_another_catalog_and_schema_with_manager(
1340    table_metadata_manager: &TableMetadataManager,
1341) {
1342    table_metadata_manager
1343        .catalog_manager()
1344        .create(CatalogNameKey::new("another_catalog"), true)
1345        .await
1346        .unwrap();
1347    table_metadata_manager
1348        .schema_manager()
1349        .create(
1350            SchemaNameKey::new("another_catalog", "another_schema"),
1351            None,
1352            true,
1353        )
1354        .await
1355        .unwrap();
1356}
1357
1358pub async fn execute_sql(instance: &Arc<Instance>, sql: &str) -> Output {
1359    SqlQueryHandler::do_query(instance.as_ref(), sql, QueryContext::arc())
1360        .await
1361        .remove(0)
1362        .unwrap()
1363}
1364
1365pub async fn try_execute_sql(
1366    instance: &Arc<Instance>,
1367    sql: &str,
1368) -> servers::error::Result<Output> {
1369    SqlQueryHandler::do_query(instance.as_ref(), sql, QueryContext::arc())
1370        .await
1371        .remove(0)
1372}
1373
1374pub async fn execute_sql_and_expect(instance: &Arc<Instance>, sql: &str, expected: &str) {
1375    let output = execute_sql(instance, sql).await;
1376    let output = output.data.pretty_print().await;
1377    assert_eq!(output, expected.trim());
1378}