arrow_flight/sql/metadata/
db_schemas.rs1use std::sync::{Arc, LazyLock};
10
11use arrow_arith::boolean::and;
12use arrow_array::{ArrayRef, RecordBatch, StringArray, builder::StringBuilder};
13use arrow_ord::cmp::eq;
14use arrow_schema::{DataType, Field, Schema, SchemaRef};
15use arrow_select::{filter::filter_record_batch, take::take};
16use arrow_string::like::like;
17
18use super::lexsort_to_indices;
19use crate::error::*;
20use crate::sql::CommandGetDbSchemas;
21
22pub struct GetDbSchemasBuilder {
29 catalog_filter: Option<String>,
33 db_schema_filter_pattern: Option<String>,
35 catalog_name: StringBuilder,
37 db_schema_name: StringBuilder,
39}
40
41impl CommandGetDbSchemas {
42 pub fn into_builder(self) -> GetDbSchemasBuilder {
44 self.into()
45 }
46}
47
48impl From<CommandGetDbSchemas> for GetDbSchemasBuilder {
49 fn from(value: CommandGetDbSchemas) -> Self {
50 Self::new(value.catalog, value.db_schema_filter_pattern)
51 }
52}
53
54impl GetDbSchemasBuilder {
55 pub fn new(
70 catalog: Option<impl Into<String>>,
71 db_schema_filter_pattern: Option<impl Into<String>>,
72 ) -> Self {
73 Self {
74 catalog_filter: catalog.map(|v| v.into()),
75 db_schema_filter_pattern: db_schema_filter_pattern.map(|v| v.into()),
76 catalog_name: StringBuilder::new(),
77 db_schema_name: StringBuilder::new(),
78 }
79 }
80
81 pub fn append(&mut self, catalog_name: impl AsRef<str>, schema_name: impl AsRef<str>) {
85 self.catalog_name.append_value(catalog_name);
86 self.db_schema_name.append_value(schema_name);
87 }
88
89 pub fn build(self) -> Result<RecordBatch> {
91 let schema = self.schema();
92 let Self {
93 catalog_filter,
94 db_schema_filter_pattern,
95 mut catalog_name,
96 mut db_schema_name,
97 } = self;
98
99 let catalog_name = catalog_name.finish();
101 let db_schema_name = db_schema_name.finish();
102
103 let mut filters = vec![];
104
105 if let Some(db_schema_filter_pattern) = db_schema_filter_pattern {
106 let scalar = StringArray::new_scalar(db_schema_filter_pattern);
108 filters.push(like(&db_schema_name, &scalar)?)
109 }
110
111 if let Some(catalog_filter_name) = catalog_filter {
112 let scalar = StringArray::new_scalar(catalog_filter_name);
113 filters.push(eq(&catalog_name, &scalar)?);
114 }
115
116 let mut total_filter = None;
118 while let Some(filter) = filters.pop() {
119 let new_filter = match total_filter {
120 Some(total_filter) => and(&total_filter, &filter)?,
121 None => filter,
122 };
123 total_filter = Some(new_filter);
124 }
125
126 let batch = RecordBatch::try_new(
127 schema,
128 vec![
129 Arc::new(catalog_name) as ArrayRef,
130 Arc::new(db_schema_name) as ArrayRef,
131 ],
132 )?;
133
134 let filtered_batch = if let Some(filter) = total_filter {
136 filter_record_batch(&batch, &filter)?
137 } else {
138 batch
139 };
140
141 let indices = lexsort_to_indices(filtered_batch.columns());
143 let columns = filtered_batch
144 .columns()
145 .iter()
146 .map(|c| take(c, &indices, None))
147 .collect::<std::result::Result<Vec<_>, _>>()?;
148
149 Ok(RecordBatch::try_new(filtered_batch.schema(), columns)?)
150 }
151
152 pub fn schema(&self) -> SchemaRef {
155 get_db_schemas_schema()
156 }
157}
158
159fn get_db_schemas_schema() -> SchemaRef {
160 Arc::clone(&GET_DB_SCHEMAS_SCHEMA)
161}
162
163static GET_DB_SCHEMAS_SCHEMA: LazyLock<SchemaRef> = LazyLock::new(|| {
165 Arc::new(Schema::new(vec![
166 Field::new("catalog_name", DataType::Utf8, true),
167 Field::new("db_schema_name", DataType::Utf8, false),
168 ]))
169});
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174 use arrow_array::{StringArray, UInt32Array};
175
176 fn get_ref_batch() -> RecordBatch {
177 RecordBatch::try_new(
178 get_db_schemas_schema(),
179 vec![
180 Arc::new(StringArray::from(vec![
181 "a_catalog",
182 "a_catalog",
183 "b_catalog",
184 "b_catalog",
185 ])) as ArrayRef,
186 Arc::new(StringArray::from(vec![
187 "a_schema", "b_schema", "a_schema", "b_schema",
188 ])) as ArrayRef,
189 ],
190 )
191 .unwrap()
192 }
193
194 #[test]
195 fn test_schemas_are_filtered() {
196 let ref_batch = get_ref_batch();
197
198 let mut builder = GetDbSchemasBuilder::new(None::<String>, None::<String>);
199 builder.append("a_catalog", "a_schema");
200 builder.append("a_catalog", "b_schema");
201 builder.append("b_catalog", "a_schema");
202 builder.append("b_catalog", "b_schema");
203 let schema_batch = builder.build().unwrap();
204
205 assert_eq!(schema_batch, ref_batch);
206
207 let mut builder = GetDbSchemasBuilder::new(None::<String>, Some("a%"));
208 builder.append("a_catalog", "a_schema");
209 builder.append("a_catalog", "b_schema");
210 builder.append("b_catalog", "a_schema");
211 builder.append("b_catalog", "b_schema");
212 let schema_batch = builder.build().unwrap();
213
214 let indices = UInt32Array::from(vec![0, 2]);
215 let ref_filtered = RecordBatch::try_new(
216 get_db_schemas_schema(),
217 ref_batch
218 .columns()
219 .iter()
220 .map(|c| take(c, &indices, None))
221 .collect::<std::result::Result<Vec<_>, _>>()
222 .unwrap(),
223 )
224 .unwrap();
225
226 assert_eq!(schema_batch, ref_filtered);
227 }
228
229 #[test]
230 fn test_schemas_are_sorted() {
231 let ref_batch = get_ref_batch();
232
233 let mut builder = GetDbSchemasBuilder::new(None::<String>, None::<String>);
234 builder.append("a_catalog", "b_schema");
235 builder.append("b_catalog", "a_schema");
236 builder.append("a_catalog", "a_schema");
237 builder.append("b_catalog", "b_schema");
238 let schema_batch = builder.build().unwrap();
239
240 assert_eq!(schema_batch, ref_batch)
241 }
242
243 #[test]
244 fn test_builder_from_query() {
245 let ref_batch = get_ref_batch();
246 let query = CommandGetDbSchemas {
247 catalog: Some("a_catalog".into()),
248 db_schema_filter_pattern: Some("b%".into()),
249 };
250
251 let mut builder = query.into_builder();
252 builder.append("a_catalog", "a_schema");
253 builder.append("a_catalog", "b_schema");
254 builder.append("b_catalog", "a_schema");
255 builder.append("b_catalog", "b_schema");
256 let schema_batch = builder.build().unwrap();
257
258 let indices = UInt32Array::from(vec![1]);
259 let ref_filtered = RecordBatch::try_new(
260 get_db_schemas_schema(),
261 ref_batch
262 .columns()
263 .iter()
264 .map(|c| take(c, &indices, None))
265 .collect::<std::result::Result<Vec<_>, _>>()
266 .unwrap(),
267 )
268 .unwrap();
269
270 assert_eq!(schema_batch, ref_filtered);
271 }
272}