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.catalog_manager().clone(),
896            frontend_ref.table_flownode_set_cache().clone(),
897            true,
898            frontend_ref.clone(),
899            Duration::from_millis(50),
900            1000,
901            4,
902            64,
903            64,
904            std::num::NonZeroUsize::new(1024).unwrap(),
905        )
906    } else {
907        None
908    };
909    assert_eq!(pending_rows_batcher.is_some(), enable_batcher);
910    let mut builder = HttpServerBuilder::new(http_opts)
911        .with_sql_handler(frontend_ref.clone())
912        .with_logs_handler(frontend_ref.clone());
913    if let Some(limiter) = memory_limiter {
914        builder = builder.with_memory_limiter(limiter);
915    }
916    builder
917        .with_prom_handler(
918            frontend_ref.clone(),
919            Some(frontend_ref.clone()),
920            true,
921            PromValidationMode::Strict,
922            pending_rows_batcher,
923        )
924        .with_prometheus_handler(frontend_ref)
925}
926
927pub async fn setup_grpc_server(
928    store_type: StorageType,
929    name: &str,
930) -> (GreptimeDbStandalone, Arc<GrpcServer>) {
931    setup_grpc_server_with(store_type, name, None, None, None).await
932}
933
934pub async fn setup_grpc_server_with_user_provider(
935    store_type: StorageType,
936    name: &str,
937    user_provider: Option<UserProviderRef>,
938) -> (GreptimeDbStandalone, Arc<GrpcServer>) {
939    setup_grpc_server_with(store_type, name, user_provider, None, None).await
940}
941
942/// Sets up a gRPC server backed by a standalone instance whose frontend has auto
943/// table creation disabled, for testing the server-side global switch.
944pub async fn setup_grpc_server_with_auto_create_table_disabled(
945    store_type: StorageType,
946    name: &str,
947) -> (GreptimeDbStandalone, Arc<GrpcServer>) {
948    let instance = GreptimeDbStandaloneBuilder::new(name)
949        .with_default_store_type(store_type)
950        .with_auto_create_table(false)
951        .build()
952        .await;
953    setup_grpc_server_for_instance(instance, None, None, None).await
954}
955
956pub async fn setup_grpc_server_with(
957    store_type: StorageType,
958    name: &str,
959    user_provider: Option<UserProviderRef>,
960    grpc_config: Option<GrpcServerConfig>,
961    memory_limiter: Option<servers::request_memory_limiter::ServerMemoryLimiter>,
962) -> (GreptimeDbStandalone, Arc<GrpcServer>) {
963    let instance = setup_standalone_instance(name, store_type).await;
964    setup_grpc_server_for_instance(instance, user_provider, grpc_config, memory_limiter).await
965}
966
967/// Builds and starts a gRPC server on top of an already-constructed standalone
968/// instance. This is the shared core behind the `setup_grpc_server_*` helpers.
969async fn setup_grpc_server_for_instance(
970    instance: GreptimeDbStandalone,
971    user_provider: Option<UserProviderRef>,
972    grpc_config: Option<GrpcServerConfig>,
973    memory_limiter: Option<servers::request_memory_limiter::ServerMemoryLimiter>,
974) -> (GreptimeDbStandalone, Arc<GrpcServer>) {
975    let grpc_server = setup_grpc_server_for_frontend_instance_with(
976        instance.fe_instance().clone(),
977        user_provider,
978        grpc_config,
979        memory_limiter,
980    )
981    .await;
982    (instance, grpc_server)
983}
984
985/// Builds and starts an authenticated gRPC server on an existing frontend instance.
986pub async fn setup_grpc_server_for_frontend_instance(
987    instance: Arc<Instance>,
988    user_provider: Option<UserProviderRef>,
989) -> Arc<GrpcServer> {
990    setup_grpc_server_for_frontend_instance_with(instance, user_provider, None, None).await
991}
992
993/// Starts a gRPC server backed by `instance` and returns a client authenticated
994/// as the supplied test user. The server handle keeps the listener alive.
995pub async fn setup_authenticated_grpc_database(
996    instance: Arc<Instance>,
997    username: &str,
998    password: &str,
999) -> (Database, Arc<GrpcServer>) {
1000    let user_provider =
1001        user_provider_from_option(&format!("static_user_provider:cmd:{username}={password}"))
1002            .unwrap();
1003    let grpc_server = setup_grpc_server_for_frontend_instance(instance, Some(user_provider)).await;
1004    let grpc_addr = grpc_server.bind_addr().unwrap().to_string();
1005    let mut database = Database::new_with_dbname(
1006        "greptime-public",
1007        Client::with_query_and_control_managers(
1008            Default::default(),
1009            Default::default(),
1010            vec![grpc_addr],
1011        ),
1012    );
1013    database.set_auth(api::v1::auth_header::AuthScheme::Basic(Basic {
1014        username: username.to_string(),
1015        password: password.to_string(),
1016    }));
1017    (database, grpc_server)
1018}
1019
1020async fn setup_grpc_server_for_frontend_instance_with(
1021    fe_instance_ref: Arc<Instance>,
1022    user_provider: Option<UserProviderRef>,
1023    grpc_config: Option<GrpcServerConfig>,
1024    memory_limiter: Option<servers::request_memory_limiter::ServerMemoryLimiter>,
1025) -> Arc<GrpcServer> {
1026    let runtime: Runtime = RuntimeBuilder::default()
1027        .worker_threads(2)
1028        .thread_name("grpc-handlers")
1029        .build()
1030        .unwrap();
1031
1032    let greptime_request_handler = GreptimeRequestHandler::new(
1033        fe_instance_ref.clone(),
1034        user_provider.clone(),
1035        Some(runtime.clone()),
1036        FlightCompression::default(),
1037    );
1038
1039    let flight_handler = Arc::new(greptime_request_handler.clone());
1040
1041    let grpc_config = grpc_config.unwrap_or_default();
1042    let mut grpc_builder = GrpcServerBuilder::new(grpc_config.clone(), runtime);
1043
1044    if let Some(limiter) = memory_limiter {
1045        grpc_builder = grpc_builder.with_memory_limiter(limiter);
1046    }
1047
1048    let grpc_builder = grpc_builder
1049        .database_handler(greptime_request_handler)
1050        .flight_handler(flight_handler)
1051        .prometheus_handler(fe_instance_ref.clone(), user_provider.clone())
1052        .otel_arrow_handler(OtelArrowServiceHandler::new(fe_instance_ref, user_provider))
1053        .with_tls_config(grpc_config.tls)
1054        .unwrap();
1055
1056    let mut grpc_server = grpc_builder.build();
1057
1058    let fe_grpc_addr = "127.0.0.1:0".parse::<SocketAddr>().unwrap();
1059    grpc_server.start(fe_grpc_addr).await.unwrap();
1060
1061    Arc::new(grpc_server)
1062}
1063
1064pub async fn setup_mysql_server(
1065    store_type: StorageType,
1066    name: &str,
1067) -> (TestGuard, Arc<Box<dyn Server>>) {
1068    setup_mysql_server_with_user_provider(store_type, name, None).await
1069}
1070
1071pub async fn setup_mysql_server_with_slow_query_threshold(
1072    store_type: StorageType,
1073    name: &str,
1074    slow_query_threshold: std::time::Duration,
1075) -> (TestGuard, Arc<Box<dyn Server>>) {
1076    let plugins = Plugins::new();
1077    let instance = setup_standalone_instance_with_plugins_and_slow_query_threshold(
1078        name,
1079        store_type,
1080        plugins,
1081        slow_query_threshold,
1082    )
1083    .await;
1084
1085    let runtime = RuntimeBuilder::default()
1086        .worker_threads(2)
1087        .thread_name("mysql-runtime")
1088        .build()
1089        .unwrap();
1090
1091    let fe_mysql_addr = format!("127.0.0.1:{}", ports::get_port());
1092
1093    let fe_instance_ref = instance.fe_instance().clone();
1094    let opts = MysqlOptions {
1095        addr: fe_mysql_addr.clone(),
1096        ..Default::default()
1097    };
1098    let mut mysql_server = MysqlServer::create_server(
1099        runtime,
1100        Arc::new(MysqlSpawnRef::new(fe_instance_ref, None)),
1101        Arc::new(MysqlSpawnConfig::new(
1102            false,
1103            Arc::new(
1104                ReloadableTlsServerConfig::try_new(opts.tls.clone())
1105                    .expect("Failed to load certificates and keys"),
1106            ),
1107            0,
1108            opts.reject_no_database.unwrap_or(false),
1109            opts.prepared_stmt_cache_size,
1110        )),
1111        None,
1112    );
1113
1114    mysql_server
1115        .start(fe_mysql_addr.parse::<SocketAddr>().unwrap())
1116        .await
1117        .unwrap();
1118
1119    (instance.guard, Arc::new(mysql_server))
1120}
1121
1122pub async fn setup_mysql_server_with_user_provider(
1123    store_type: StorageType,
1124    name: &str,
1125    user_provider: Option<UserProviderRef>,
1126) -> (TestGuard, Arc<Box<dyn Server>>) {
1127    let plugins = Plugins::new();
1128    if let Some(user_provider) = user_provider.clone() {
1129        plugins.insert::<UserProviderRef>(user_provider.clone());
1130        plugins.insert::<PermissionCheckerRef>(DefaultPermissionChecker::arc());
1131    }
1132
1133    let instance = setup_standalone_instance_with_plugins(name, store_type, plugins).await;
1134
1135    let runtime = RuntimeBuilder::default()
1136        .worker_threads(2)
1137        .thread_name("mysql-runtime")
1138        .build()
1139        .unwrap();
1140
1141    let fe_mysql_addr = format!("127.0.0.1:{}", ports::get_port());
1142
1143    let fe_instance_ref = instance.fe_instance().clone();
1144    let opts = MysqlOptions {
1145        addr: fe_mysql_addr.clone(),
1146        ..Default::default()
1147    };
1148    let mut mysql_server = MysqlServer::create_server(
1149        runtime,
1150        Arc::new(MysqlSpawnRef::new(fe_instance_ref, user_provider)),
1151        Arc::new(MysqlSpawnConfig::new(
1152            false,
1153            Arc::new(
1154                ReloadableTlsServerConfig::try_new(opts.tls.clone())
1155                    .expect("Failed to load certificates and keys"),
1156            ),
1157            0,
1158            opts.reject_no_database.unwrap_or(false),
1159            opts.prepared_stmt_cache_size,
1160        )),
1161        None,
1162    );
1163
1164    mysql_server
1165        .start(fe_mysql_addr.parse::<SocketAddr>().unwrap())
1166        .await
1167        .unwrap();
1168
1169    (instance.guard, Arc::new(mysql_server))
1170}
1171
1172pub async fn setup_pg_server(
1173    store_type: StorageType,
1174    name: &str,
1175) -> (TestGuard, Arc<Box<dyn Server>>) {
1176    setup_pg_server_with_user_provider(store_type, name, None).await
1177}
1178
1179pub async fn setup_pg_server_with_slow_query_threshold(
1180    store_type: StorageType,
1181    name: &str,
1182    slow_query_threshold: std::time::Duration,
1183) -> (TestGuard, Arc<Box<dyn Server>>) {
1184    let instance =
1185        setup_standalone_instance_with_slow_query_threshold(name, store_type, slow_query_threshold)
1186            .await;
1187
1188    let runtime = RuntimeBuilder::default()
1189        .worker_threads(2)
1190        .thread_name("pg-runtime")
1191        .build()
1192        .unwrap();
1193
1194    let fe_pg_addr = format!("127.0.0.1:{}", ports::get_port());
1195
1196    let fe_instance_ref = instance.fe_instance().clone();
1197    let opts = PostgresOptions {
1198        addr: fe_pg_addr.clone(),
1199        ..Default::default()
1200    };
1201    let tls_server_config = Arc::new(
1202        ReloadableTlsServerConfig::try_new(opts.tls.clone())
1203            .expect("Failed to load certificates and keys"),
1204    );
1205
1206    let mut pg_server = Box::new(PostgresServer::new(
1207        fe_instance_ref,
1208        opts.tls.should_force_tls(),
1209        tls_server_config,
1210        0,
1211        runtime,
1212        None,
1213        None,
1214    ));
1215
1216    pg_server
1217        .start(fe_pg_addr.parse::<SocketAddr>().unwrap())
1218        .await
1219        .unwrap();
1220
1221    (instance.guard, Arc::new(pg_server))
1222}
1223
1224pub async fn setup_pg_server_with_user_provider(
1225    store_type: StorageType,
1226    name: &str,
1227    user_provider: Option<UserProviderRef>,
1228) -> (TestGuard, Arc<Box<dyn Server>>) {
1229    let instance = setup_standalone_instance(name, store_type).await;
1230
1231    let runtime = RuntimeBuilder::default()
1232        .worker_threads(2)
1233        .thread_name("pg-runtime")
1234        .build()
1235        .unwrap();
1236
1237    let fe_pg_addr = format!("127.0.0.1:{}", ports::get_port());
1238
1239    let fe_instance_ref = instance.fe_instance().clone();
1240    let opts = PostgresOptions {
1241        addr: fe_pg_addr.clone(),
1242        ..Default::default()
1243    };
1244    let tls_server_config = Arc::new(
1245        ReloadableTlsServerConfig::try_new(opts.tls.clone())
1246            .expect("Failed to load certificates and keys"),
1247    );
1248
1249    let mut pg_server = Box::new(PostgresServer::new(
1250        fe_instance_ref,
1251        opts.tls.should_force_tls(),
1252        tls_server_config,
1253        0,
1254        runtime,
1255        user_provider,
1256        None,
1257    ));
1258
1259    pg_server
1260        .start(fe_pg_addr.parse::<SocketAddr>().unwrap())
1261        .await
1262        .unwrap();
1263
1264    (instance.guard, Arc::new(pg_server))
1265}
1266
1267/// Sets up a standalone instance with both a Prometheus remote-write HTTP app
1268/// (native histograms enabled) and a Postgres server attached, so native
1269/// histogram data written via remote-write can be queried over the Postgres
1270/// protocol.
1271pub async fn setup_pg_server_with_prom_native_histogram(
1272    store_type: StorageType,
1273    name: &str,
1274) -> (TestGuard, Router, Arc<Box<dyn Server>>) {
1275    unsafe {
1276        std::env::set_var("TZ", "UTC");
1277    }
1278
1279    let instance = setup_standalone_instance(name, store_type).await;
1280
1281    // Prometheus remote-write HTTP app with native histograms enabled.
1282    let http_server = build_test_prom_server(instance.fe_instance().clone(), false, None)
1283        .with_greptime_config_options(instance.opts.datanode_options().to_toml().unwrap())
1284        .build();
1285    let app = http_server.build(http_server.make_app()).unwrap();
1286
1287    // Postgres server on the same instance.
1288    let runtime = RuntimeBuilder::default()
1289        .worker_threads(2)
1290        .thread_name("pg-runtime")
1291        .build()
1292        .unwrap();
1293
1294    let fe_pg_addr = format!("127.0.0.1:{}", ports::get_port());
1295    let opts = PostgresOptions {
1296        addr: fe_pg_addr.clone(),
1297        ..Default::default()
1298    };
1299    let tls_server_config = Arc::new(
1300        ReloadableTlsServerConfig::try_new(opts.tls.clone())
1301            .expect("Failed to load certificates and keys"),
1302    );
1303
1304    let mut pg_server = Box::new(PostgresServer::new(
1305        instance.fe_instance().clone(),
1306        opts.tls.should_force_tls(),
1307        tls_server_config,
1308        0,
1309        runtime,
1310        None,
1311        None,
1312    ));
1313
1314    pg_server
1315        .start(fe_pg_addr.parse::<SocketAddr>().unwrap())
1316        .await
1317        .unwrap();
1318
1319    (instance.guard, app, Arc::new(pg_server))
1320}
1321
1322pub(crate) async fn prepare_another_catalog_and_schema(instance: &Instance) {
1323    let catalog_manager = instance
1324        .catalog_manager()
1325        .as_any()
1326        .downcast_ref::<KvBackendCatalogManager>()
1327        .unwrap();
1328
1329    let table_metadata_manager = catalog_manager.table_metadata_manager_ref();
1330    prepare_another_catalog_and_schema_with_manager(table_metadata_manager).await;
1331}
1332
1333pub(crate) async fn prepare_another_catalog_and_schema_with_kv_backend(kv_backend: KvBackendRef) {
1334    let table_metadata_manager = TableMetadataManager::new(kv_backend);
1335    prepare_another_catalog_and_schema_with_manager(&table_metadata_manager).await;
1336}
1337
1338async fn prepare_another_catalog_and_schema_with_manager(
1339    table_metadata_manager: &TableMetadataManager,
1340) {
1341    table_metadata_manager
1342        .catalog_manager()
1343        .create(CatalogNameKey::new("another_catalog"), true)
1344        .await
1345        .unwrap();
1346    table_metadata_manager
1347        .schema_manager()
1348        .create(
1349            SchemaNameKey::new("another_catalog", "another_schema"),
1350            None,
1351            true,
1352        )
1353        .await
1354        .unwrap();
1355}
1356
1357pub async fn execute_sql(instance: &Arc<Instance>, sql: &str) -> Output {
1358    SqlQueryHandler::do_query(instance.as_ref(), sql, QueryContext::arc())
1359        .await
1360        .remove(0)
1361        .unwrap()
1362}
1363
1364pub async fn try_execute_sql(
1365    instance: &Arc<Instance>,
1366    sql: &str,
1367) -> servers::error::Result<Output> {
1368    SqlQueryHandler::do_query(instance.as_ref(), sql, QueryContext::arc())
1369        .await
1370        .remove(0)
1371}
1372
1373pub async fn execute_sql_and_expect(instance: &Arc<Instance>, sql: &str, expected: &str) {
1374    let output = execute_sql(instance, sql).await;
1375    let output = output.data.pretty_print().await;
1376    assert_eq!(output, expected.trim());
1377}