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            probe_on_startup: false,
582            providers,
583            store: default_store,
584        },
585        grpc: GrpcOptions::default()
586            .with_bind_addr(PEER_PLACEHOLDER_ADDR)
587            .with_server_addr(PEER_PLACEHOLDER_ADDR),
588        wal: wal_config,
589        region_engine,
590        ..Default::default()
591    }
592}
593
594pub(crate) async fn create_test_table(instance: &Instance, table_name: &str) {
595    let sql = format!(
596        r#"
597CREATE TABLE IF NOT EXISTS {table_name} (
598    host String NOT NULL PRIMARY KEY,
599    cpu DOUBLE NULL,
600    memory DOUBLE NULL,
601    ts TIMESTAMP NOT NULL TIME INDEX,
602)
603"#
604    );
605
606    let result = instance.do_query(&sql, QueryContext::arc()).await;
607    let _ = result.first().unwrap().as_ref().unwrap();
608}
609
610async fn setup_standalone_instance(
611    test_name: &str,
612    store_type: StorageType,
613) -> GreptimeDbStandalone {
614    GreptimeDbStandaloneBuilder::new(test_name)
615        .with_default_store_type(store_type)
616        .build()
617        .await
618}
619
620async fn setup_standalone_instance_with_slow_query_threshold(
621    test_name: &str,
622    store_type: StorageType,
623    slow_query_threshold: std::time::Duration,
624) -> GreptimeDbStandalone {
625    GreptimeDbStandaloneBuilder::new(test_name)
626        .with_default_store_type(store_type)
627        .with_slow_query_threshold(slow_query_threshold)
628        .build()
629        .await
630}
631
632async fn setup_standalone_instance_with_plugins(
633    test_name: &str,
634    store_type: StorageType,
635    plugins: Plugins,
636) -> GreptimeDbStandalone {
637    GreptimeDbStandaloneBuilder::new(test_name)
638        .with_default_store_type(store_type)
639        .with_plugin(plugins)
640        .build()
641        .await
642}
643
644async fn setup_standalone_instance_with_plugins_and_slow_query_threshold(
645    test_name: &str,
646    store_type: StorageType,
647    plugins: Plugins,
648    slow_query_threshold: std::time::Duration,
649) -> GreptimeDbStandalone {
650    GreptimeDbStandaloneBuilder::new(test_name)
651        .with_default_store_type(store_type)
652        .with_plugin(plugins)
653        .with_slow_query_threshold(slow_query_threshold)
654        .build()
655        .await
656}
657
658pub async fn setup_test_http_app(store_type: StorageType, name: &str) -> (Router, TestGuard) {
659    let instance = setup_standalone_instance(name, store_type).await;
660
661    let http_opts = HttpOptions {
662        addr: format!("127.0.0.1:{}", ports::get_port()),
663        ..Default::default()
664    };
665    let http_server = HttpServerBuilder::new(http_opts)
666        .with_sql_handler(instance.fe_instance().clone())
667        .with_logs_handler(instance.fe_instance().clone())
668        .with_metrics_handler(MetricsHandler)
669        .with_greptime_config_options(instance.opts.datanode_options().to_toml().unwrap())
670        .build();
671    (
672        http_server.build(http_server.make_app()).unwrap(),
673        instance.guard,
674    )
675}
676
677pub async fn setup_test_http_app_with_frontend(
678    store_type: StorageType,
679    name: &str,
680) -> (Router, TestGuard) {
681    setup_test_http_app_with_frontend_and_user_provider(store_type, name, None).await
682}
683
684pub async fn setup_test_http_app_with_frontend_and_slow_query_threshold(
685    store_type: StorageType,
686    name: &str,
687    slow_query_threshold: std::time::Duration,
688) -> (Router, TestGuard) {
689    let instance =
690        setup_standalone_instance_with_slow_query_threshold(name, store_type, slow_query_threshold)
691            .await;
692
693    create_test_table(instance.fe_instance(), "demo").await;
694
695    let http_opts = HttpOptions {
696        addr: format!("127.0.0.1:{}", ports::get_port()),
697        ..Default::default()
698    };
699
700    let http_server = HttpServerBuilder::new(http_opts)
701        .with_sql_handler(instance.fe_instance().clone())
702        .with_log_ingest_handler(instance.fe_instance().clone(), None, None)
703        .with_logs_handler(instance.fe_instance().clone())
704        .with_influxdb_handler(instance.fe_instance().clone())
705        .with_otlp_handler(instance.fe_instance().clone(), true)
706        .with_jaeger_handler(instance.fe_instance().clone())
707        .with_greptime_config_options(instance.opts.to_toml().unwrap())
708        .build();
709
710    let app = http_server.build(http_server.make_app()).unwrap();
711    (app, instance.guard)
712}
713
714pub async fn setup_test_http_app_with_frontend_and_user_provider(
715    store_type: StorageType,
716    name: &str,
717    user_provider: Option<UserProviderRef>,
718) -> (Router, TestGuard) {
719    setup_test_http_app_with_frontend_and_custom_options(
720        store_type,
721        name,
722        user_provider,
723        None,
724        None,
725    )
726    .await
727}
728
729pub async fn setup_test_http_app_with_frontend_and_custom_options(
730    store_type: StorageType,
731    name: &str,
732    user_provider: Option<UserProviderRef>,
733    http_opts: Option<HttpOptions>,
734    memory_limiter: Option<ServerMemoryLimiter>,
735) -> (Router, TestGuard) {
736    let plugins = Plugins::new();
737    if let Some(user_provider) = user_provider.clone() {
738        plugins.insert::<UserProviderRef>(user_provider.clone());
739        plugins.insert::<PermissionCheckerRef>(DefaultPermissionChecker::arc());
740    }
741
742    let instance = setup_standalone_instance_with_plugins(name, store_type, plugins).await;
743
744    create_test_table(instance.fe_instance(), "demo").await;
745
746    let http_opts = http_opts.unwrap_or_else(|| HttpOptions {
747        addr: format!("127.0.0.1:{}", ports::get_port()),
748        ..Default::default()
749    });
750
751    // The memory limiter must be wired before the handlers: routes capture
752    // the builder's limiter at wiring time (mirrors `frontend::server`).
753    let mut http_server = HttpServerBuilder::new(http_opts);
754    if let Some(limiter) = memory_limiter {
755        http_server = http_server.with_memory_limiter(limiter);
756    }
757    let mut http_server = http_server
758        .with_sql_handler(instance.fe_instance().clone())
759        .with_log_ingest_handler(instance.fe_instance().clone(), None, None)
760        .with_logs_handler(instance.fe_instance().clone())
761        .with_influxdb_handler(instance.fe_instance().clone())
762        .with_otlp_handler(instance.fe_instance().clone(), true)
763        .with_prometheus_handler(instance.fe_instance().clone())
764        .with_jaeger_handler(instance.fe_instance().clone())
765        .with_dashboard_handler(instance.fe_instance().clone())
766        .with_greptime_config_options(instance.opts.to_toml().unwrap());
767
768    if let Some(user_provider) = user_provider {
769        http_server = http_server.with_user_provider(user_provider);
770    }
771
772    let http_server = http_server.build();
773
774    let app = http_server.build(http_server.make_app()).unwrap();
775    (app, instance.guard)
776}
777
778async fn run_sql(sql: &str, instance: &GreptimeDbStandalone) {
779    let result = instance
780        .fe_instance()
781        .do_query(sql, QueryContext::arc())
782        .await;
783    let _ = result.first().unwrap().as_ref().unwrap();
784}
785
786pub async fn setup_test_prom_app_with_frontend(
787    store_type: StorageType,
788    name: &str,
789) -> (Router, TestGuard) {
790    setup_test_prom_app_with_frontend_inner(store_type, name, false, None).await
791}
792
793/// Like [`setup_test_prom_app_with_frontend`] but enables the pending-rows batcher,
794/// so Prometheus remote write goes through the batched (metric-engine) path instead
795/// of the direct `PromStoreProtocolHandler::write` path.
796pub async fn setup_test_prom_app_with_frontend_batched(
797    store_type: StorageType,
798    name: &str,
799) -> (Router, TestGuard) {
800    setup_test_prom_app_with_frontend_inner(store_type, name, true, None).await
801}
802
803/// Like [`setup_test_prom_app_with_frontend`] but wires a shared request-memory
804/// limiter into the server, mirroring production deployments that configure
805/// `max_in_flight_write_bytes`.
806pub async fn setup_test_prom_app_with_frontend_and_memory_limiter(
807    store_type: StorageType,
808    name: &str,
809    memory_limiter: Option<ServerMemoryLimiter>,
810) -> (Router, TestGuard) {
811    setup_test_prom_app_with_frontend_inner(store_type, name, false, memory_limiter).await
812}
813
814async fn setup_test_prom_app_with_frontend_inner(
815    store_type: StorageType,
816    name: &str,
817    enable_batcher: bool,
818    memory_limiter: Option<ServerMemoryLimiter>,
819) -> (Router, TestGuard) {
820    unsafe {
821        std::env::set_var("TZ", "UTC");
822    }
823
824    let instance = setup_standalone_instance(name, store_type).await;
825
826    // build physical table
827    let sql = "CREATE TABLE phy (ts timestamp time index, val double, host string primary key) engine=metric with ('physical_metric_table' = '')";
828    run_sql(sql, &instance).await;
829    let sql = "CREATE TABLE phy_ns (ts timestamp(0) time index, val double, host string primary key) engine=metric with ('physical_metric_table' = '')";
830    run_sql(sql, &instance).await;
831    // build metric tables
832    let sql = "CREATE TABLE demo (ts timestamp time index, val double, host string primary key) engine=metric with ('on_physical_table' = 'phy')";
833    run_sql(sql, &instance).await;
834    let sql = "CREATE TABLE demo_metrics (ts timestamp time index, val double, idc string primary key) engine=metric with ('on_physical_table' = 'phy')";
835    run_sql(sql, &instance).await;
836    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')";
837    run_sql(sql, &instance).await;
838
839    // insert rows
840    let sql = "INSERT INTO demo(host, val, ts) VALUES ('host1', 1.1, 0), ('host2', 2.1, 600000)";
841    run_sql(sql, &instance).await;
842    let sql =
843        "INSERT INTO demo_metrics(idc, val, ts) VALUES ('idc1', 1.1, 0), ('idc2', 2.1, 600000)";
844    run_sql(sql, &instance).await;
845    // insert a row with empty label
846    let sql = "INSERT INTO demo_metrics(val, ts) VALUES (1.1, 0)";
847    run_sql(sql, &instance).await;
848
849    // insert rows to multi_labels
850    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)";
851    run_sql(sql, &instance).await;
852
853    // build physical table
854    let sql = "CREATE TABLE phy2 (ts timestamp(9) time index, val double, host string primary key) engine=metric with ('physical_metric_table' = '')";
855    run_sql(sql, &instance).await;
856    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')";
857    run_sql(sql, &instance).await;
858    let sql = "INSERT INTO demo_metrics_with_nanos(idc, val, ts) VALUES ('idc1', 1.1, 0)";
859    run_sql(sql, &instance).await;
860
861    // a mito table with non-prometheus compatible values
862    let sql = "CREATE TABLE mito (ts timestamp(9) time index, val double, host bigint primary key) engine=mito";
863    run_sql(sql, &instance).await;
864    let sql = "INSERT INTO mito(host, val, ts) VALUES (1, 1.1, 0)";
865    run_sql(sql, &instance).await;
866
867    let http_server = build_test_prom_server(
868        instance.fe_instance().clone(),
869        enable_batcher,
870        memory_limiter,
871    )
872    .with_greptime_config_options(instance.opts.datanode_options().to_toml().unwrap())
873    .build();
874    let app = http_server.build(http_server.make_app()).unwrap();
875    (app, instance.guard)
876}
877
878/// Builds Prometheus HTTP routes for either a standalone or distributed frontend.
879/// `memory_limiter` must be set before the handlers are wired (handlers capture
880/// the limiter at wiring time, mirroring production in `frontend::server`).
881pub fn build_test_prom_server(
882    frontend_ref: Arc<Instance>,
883    enable_batcher: bool,
884    memory_limiter: Option<ServerMemoryLimiter>,
885) -> HttpServerBuilder {
886    let http_opts = HttpOptions {
887        addr: format!("127.0.0.1:{}", ports::get_port()),
888        ..Default::default()
889    };
890    // Mirror the production wiring at `frontend::server`: build the batcher from the
891    // instance's managers. A short flush interval keeps the test responsive.
892    let pending_rows_batcher = if enable_batcher {
893        LogicalTablePendingRowsBatcher::try_new(
894            frontend_ref.partition_manager().clone(),
895            frontend_ref.node_manager().clone(),
896            frontend_ref.inserter().write_dispatcher().clone(),
897            frontend_ref.catalog_manager().clone(),
898            frontend_ref.table_flownode_set_cache().clone(),
899            true,
900            frontend_ref.clone(),
901            Duration::from_millis(50),
902            1000,
903            4,
904            64,
905            64,
906            std::num::NonZeroUsize::new(1024).unwrap(),
907        )
908    } else {
909        None
910    };
911    assert_eq!(pending_rows_batcher.is_some(), enable_batcher);
912    let mut builder = HttpServerBuilder::new(http_opts)
913        .with_sql_handler(frontend_ref.clone())
914        .with_logs_handler(frontend_ref.clone());
915    if let Some(limiter) = memory_limiter {
916        builder = builder.with_memory_limiter(limiter);
917    }
918    builder
919        .with_prom_handler(
920            frontend_ref.clone(),
921            Some(frontend_ref.clone()),
922            true,
923            PromValidationMode::Strict,
924            pending_rows_batcher,
925        )
926        .with_prometheus_handler(frontend_ref)
927}
928
929pub async fn setup_grpc_server(
930    store_type: StorageType,
931    name: &str,
932) -> (GreptimeDbStandalone, Arc<GrpcServer>) {
933    setup_grpc_server_with(store_type, name, None, None, None).await
934}
935
936pub async fn setup_grpc_server_with_user_provider(
937    store_type: StorageType,
938    name: &str,
939    user_provider: Option<UserProviderRef>,
940) -> (GreptimeDbStandalone, Arc<GrpcServer>) {
941    setup_grpc_server_with(store_type, name, user_provider, None, None).await
942}
943
944/// Sets up a gRPC server backed by a standalone instance whose frontend has auto
945/// table creation disabled, for testing the server-side global switch.
946pub async fn setup_grpc_server_with_auto_create_table_disabled(
947    store_type: StorageType,
948    name: &str,
949) -> (GreptimeDbStandalone, Arc<GrpcServer>) {
950    let instance = GreptimeDbStandaloneBuilder::new(name)
951        .with_default_store_type(store_type)
952        .with_auto_create_table(false)
953        .build()
954        .await;
955    setup_grpc_server_for_instance(instance, None, None, None).await
956}
957
958pub async fn setup_grpc_server_with(
959    store_type: StorageType,
960    name: &str,
961    user_provider: Option<UserProviderRef>,
962    grpc_config: Option<GrpcServerConfig>,
963    memory_limiter: Option<servers::request_memory_limiter::ServerMemoryLimiter>,
964) -> (GreptimeDbStandalone, Arc<GrpcServer>) {
965    let instance = setup_standalone_instance(name, store_type).await;
966    setup_grpc_server_for_instance(instance, user_provider, grpc_config, memory_limiter).await
967}
968
969/// Builds and starts a gRPC server on top of an already-constructed standalone
970/// instance. This is the shared core behind the `setup_grpc_server_*` helpers.
971async fn setup_grpc_server_for_instance(
972    instance: GreptimeDbStandalone,
973    user_provider: Option<UserProviderRef>,
974    grpc_config: Option<GrpcServerConfig>,
975    memory_limiter: Option<servers::request_memory_limiter::ServerMemoryLimiter>,
976) -> (GreptimeDbStandalone, Arc<GrpcServer>) {
977    let grpc_server = setup_grpc_server_for_frontend_instance_with(
978        instance.fe_instance().clone(),
979        user_provider,
980        grpc_config,
981        memory_limiter,
982    )
983    .await;
984    (instance, grpc_server)
985}
986
987/// Builds and starts an authenticated gRPC server on an existing frontend instance.
988pub async fn setup_grpc_server_for_frontend_instance(
989    instance: Arc<Instance>,
990    user_provider: Option<UserProviderRef>,
991) -> Arc<GrpcServer> {
992    setup_grpc_server_for_frontend_instance_with(instance, user_provider, None, None).await
993}
994
995/// Starts a gRPC server backed by `instance` and returns a client authenticated
996/// as the supplied test user. The server handle keeps the listener alive.
997pub async fn setup_authenticated_grpc_database(
998    instance: Arc<Instance>,
999    username: &str,
1000    password: &str,
1001) -> (Database, Arc<GrpcServer>) {
1002    let user_provider =
1003        user_provider_from_option(&format!("static_user_provider:cmd:{username}={password}"))
1004            .unwrap();
1005    let grpc_server = setup_grpc_server_for_frontend_instance(instance, Some(user_provider)).await;
1006    let grpc_addr = grpc_server.bind_addr().unwrap().to_string();
1007    let mut database = Database::new_with_dbname(
1008        "greptime-public",
1009        Client::with_query_and_control_managers(
1010            Default::default(),
1011            Default::default(),
1012            vec![grpc_addr],
1013        ),
1014    );
1015    database.set_auth(api::v1::auth_header::AuthScheme::Basic(Basic {
1016        username: username.to_string(),
1017        password: password.to_string(),
1018    }));
1019    (database, grpc_server)
1020}
1021
1022async fn setup_grpc_server_for_frontend_instance_with(
1023    fe_instance_ref: Arc<Instance>,
1024    user_provider: Option<UserProviderRef>,
1025    grpc_config: Option<GrpcServerConfig>,
1026    memory_limiter: Option<servers::request_memory_limiter::ServerMemoryLimiter>,
1027) -> Arc<GrpcServer> {
1028    let runtime: Runtime = RuntimeBuilder::default()
1029        .worker_threads(2)
1030        .thread_name("grpc-handlers")
1031        .build()
1032        .unwrap();
1033
1034    let greptime_request_handler = GreptimeRequestHandler::new(
1035        fe_instance_ref.clone(),
1036        user_provider.clone(),
1037        Some(runtime.clone()),
1038        FlightCompression::default(),
1039    );
1040
1041    let flight_handler = Arc::new(greptime_request_handler.clone());
1042
1043    let grpc_config = grpc_config.unwrap_or_default();
1044    let mut grpc_builder = GrpcServerBuilder::new(grpc_config.clone(), runtime);
1045
1046    if let Some(limiter) = memory_limiter {
1047        grpc_builder = grpc_builder.with_memory_limiter(limiter);
1048    }
1049
1050    let grpc_builder = grpc_builder
1051        .database_handler(greptime_request_handler)
1052        .flight_handler(flight_handler)
1053        .prometheus_handler(fe_instance_ref.clone(), user_provider.clone())
1054        .otel_arrow_handler(OtelArrowServiceHandler::new(fe_instance_ref, user_provider))
1055        .with_tls_config(grpc_config.tls)
1056        .unwrap();
1057
1058    let mut grpc_server = grpc_builder.build();
1059
1060    let fe_grpc_addr = "127.0.0.1:0".parse::<SocketAddr>().unwrap();
1061    grpc_server.start(fe_grpc_addr).await.unwrap();
1062
1063    Arc::new(grpc_server)
1064}
1065
1066pub async fn setup_mysql_server(
1067    store_type: StorageType,
1068    name: &str,
1069) -> (TestGuard, Arc<Box<dyn Server>>) {
1070    setup_mysql_server_with_user_provider(store_type, name, None).await
1071}
1072
1073pub async fn setup_mysql_server_with_slow_query_threshold(
1074    store_type: StorageType,
1075    name: &str,
1076    slow_query_threshold: std::time::Duration,
1077) -> (TestGuard, Arc<Box<dyn Server>>) {
1078    let plugins = Plugins::new();
1079    let instance = setup_standalone_instance_with_plugins_and_slow_query_threshold(
1080        name,
1081        store_type,
1082        plugins,
1083        slow_query_threshold,
1084    )
1085    .await;
1086
1087    let runtime = RuntimeBuilder::default()
1088        .worker_threads(2)
1089        .thread_name("mysql-runtime")
1090        .build()
1091        .unwrap();
1092
1093    let fe_mysql_addr = format!("127.0.0.1:{}", ports::get_port());
1094
1095    let fe_instance_ref = instance.fe_instance().clone();
1096    let opts = MysqlOptions {
1097        addr: fe_mysql_addr.clone(),
1098        ..Default::default()
1099    };
1100    let mut mysql_server = MysqlServer::create_server(
1101        runtime,
1102        Arc::new(MysqlSpawnRef::new(fe_instance_ref, None)),
1103        Arc::new(MysqlSpawnConfig::new(
1104            false,
1105            Arc::new(
1106                ReloadableTlsServerConfig::try_new(opts.tls.clone())
1107                    .expect("Failed to load certificates and keys"),
1108            ),
1109            0,
1110            opts.reject_no_database.unwrap_or(false),
1111            opts.prepared_stmt_cache_size,
1112        )),
1113        None,
1114    );
1115
1116    mysql_server
1117        .start(fe_mysql_addr.parse::<SocketAddr>().unwrap())
1118        .await
1119        .unwrap();
1120
1121    (instance.guard, Arc::new(mysql_server))
1122}
1123
1124pub async fn setup_mysql_server_with_user_provider(
1125    store_type: StorageType,
1126    name: &str,
1127    user_provider: Option<UserProviderRef>,
1128) -> (TestGuard, Arc<Box<dyn Server>>) {
1129    let plugins = Plugins::new();
1130    if let Some(user_provider) = user_provider.clone() {
1131        plugins.insert::<UserProviderRef>(user_provider.clone());
1132        plugins.insert::<PermissionCheckerRef>(DefaultPermissionChecker::arc());
1133    }
1134
1135    let instance = setup_standalone_instance_with_plugins(name, store_type, plugins).await;
1136
1137    let runtime = RuntimeBuilder::default()
1138        .worker_threads(2)
1139        .thread_name("mysql-runtime")
1140        .build()
1141        .unwrap();
1142
1143    let fe_mysql_addr = format!("127.0.0.1:{}", ports::get_port());
1144
1145    let fe_instance_ref = instance.fe_instance().clone();
1146    let opts = MysqlOptions {
1147        addr: fe_mysql_addr.clone(),
1148        ..Default::default()
1149    };
1150    let mut mysql_server = MysqlServer::create_server(
1151        runtime,
1152        Arc::new(MysqlSpawnRef::new(fe_instance_ref, user_provider)),
1153        Arc::new(MysqlSpawnConfig::new(
1154            false,
1155            Arc::new(
1156                ReloadableTlsServerConfig::try_new(opts.tls.clone())
1157                    .expect("Failed to load certificates and keys"),
1158            ),
1159            0,
1160            opts.reject_no_database.unwrap_or(false),
1161            opts.prepared_stmt_cache_size,
1162        )),
1163        None,
1164    );
1165
1166    mysql_server
1167        .start(fe_mysql_addr.parse::<SocketAddr>().unwrap())
1168        .await
1169        .unwrap();
1170
1171    (instance.guard, Arc::new(mysql_server))
1172}
1173
1174pub async fn setup_pg_server(
1175    store_type: StorageType,
1176    name: &str,
1177) -> (TestGuard, Arc<Box<dyn Server>>) {
1178    setup_pg_server_with_user_provider(store_type, name, None).await
1179}
1180
1181pub async fn setup_pg_server_with_slow_query_threshold(
1182    store_type: StorageType,
1183    name: &str,
1184    slow_query_threshold: std::time::Duration,
1185) -> (TestGuard, Arc<Box<dyn Server>>) {
1186    let instance =
1187        setup_standalone_instance_with_slow_query_threshold(name, store_type, slow_query_threshold)
1188            .await;
1189
1190    let runtime = RuntimeBuilder::default()
1191        .worker_threads(2)
1192        .thread_name("pg-runtime")
1193        .build()
1194        .unwrap();
1195
1196    let fe_pg_addr = format!("127.0.0.1:{}", ports::get_port());
1197
1198    let fe_instance_ref = instance.fe_instance().clone();
1199    let opts = PostgresOptions {
1200        addr: fe_pg_addr.clone(),
1201        ..Default::default()
1202    };
1203    let tls_server_config = Arc::new(
1204        ReloadableTlsServerConfig::try_new(opts.tls.clone())
1205            .expect("Failed to load certificates and keys"),
1206    );
1207
1208    let mut pg_server = Box::new(
1209        PostgresServer::new(
1210            fe_instance_ref.clone(),
1211            fe_instance_ref.clone(),
1212            opts.tls.should_force_tls(),
1213            tls_server_config,
1214            0,
1215            runtime,
1216            None,
1217            None,
1218        )
1219        .with_influxdb_handler(fe_instance_ref.clone()),
1220    );
1221
1222    pg_server
1223        .start(fe_pg_addr.parse::<SocketAddr>().unwrap())
1224        .await
1225        .unwrap();
1226
1227    (instance.guard, Arc::new(pg_server))
1228}
1229
1230pub async fn setup_pg_server_with_user_provider(
1231    store_type: StorageType,
1232    name: &str,
1233    user_provider: Option<UserProviderRef>,
1234) -> (TestGuard, Arc<Box<dyn Server>>) {
1235    let instance = setup_standalone_instance(name, store_type).await;
1236
1237    let runtime = RuntimeBuilder::default()
1238        .worker_threads(2)
1239        .thread_name("pg-runtime")
1240        .build()
1241        .unwrap();
1242
1243    let fe_pg_addr = format!("127.0.0.1:{}", ports::get_port());
1244
1245    let fe_instance_ref = instance.fe_instance().clone();
1246    let opts = PostgresOptions {
1247        addr: fe_pg_addr.clone(),
1248        ..Default::default()
1249    };
1250    let tls_server_config = Arc::new(
1251        ReloadableTlsServerConfig::try_new(opts.tls.clone())
1252            .expect("Failed to load certificates and keys"),
1253    );
1254
1255    let mut pg_server = Box::new(
1256        PostgresServer::new(
1257            fe_instance_ref.clone(),
1258            fe_instance_ref.clone(),
1259            opts.tls.should_force_tls(),
1260            tls_server_config,
1261            0,
1262            runtime,
1263            user_provider,
1264            None,
1265        )
1266        .with_influxdb_handler(fe_instance_ref.clone()),
1267    );
1268
1269    pg_server
1270        .start(fe_pg_addr.parse::<SocketAddr>().unwrap())
1271        .await
1272        .unwrap();
1273
1274    (instance.guard, Arc::new(pg_server))
1275}
1276
1277/// Sets up a standalone instance with both a Prometheus remote-write HTTP app
1278/// (native histograms enabled) and a Postgres server attached, so native
1279/// histogram data written via remote-write can be queried over the Postgres
1280/// protocol.
1281pub async fn setup_pg_server_with_prom_native_histogram(
1282    store_type: StorageType,
1283    name: &str,
1284) -> (TestGuard, Router, Arc<Box<dyn Server>>) {
1285    unsafe {
1286        std::env::set_var("TZ", "UTC");
1287    }
1288
1289    let instance = setup_standalone_instance(name, store_type).await;
1290
1291    // Prometheus remote-write HTTP app with native histograms enabled.
1292    let http_server = build_test_prom_server(instance.fe_instance().clone(), false, None)
1293        .with_greptime_config_options(instance.opts.datanode_options().to_toml().unwrap())
1294        .build();
1295    let app = http_server.build(http_server.make_app()).unwrap();
1296
1297    // Postgres server on the same instance.
1298    let runtime = RuntimeBuilder::default()
1299        .worker_threads(2)
1300        .thread_name("pg-runtime")
1301        .build()
1302        .unwrap();
1303
1304    let fe_pg_addr = format!("127.0.0.1:{}", ports::get_port());
1305    let opts = PostgresOptions {
1306        addr: fe_pg_addr.clone(),
1307        ..Default::default()
1308    };
1309    let tls_server_config = Arc::new(
1310        ReloadableTlsServerConfig::try_new(opts.tls.clone())
1311            .expect("Failed to load certificates and keys"),
1312    );
1313
1314    let mut pg_server = Box::new(
1315        PostgresServer::new(
1316            instance.fe_instance().clone(),
1317            instance.fe_instance().clone(),
1318            opts.tls.should_force_tls(),
1319            tls_server_config,
1320            0,
1321            runtime,
1322            None,
1323            None,
1324        )
1325        .with_influxdb_handler(instance.fe_instance().clone()),
1326    );
1327
1328    pg_server
1329        .start(fe_pg_addr.parse::<SocketAddr>().unwrap())
1330        .await
1331        .unwrap();
1332
1333    (instance.guard, app, Arc::new(pg_server))
1334}
1335
1336pub(crate) async fn prepare_another_catalog_and_schema(instance: &Instance) {
1337    let catalog_manager = instance
1338        .catalog_manager()
1339        .as_any()
1340        .downcast_ref::<KvBackendCatalogManager>()
1341        .unwrap();
1342
1343    let table_metadata_manager = catalog_manager.table_metadata_manager_ref();
1344    prepare_another_catalog_and_schema_with_manager(table_metadata_manager).await;
1345}
1346
1347pub(crate) async fn prepare_another_catalog_and_schema_with_kv_backend(kv_backend: KvBackendRef) {
1348    let table_metadata_manager = TableMetadataManager::new(kv_backend);
1349    prepare_another_catalog_and_schema_with_manager(&table_metadata_manager).await;
1350}
1351
1352async fn prepare_another_catalog_and_schema_with_manager(
1353    table_metadata_manager: &TableMetadataManager,
1354) {
1355    table_metadata_manager
1356        .catalog_manager()
1357        .create(CatalogNameKey::new("another_catalog"), true)
1358        .await
1359        .unwrap();
1360    table_metadata_manager
1361        .schema_manager()
1362        .create(
1363            SchemaNameKey::new("another_catalog", "another_schema"),
1364            None,
1365            true,
1366        )
1367        .await
1368        .unwrap();
1369}
1370
1371pub async fn execute_sql(instance: &Arc<Instance>, sql: &str) -> Output {
1372    SqlQueryHandler::do_query(instance.as_ref(), sql, QueryContext::arc())
1373        .await
1374        .remove(0)
1375        .unwrap()
1376}
1377
1378pub async fn try_execute_sql(
1379    instance: &Arc<Instance>,
1380    sql: &str,
1381) -> servers::error::Result<Output> {
1382    SqlQueryHandler::do_query(instance.as_ref(), sql, QueryContext::arc())
1383        .await
1384        .remove(0)
1385}
1386
1387pub async fn execute_sql_and_expect(instance: &Arc<Instance>, sql: &str, expected: &str) {
1388    let output = execute_sql(instance, sql).await;
1389    let output = output.data.pretty_print().await;
1390    assert_eq!(output, expected.trim());
1391}