From 1b38b6ec5db11bdaf67a2721a89eb69b7faa3070 Mon Sep 17 00:00:00 2001 From: lyang24 Date: Wed, 10 Jul 2024 16:26:25 -0700 Subject: [PATCH] This commit introduces information_schema.views table. The VIEWS table provides information about views in databases. --- src/catalog/src/information_schema.rs | 7 + .../src/information_schema/table_names.rs | 1 + src/catalog/src/information_schema/views.rs | 289 ++++++++++++++++++ src/common/catalog/src/consts.rs | 2 + .../common/information_schema/views.result | 66 ++++ .../common/information_schema/views.sql | 28 ++ .../common/show/show_databases_tables.result | 3 + .../common/system/information_schema.result | 11 + .../standalone/common/view/create.result | 1 + 9 files changed, 408 insertions(+) create mode 100644 src/catalog/src/information_schema/views.rs create mode 100644 tests/cases/standalone/common/information_schema/views.result create mode 100644 tests/cases/standalone/common/information_schema/views.sql diff --git a/src/catalog/src/information_schema.rs b/src/catalog/src/information_schema.rs index 5a812ff6c1ea..3521ccf1359b 100644 --- a/src/catalog/src/information_schema.rs +++ b/src/catalog/src/information_schema.rs @@ -25,6 +25,7 @@ mod table_constraints; mod table_names; pub mod tables; pub(crate) mod utils; +mod views; use std::collections::HashMap; use std::sync::{Arc, Weak}; @@ -46,6 +47,7 @@ use table::metadata::{ }; use table::{Table, TableRef}; pub use table_names::*; +use views::InformationSchemaViews; use self::columns::InformationSchemaColumns; use crate::error::Result; @@ -177,6 +179,7 @@ impl InformationSchemaProvider { } tables.insert(TABLES.to_string(), self.build_table(TABLES).unwrap()); + tables.insert(VIEWS.to_string(), self.build_table(VIEWS).unwrap()); tables.insert(SCHEMATA.to_string(), self.build_table(SCHEMATA).unwrap()); tables.insert(COLUMNS.to_string(), self.build_table(COLUMNS).unwrap()); tables.insert( @@ -262,6 +265,10 @@ impl InformationSchemaProvider { CLUSTER_INFO => Some(Arc::new(InformationSchemaClusterInfo::new( self.catalog_manager.clone(), )) as _), + VIEWS => Some(Arc::new(InformationSchemaViews::new( + self.catalog_name.clone(), + self.catalog_manager.clone(), + )) as _), _ => None, } } diff --git a/src/catalog/src/information_schema/table_names.rs b/src/catalog/src/information_schema/table_names.rs index eb856e031834..5d291bc67812 100644 --- a/src/catalog/src/information_schema/table_names.rs +++ b/src/catalog/src/information_schema/table_names.rs @@ -43,3 +43,4 @@ pub const PARTITIONS: &str = "partitions"; pub const REGION_PEERS: &str = "region_peers"; pub const TABLE_CONSTRAINTS: &str = "table_constraints"; pub const CLUSTER_INFO: &str = "cluster_info"; +pub const VIEWS: &str = "views"; diff --git a/src/catalog/src/information_schema/views.rs b/src/catalog/src/information_schema/views.rs new file mode 100644 index 000000000000..985fd27850ab --- /dev/null +++ b/src/catalog/src/information_schema/views.rs @@ -0,0 +1,289 @@ +// Copyright 2023 Greptime Team +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::sync::{Arc, Weak}; + +use arrow_schema::SchemaRef as ArrowSchemaRef; +use common_catalog::consts::INFORMATION_SCHEMA_VIEW_TABLE_ID; +use common_error::ext::BoxedError; +use common_recordbatch::adapter::RecordBatchStreamAdapter; +use common_recordbatch::{RecordBatch, SendableRecordBatchStream}; +use datafusion::execution::TaskContext; +use datafusion::physical_plan::stream::RecordBatchStreamAdapter as DfRecordBatchStreamAdapter; +use datafusion::physical_plan::streaming::PartitionStream as DfPartitionStream; +use datafusion::physical_plan::SendableRecordBatchStream as DfSendableRecordBatchStream; +use datatypes::prelude::{ConcreteDataType, ScalarVectorBuilder, VectorRef}; +use datatypes::schema::{ColumnSchema, Schema, SchemaRef}; +use datatypes::value::Value; +use datatypes::vectors::{BooleanVectorBuilder, StringVectorBuilder}; +use futures::TryStreamExt; +use snafu::{OptionExt, ResultExt}; +use store_api::storage::{ScanRequest, TableId}; +use table::metadata::TableType; + +use super::VIEWS; +use crate::error::{ + CastManagerSnafu, CreateRecordBatchSnafu, GetViewCacheSnafu, InternalSnafu, Result, + UpgradeWeakCatalogManagerRefSnafu, ViewInfoNotFoundSnafu, +}; +use crate::information_schema::{InformationTable, Predicates}; +use crate::kvbackend::KvBackendCatalogManager; +use crate::CatalogManager; +const INIT_CAPACITY: usize = 42; + +pub const TABLE_CATALOG: &str = "table_catalog"; +pub const TABLE_SCHEMA: &str = "table_schema"; +pub const TABLE_NAME: &str = "table_name"; +pub const VIEW_DEFINITION: &str = "view_definition"; +pub const CHECK_OPTION: &str = "check_option"; +pub const IS_UPDATABLE: &str = "is_updatable"; +pub const DEFINER: &str = "definer"; +pub const SECURITY_TYPE: &str = "security_type"; +pub const CHARACTER_SET_CLIENT: &str = "character_set_client"; +pub const COLLATION_CONNECTION: &str = "collation_connection"; + +/// The `information_schema.views` to provides information about views in databases. +pub(super) struct InformationSchemaViews { + schema: SchemaRef, + catalog_name: String, + catalog_manager: Weak, +} + +impl InformationSchemaViews { + pub(super) fn new(catalog_name: String, catalog_manager: Weak) -> Self { + Self { + schema: Self::schema(), + catalog_name, + catalog_manager, + } + } + + pub(crate) fn schema() -> SchemaRef { + Arc::new(Schema::new(vec![ + ColumnSchema::new(TABLE_CATALOG, ConcreteDataType::string_datatype(), false), + ColumnSchema::new(TABLE_SCHEMA, ConcreteDataType::string_datatype(), false), + ColumnSchema::new(TABLE_NAME, ConcreteDataType::string_datatype(), false), + ColumnSchema::new(VIEW_DEFINITION, ConcreteDataType::string_datatype(), false), + ColumnSchema::new(CHECK_OPTION, ConcreteDataType::string_datatype(), true), + ColumnSchema::new(IS_UPDATABLE, ConcreteDataType::boolean_datatype(), true), + ColumnSchema::new(DEFINER, ConcreteDataType::string_datatype(), true), + ColumnSchema::new(SECURITY_TYPE, ConcreteDataType::string_datatype(), true), + ColumnSchema::new( + CHARACTER_SET_CLIENT, + ConcreteDataType::string_datatype(), + true, + ), + ColumnSchema::new( + COLLATION_CONNECTION, + ConcreteDataType::string_datatype(), + true, + ), + ])) + } + + fn builder(&self) -> InformationSchemaViewsBuilder { + InformationSchemaViewsBuilder::new( + self.schema.clone(), + self.catalog_name.clone(), + self.catalog_manager.clone(), + ) + } +} + +impl InformationTable for InformationSchemaViews { + fn table_id(&self) -> TableId { + INFORMATION_SCHEMA_VIEW_TABLE_ID + } + + fn table_name(&self) -> &'static str { + VIEWS + } + + fn schema(&self) -> SchemaRef { + self.schema.clone() + } + + fn to_stream(&self, request: ScanRequest) -> Result { + let schema = self.schema.arrow_schema().clone(); + let mut builder = self.builder(); + let stream = Box::pin(DfRecordBatchStreamAdapter::new( + schema, + futures::stream::once(async move { + builder + .make_views(Some(request)) + .await + .map(|x| x.into_df_record_batch()) + .map_err(|err| { + datafusion::error::DataFusionError::External(format!("{err:?}").into()) + }) + }), + )); + Ok(Box::pin( + RecordBatchStreamAdapter::try_new(stream) + .map_err(BoxedError::new) + .context(InternalSnafu)?, + )) + } +} + +/// Builds the `information_schema.VIEWS` table row by row +/// +/// Columns are based on +struct InformationSchemaViewsBuilder { + schema: SchemaRef, + catalog_name: String, + catalog_manager: Weak, + + catalog_names: StringVectorBuilder, + schema_names: StringVectorBuilder, + table_names: StringVectorBuilder, + view_definitions: StringVectorBuilder, + check_options: StringVectorBuilder, + is_updatable: BooleanVectorBuilder, + definer: StringVectorBuilder, + security_type: StringVectorBuilder, + character_set_client: StringVectorBuilder, + collation_connection: StringVectorBuilder, +} + +impl InformationSchemaViewsBuilder { + fn new( + schema: SchemaRef, + catalog_name: String, + catalog_manager: Weak, + ) -> Self { + Self { + schema, + catalog_name, + catalog_manager, + catalog_names: StringVectorBuilder::with_capacity(INIT_CAPACITY), + schema_names: StringVectorBuilder::with_capacity(INIT_CAPACITY), + table_names: StringVectorBuilder::with_capacity(INIT_CAPACITY), + view_definitions: StringVectorBuilder::with_capacity(INIT_CAPACITY), + check_options: StringVectorBuilder::with_capacity(INIT_CAPACITY), + is_updatable: BooleanVectorBuilder::with_capacity(INIT_CAPACITY), + definer: StringVectorBuilder::with_capacity(INIT_CAPACITY), + security_type: StringVectorBuilder::with_capacity(INIT_CAPACITY), + character_set_client: StringVectorBuilder::with_capacity(INIT_CAPACITY), + collation_connection: StringVectorBuilder::with_capacity(INIT_CAPACITY), + } + } + + /// Construct the `information_schema.views` virtual table + async fn make_views(&mut self, request: Option) -> Result { + let catalog_name = self.catalog_name.clone(); + let catalog_manager = self + .catalog_manager + .upgrade() + .context(UpgradeWeakCatalogManagerRefSnafu)?; + let predicates = Predicates::from_scan_request(&request); + let view_info_cache = catalog_manager + .as_any() + .downcast_ref::() + .context(CastManagerSnafu)? + .view_info_cache()?; + + for schema_name in catalog_manager.schema_names(&catalog_name).await? { + let mut stream = catalog_manager.tables(&catalog_name, &schema_name); + + while let Some(table) = stream.try_next().await? { + let table_info = table.table_info(); + if table_info.table_type == TableType::View { + let view_info = view_info_cache + .get(table_info.ident.table_id) + .await + .context(GetViewCacheSnafu)? + .context(ViewInfoNotFoundSnafu { + name: &table_info.name, + })?; + self.add_view( + &predicates, + &catalog_name, + &schema_name, + &table_info.name, + &view_info.definition, + ) + } + } + } + + self.finish() + } + + fn add_view( + &mut self, + predicates: &Predicates, + catalog_name: &str, + schema_name: &str, + table_name: &str, + definition: &str, + ) { + let row = [ + (TABLE_CATALOG, &Value::from(catalog_name)), + (TABLE_SCHEMA, &Value::from(schema_name)), + (TABLE_NAME, &Value::from(table_name)), + ]; + + if !predicates.eval(&row) { + return; + } + self.catalog_names.push(Some(catalog_name)); + self.schema_names.push(Some(schema_name)); + self.table_names.push(Some(table_name)); + self.view_definitions.push(Some(definition)); + self.check_options.push(None); + self.is_updatable.push(Some(true)); + self.definer.push(None); + self.security_type.push(None); + self.character_set_client.push(Some("utf8")); + self.collation_connection.push(Some("utf8_bin")); + } + + fn finish(&mut self) -> Result { + let columns: Vec = vec![ + Arc::new(self.catalog_names.finish()), + Arc::new(self.schema_names.finish()), + Arc::new(self.table_names.finish()), + Arc::new(self.view_definitions.finish()), + Arc::new(self.check_options.finish()), + Arc::new(self.is_updatable.finish()), + Arc::new(self.definer.finish()), + Arc::new(self.security_type.finish()), + Arc::new(self.character_set_client.finish()), + Arc::new(self.collation_connection.finish()), + ]; + RecordBatch::new(self.schema.clone(), columns).context(CreateRecordBatchSnafu) + } +} + +impl DfPartitionStream for InformationSchemaViews { + fn schema(&self) -> &ArrowSchemaRef { + self.schema.arrow_schema() + } + + fn execute(&self, _: Arc) -> DfSendableRecordBatchStream { + let schema = self.schema.arrow_schema().clone(); + let mut builder = self.builder(); + Box::pin(DfRecordBatchStreamAdapter::new( + schema, + futures::stream::once(async move { + builder + .make_views(None) + .await + .map(|x| x.into_df_record_batch()) + .map_err(Into::into) + }), + )) + } +} diff --git a/src/common/catalog/src/consts.rs b/src/common/catalog/src/consts.rs index 876d4bcb67f4..2a5e05fad2f4 100644 --- a/src/common/catalog/src/consts.rs +++ b/src/common/catalog/src/consts.rs @@ -93,6 +93,8 @@ pub const INFORMATION_SCHEMA_REGION_PEERS_TABLE_ID: u32 = 29; pub const INFORMATION_SCHEMA_TABLE_CONSTRAINTS_TABLE_ID: u32 = 30; /// id for information_schema.cluster_info pub const INFORMATION_SCHEMA_CLUSTER_INFO_TABLE_ID: u32 = 31; +/// id for information_schema.VIEWS +pub const INFORMATION_SCHEMA_VIEW_TABLE_ID: u32 = 32; /// ----- End of information_schema tables ----- pub const MITO_ENGINE: &str = "mito"; diff --git a/tests/cases/standalone/common/information_schema/views.result b/tests/cases/standalone/common/information_schema/views.result new file mode 100644 index 000000000000..c7a61d4922dc --- /dev/null +++ b/tests/cases/standalone/common/information_schema/views.result @@ -0,0 +1,66 @@ +--- test information_schema.views ---- +create schema test_information_schema_views; + +Affected Rows: 1 + +use test_information_schema_views; + +Affected Rows: 0 + +USE test_information_schema_views; + +Affected Rows: 0 + +create table t1 (ts timestamp time index, val1 int); + +Affected Rows: 0 + +create table t2 (ts timestamp time index, val2 int primary key); + +Affected Rows: 0 + +create view myview as select * from t1 where val1 > 5; + +Affected Rows: 0 + +create view myview2 as select * from t2 inner join t1 on t1.val1 = t2.val2; + +Affected Rows: 0 + +select table_catalog, table_schema, table_name, view_definition from information_schema.views order by table_name; + ++---------------+-------------------------------+------------+----------------------------------------------------------------------+ +| table_catalog | table_schema | table_name | view_definition | ++---------------+-------------------------------+------------+----------------------------------------------------------------------+ +| greptime | test_information_schema_views | myview | CREATE VIEW myview AS SELECT * FROM t1 WHERE val1 > 5 | +| greptime | test_information_schema_views | myview2 | CREATE VIEW myview2 AS SELECT * FROM t2 JOIN t1 ON t1.val1 = t2.val2 | ++---------------+-------------------------------+------------+----------------------------------------------------------------------+ + +drop view myview; + +Affected Rows: 0 + +select table_catalog, table_schema, table_name, view_definition from information_schema.views order by table_name; + ++---------------+-------------------------------+------------+----------------------------------------------------------------------+ +| table_catalog | table_schema | table_name | view_definition | ++---------------+-------------------------------+------------+----------------------------------------------------------------------+ +| greptime | test_information_schema_views | myview2 | CREATE VIEW myview2 AS SELECT * FROM t2 JOIN t1 ON t1.val1 = t2.val2 | ++---------------+-------------------------------+------------+----------------------------------------------------------------------+ + +drop view myview2; + +Affected Rows: 0 + +drop table t1, t2; + +Affected Rows: 0 + +USE public; + +Affected Rows: 0 + +drop schema test_information_schema_views; + +Affected Rows: 0 + diff --git a/tests/cases/standalone/common/information_schema/views.sql b/tests/cases/standalone/common/information_schema/views.sql new file mode 100644 index 000000000000..665652d2c520 --- /dev/null +++ b/tests/cases/standalone/common/information_schema/views.sql @@ -0,0 +1,28 @@ +--- test information_schema.views ---- +create schema test_information_schema_views; + +use test_information_schema_views; + +USE test_information_schema_views; + +create table t1 (ts timestamp time index, val1 int); + +create table t2 (ts timestamp time index, val2 int primary key); + +create view myview as select * from t1 where val1 > 5; + +create view myview2 as select * from t2 inner join t1 on t1.val1 = t2.val2; + +select table_catalog, table_schema, table_name, view_definition from information_schema.views order by table_name; + +drop view myview; + +select table_catalog, table_schema, table_name, view_definition from information_schema.views order by table_name; + +drop view myview2; + +drop table t1, t2; + +USE public; + +drop schema test_information_schema_views; diff --git a/tests/cases/standalone/common/show/show_databases_tables.result b/tests/cases/standalone/common/show/show_databases_tables.result index 8f5ec97bda2a..21abd9d2ed11 100644 --- a/tests/cases/standalone/common/show/show_databases_tables.result +++ b/tests/cases/standalone/common/show/show_databases_tables.result @@ -56,6 +56,7 @@ SHOW TABLES; | table_privileges | | tables | | triggers | +| views | +---------------------------------------+ SHOW TABLES LIKE 'tables'; @@ -100,6 +101,7 @@ SHOW FULL TABLES; | table_privileges | LOCAL TEMPORARY | | tables | LOCAL TEMPORARY | | triggers | LOCAL TEMPORARY | +| views | LOCAL TEMPORARY | +---------------------------------------+-----------------+ -- SQLNESS REPLACE (\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}) DATETIME @@ -137,6 +139,7 @@ SHOW TABLE STATUS; | table_privileges | | 11 | Fixed | 0 | 0 | 0 | 0 | 0 | 0 | 0 | DATETIME | | | | 0 | | | | tables | | 11 | Fixed | 0 | 0 | 0 | 0 | 0 | 0 | 0 | DATETIME | | | | 0 | | | | triggers | | 11 | Fixed | 0 | 0 | 0 | 0 | 0 | 0 | 0 | DATETIME | | | | 0 | | | +| views | | 11 | Fixed | 0 | 0 | 0 | 0 | 0 | 0 | 0 | DATETIME | | | | 0 | | | +---------------------------------------+--------+---------+------------+------+----------------+-------------+-----------------+--------------+-----------+----------------+-------------------------+-------------+------------+-----------+----------+----------------+---------+ -- SQLNESS REPLACE (\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}) DATETIME diff --git a/tests/cases/standalone/common/system/information_schema.result b/tests/cases/standalone/common/system/information_schema.result index 3f38df1c43ed..949eacaa3e6d 100644 --- a/tests/cases/standalone/common/system/information_schema.result +++ b/tests/cases/standalone/common/system/information_schema.result @@ -42,6 +42,7 @@ order by table_schema, table_name; | greptime | information_schema | table_privileges | LOCAL TEMPORARY | 23 | 0 | 0 | 0 | 0 | 0 | | 11 | Fixed | 0 | 0 | 0 | DATETIME | | | | 0 | | | Y | | greptime | information_schema | tables | LOCAL TEMPORARY | 3 | 0 | 0 | 0 | 0 | 0 | | 11 | Fixed | 0 | 0 | 0 | DATETIME | | | | 0 | | | Y | | greptime | information_schema | triggers | LOCAL TEMPORARY | 24 | 0 | 0 | 0 | 0 | 0 | | 11 | Fixed | 0 | 0 | 0 | DATETIME | | | | 0 | | | Y | +| greptime | information_schema | views | LOCAL TEMPORARY | 32 | 0 | 0 | 0 | 0 | 0 | | 11 | Fixed | 0 | 0 | 0 | DATETIME | | | | 0 | | | Y | | greptime | public | numbers | LOCAL TEMPORARY | 2 | 0 | 0 | 0 | 0 | 0 | test_engine | 11 | Fixed | 0 | 0 | 0 | DATETIME | | | | 0 | | | Y | +---------------+--------------------+---------------------------------------+-----------------+----------+-------------+-----------------+--------------+------------------+----------------+-------------+---------+------------+------------+-----------+----------------+-------------------------+-------------+------------+-----------------+----------+----------------+---------------+-----------+ @@ -387,6 +388,16 @@ select * from information_schema.columns order by table_schema, table_name, colu | greptime | information_schema | triggers | trigger_catalog | 1 | 2147483647 | 2147483647 | | | | utf8 | utf8_bin | | | select,insert | | String | string | FIELD | | No | string | | | | greptime | information_schema | triggers | trigger_name | 3 | 2147483647 | 2147483647 | | | | utf8 | utf8_bin | | | select,insert | | String | string | FIELD | | No | string | | | | greptime | information_schema | triggers | trigger_schema | 2 | 2147483647 | 2147483647 | | | | utf8 | utf8_bin | | | select,insert | | String | string | FIELD | | No | string | | | +| greptime | information_schema | views | character_set_client | 9 | 2147483647 | 2147483647 | | | | utf8 | utf8_bin | | | select,insert | | String | string | FIELD | | Yes | string | | | +| greptime | information_schema | views | check_option | 5 | 2147483647 | 2147483647 | | | | utf8 | utf8_bin | | | select,insert | | String | string | FIELD | | Yes | string | | | +| greptime | information_schema | views | collation_connection | 10 | 2147483647 | 2147483647 | | | | utf8 | utf8_bin | | | select,insert | | String | string | FIELD | | Yes | string | | | +| greptime | information_schema | views | definer | 7 | 2147483647 | 2147483647 | | | | utf8 | utf8_bin | | | select,insert | | String | string | FIELD | | Yes | string | | | +| greptime | information_schema | views | is_updatable | 6 | | | | | | | | | | select,insert | | Boolean | boolean | FIELD | | Yes | boolean | | | +| greptime | information_schema | views | security_type | 8 | 2147483647 | 2147483647 | | | | utf8 | utf8_bin | | | select,insert | | String | string | FIELD | | Yes | string | | | +| greptime | information_schema | views | table_catalog | 1 | 2147483647 | 2147483647 | | | | utf8 | utf8_bin | | | select,insert | | String | string | FIELD | | No | string | | | +| greptime | information_schema | views | table_name | 3 | 2147483647 | 2147483647 | | | | utf8 | utf8_bin | | | select,insert | | String | string | FIELD | | No | string | | | +| greptime | information_schema | views | table_schema | 2 | 2147483647 | 2147483647 | | | | utf8 | utf8_bin | | | select,insert | | String | string | FIELD | | No | string | | | +| greptime | information_schema | views | view_definition | 4 | 2147483647 | 2147483647 | | | | utf8 | utf8_bin | | | select,insert | | String | string | FIELD | | No | string | | | | greptime | public | numbers | number | 1 | | | 10 | 0 | | | | PRI | | select,insert | | UInt32 | int unsigned | TAG | | No | int unsigned | | | +---------------+--------------------+---------------------------------------+-----------------------------------+------------------+--------------------------+------------------------+-------------------+---------------+--------------------+--------------------+----------------+------------+-------+---------------+-----------------------+----------------------+-----------------+---------------+----------------+-------------+-----------------+----------------+--------+ diff --git a/tests/cases/standalone/common/view/create.result b/tests/cases/standalone/common/view/create.result index c1ef01117a46..55902a2a5c42 100644 --- a/tests/cases/standalone/common/view/create.result +++ b/tests/cases/standalone/common/view/create.result @@ -102,6 +102,7 @@ SELECT * FROM INFORMATION_SCHEMA.TABLES ORDER BY TABLE_NAME, TABLE_TYPE; | greptime | public | test_table | BASE TABLE |ID |ID |ID |ID |ID |ID | mito |ID | Fixed |ID |ID |ID | DATETIME | | | |ID | | | N | | greptime | public | test_view | VIEW |ID |ID |ID |ID |ID |ID | |ID | Fixed |ID |ID |ID | 1970-01-01T00:00:00 | | | |ID | | | N | | greptime | information_schema | triggers | LOCAL TEMPORARY |ID |ID |ID |ID |ID |ID | |ID | Fixed |ID |ID |ID | DATETIME | | | |ID | | | Y | +| greptime | information_schema | views | LOCAL TEMPORARY |ID |ID |ID |ID |ID |ID | |ID | Fixed |ID |ID |ID | DATETIME | | | |ID | | | Y | +---------------+--------------------+---------------------------------------+-----------------+----------+-------------+-----------------+--------------+------------------+----------------+-------------+---------+------------+------------+-----------+----------------+-------------------------+-------------+------------+-----------------+----------+----------------+---------------+-----------+ -- SQLNESS REPLACE (\s\d+\s) ID