@@ -43,6 +43,7 @@ use datafusion_expr::{TableType, Volatility};
4343use datafusion_physical_plan:: SendableRecordBatchStream ;
4444use datafusion_physical_plan:: stream:: RecordBatchStreamAdapter ;
4545use datafusion_physical_plan:: streaming:: PartitionStream ;
46+ use futures:: StreamExt ;
4647use std:: collections:: { BTreeSet , HashMap , HashSet } ;
4748use std:: fmt:: Debug ;
4849use std:: sync:: Arc ;
@@ -76,6 +77,7 @@ pub const INFORMATION_SCHEMA_TABLES: &[&str] = &[
7677#[ derive( Debug ) ]
7778pub struct InformationSchemaProvider {
7879 config : InformationSchemaConfig ,
80+ uppercase_identifiers : bool ,
7981}
8082
8183impl InformationSchemaProvider {
@@ -85,10 +87,19 @@ impl InformationSchemaProvider {
8587 config : InformationSchemaConfig {
8688 catalog_list,
8789 table_functions : HashMap :: new ( ) ,
90+ uppercase_identifiers : false ,
8891 } ,
92+ uppercase_identifiers : false ,
8993 }
9094 }
9195
96+ /// Expose virtual schema table and column names in uppercase.
97+ pub fn with_uppercase_identifiers ( mut self , uppercase_identifiers : bool ) -> Self {
98+ self . uppercase_identifiers = uppercase_identifiers;
99+ self . config . uppercase_identifiers = uppercase_identifiers;
100+ self
101+ }
102+
92103 /// Attach the session's table (UDTF) functions so that they appear in
93104 /// `information_schema.routines` / `SHOW FUNCTIONS`.
94105 pub fn with_table_functions (
@@ -104,9 +115,18 @@ impl InformationSchemaProvider {
104115struct InformationSchemaConfig {
105116 catalog_list : Arc < dyn CatalogProviderList > ,
106117 table_functions : HashMap < String , Arc < TableFunction > > ,
118+ uppercase_identifiers : bool ,
107119}
108120
109121impl InformationSchemaConfig {
122+ fn virtual_schema_name ( & self ) -> & ' static str {
123+ if self . uppercase_identifiers {
124+ "INFORMATION_SCHEMA"
125+ } else {
126+ INFORMATION_SCHEMA
127+ }
128+ }
129+
110130 /// Construct the `information_schema.tables` virtual table
111131 async fn make_tables (
112132 & self ,
@@ -118,7 +138,7 @@ impl InformationSchemaConfig {
118138 let catalog = self . catalog_list . catalog ( & catalog_name) . unwrap ( ) ;
119139
120140 for schema_name in catalog. schema_names ( ) {
121- if schema_name != INFORMATION_SCHEMA {
141+ if schema_name != self . virtual_schema_name ( ) {
122142 // schema name may not exist in the catalog, so we need to check
123143 if let Some ( schema) = catalog. schema ( & schema_name) {
124144 for table_name in schema. table_names ( ) {
@@ -139,12 +159,21 @@ impl InformationSchemaConfig {
139159
140160 // Add a final list for the information schema tables themselves
141161 for table_name in INFORMATION_SCHEMA_TABLES {
142- builder. add_table (
143- & catalog_name,
144- INFORMATION_SCHEMA ,
145- table_name,
146- TableType :: View ,
147- ) ;
162+ if self . uppercase_identifiers {
163+ builder. add_table (
164+ & catalog_name,
165+ self . virtual_schema_name ( ) ,
166+ table_name. to_ascii_uppercase ( ) ,
167+ TableType :: View ,
168+ ) ;
169+ } else {
170+ builder. add_table (
171+ & catalog_name,
172+ INFORMATION_SCHEMA ,
173+ table_name,
174+ TableType :: View ,
175+ ) ;
176+ }
148177 }
149178 }
150179
@@ -156,7 +185,7 @@ impl InformationSchemaConfig {
156185 let catalog = self . catalog_list . catalog ( & catalog_name) . unwrap ( ) ;
157186
158187 for schema_name in catalog. schema_names ( ) {
159- if schema_name != INFORMATION_SCHEMA
188+ if schema_name != self . virtual_schema_name ( )
160189 && let Some ( schema) = catalog. schema ( & schema_name)
161190 {
162191 let schema_owner = schema. owner_name ( ) ;
@@ -174,7 +203,7 @@ impl InformationSchemaConfig {
174203 let catalog = self . catalog_list . catalog ( & catalog_name) . unwrap ( ) ;
175204
176205 for schema_name in catalog. schema_names ( ) {
177- if schema_name != INFORMATION_SCHEMA {
206+ if schema_name != self . virtual_schema_name ( ) {
178207 // schema name may not exist in the catalog, so we need to check
179208 if let Some ( schema) = catalog. schema ( & schema_name) {
180209 for table_name in schema. table_names ( ) {
@@ -204,7 +233,7 @@ impl InformationSchemaConfig {
204233 let catalog = self . catalog_list . catalog ( & catalog_name) . unwrap ( ) ;
205234
206235 for schema_name in catalog. schema_names ( ) {
207- if schema_name != INFORMATION_SCHEMA {
236+ if schema_name != self . virtual_schema_name ( ) {
208237 // schema name may not exist in the catalog, so we need to check
209238 if let Some ( schema) = catalog. schema ( & schema_name) {
210239 for table_name in schema. table_names ( ) {
@@ -616,14 +645,25 @@ impl SchemaProvider for InformationSchemaProvider {
616645 fn table_names ( & self ) -> Vec < String > {
617646 INFORMATION_SCHEMA_TABLES
618647 . iter ( )
619- . map ( |t| ( * t) . to_string ( ) )
648+ . map ( |name| {
649+ if self . uppercase_identifiers {
650+ name. to_ascii_uppercase ( )
651+ } else {
652+ ( * name) . to_string ( )
653+ }
654+ } )
620655 . collect ( )
621656 }
622657
623658 async fn table (
624659 & self ,
625660 name : & str ,
626661 ) -> Result < Option < Arc < dyn TableProvider > > , DataFusionError > {
662+ if self . uppercase_identifiers
663+ && name. bytes ( ) . any ( |byte| byte. is_ascii_lowercase ( ) )
664+ {
665+ return Ok ( None ) ;
666+ }
627667 let config = self . config . clone ( ) ;
628668 let table: Arc < dyn PartitionStream > = match name. to_ascii_lowercase ( ) . as_str ( ) {
629669 TABLES => Arc :: new ( InformationSchemaTables :: new ( config) ) ,
@@ -636,13 +676,70 @@ impl SchemaProvider for InformationSchemaProvider {
636676 _ => return Ok ( None ) ,
637677 } ;
638678
679+ let table: Arc < dyn PartitionStream > = if self . uppercase_identifiers {
680+ Arc :: new ( UppercaseColumnsPartitionStream :: new ( table) )
681+ } else {
682+ table
683+ } ;
684+
639685 Ok ( Some ( Arc :: new (
640686 StreamingTable :: try_new ( Arc :: clone ( table. schema ( ) ) , vec ! [ table] ) . unwrap ( ) ,
641687 ) ) )
642688 }
643689
644690 fn table_exist ( & self , name : & str ) -> bool {
645- INFORMATION_SCHEMA_TABLES . contains ( & name. to_ascii_lowercase ( ) . as_str ( ) )
691+ ( !self . uppercase_identifiers
692+ || !name. bytes ( ) . any ( |byte| byte. is_ascii_lowercase ( ) ) )
693+ && INFORMATION_SCHEMA_TABLES . contains ( & name. to_ascii_lowercase ( ) . as_str ( ) )
694+ }
695+ }
696+
697+ #[ derive( Debug ) ]
698+ struct UppercaseColumnsPartitionStream {
699+ inner : Arc < dyn PartitionStream > ,
700+ schema : SchemaRef ,
701+ }
702+
703+ impl UppercaseColumnsPartitionStream {
704+ fn new ( inner : Arc < dyn PartitionStream > ) -> Self {
705+ let original = inner. schema ( ) ;
706+ let fields: Vec < FieldRef > = original
707+ . fields ( )
708+ . iter ( )
709+ . map ( |field| {
710+ Arc :: new (
711+ field
712+ . as_ref ( )
713+ . clone ( )
714+ . with_name ( field. name ( ) . to_ascii_uppercase ( ) ) ,
715+ )
716+ } )
717+ . collect ( ) ;
718+ let schema = Arc :: new ( Schema :: new_with_metadata (
719+ fields,
720+ original. metadata ( ) . clone ( ) ,
721+ ) ) ;
722+ Self { inner, schema }
723+ }
724+ }
725+
726+ impl PartitionStream for UppercaseColumnsPartitionStream {
727+ fn schema ( & self ) -> & SchemaRef {
728+ & self . schema
729+ }
730+
731+ fn execute ( & self , ctx : Arc < TaskContext > ) -> SendableRecordBatchStream {
732+ let schema = Arc :: clone ( & self . schema ) ;
733+ let batches = self . inner . execute ( ctx) . map ( {
734+ let schema = Arc :: clone ( & schema) ;
735+ move |batch| {
736+ batch. and_then ( |batch| {
737+ RecordBatch :: try_new ( Arc :: clone ( & schema) , batch. columns ( ) . to_vec ( ) )
738+ . map_err ( Into :: into)
739+ } )
740+ }
741+ } ) ;
742+ Box :: pin ( RecordBatchStreamAdapter :: new ( schema, batches) )
646743 }
647744}
648745
0 commit comments