1use std::collections::{BTreeMap, HashMap};
28use std::sync::{Arc, LazyLock};
29
30use arrow_arith::boolean::or;
31use arrow_array::array::{Array, UInt32Array, UnionArray};
32use arrow_array::builder::{
33 ArrayBuilder, BooleanBuilder, Int8Builder, Int32Builder, Int64Builder, ListBuilder, MapBuilder,
34 StringBuilder, UInt32Builder,
35};
36use arrow_array::{RecordBatch, Scalar};
37use arrow_data::ArrayData;
38use arrow_ord::cmp::eq;
39use arrow_schema::{DataType, Field, Fields, Schema, SchemaRef, UnionFields, UnionMode};
40use arrow_select::filter::filter_record_batch;
41
42use crate::error::Result;
43use crate::sql::{CommandGetSqlInfo, SqlInfo};
44
45#[derive(Debug, Clone, PartialEq)]
47pub enum SqlInfoValue {
48 String(String),
49 Bool(bool),
50 BigInt(i64),
51 Bitmask(i32),
52 StringList(Vec<String>),
53 ListMap(BTreeMap<i32, Vec<i32>>),
54}
55
56impl From<&str> for SqlInfoValue {
57 fn from(value: &str) -> Self {
58 Self::String(value.to_string())
59 }
60}
61
62impl From<bool> for SqlInfoValue {
63 fn from(value: bool) -> Self {
64 Self::Bool(value)
65 }
66}
67
68impl From<i32> for SqlInfoValue {
69 fn from(value: i32) -> Self {
70 Self::Bitmask(value)
71 }
72}
73
74impl From<i64> for SqlInfoValue {
75 fn from(value: i64) -> Self {
76 Self::BigInt(value)
77 }
78}
79
80impl From<&[&str]> for SqlInfoValue {
81 fn from(values: &[&str]) -> Self {
82 let values = values.iter().map(|s| s.to_string()).collect();
83 Self::StringList(values)
84 }
85}
86
87impl From<Vec<String>> for SqlInfoValue {
88 fn from(values: Vec<String>) -> Self {
89 Self::StringList(values)
90 }
91}
92
93impl From<BTreeMap<i32, Vec<i32>>> for SqlInfoValue {
94 fn from(value: BTreeMap<i32, Vec<i32>>) -> Self {
95 Self::ListMap(value)
96 }
97}
98
99impl From<HashMap<i32, Vec<i32>>> for SqlInfoValue {
100 fn from(value: HashMap<i32, Vec<i32>>) -> Self {
101 Self::ListMap(value.into_iter().collect())
102 }
103}
104
105impl From<&HashMap<i32, Vec<i32>>> for SqlInfoValue {
106 fn from(value: &HashMap<i32, Vec<i32>>) -> Self {
107 Self::ListMap(
108 value
109 .iter()
110 .map(|(k, v)| (k.to_owned(), v.to_owned()))
111 .collect(),
112 )
113 }
114}
115
116pub trait SqlInfoName {
118 fn as_u32(&self) -> u32;
119}
120
121impl SqlInfoName for SqlInfo {
122 fn as_u32(&self) -> u32 {
123 u32::try_from(i32::from(*self)).expect("SqlInfo fit into u32")
126 }
127}
128
129impl SqlInfoName for u32 {
131 fn as_u32(&self) -> u32 {
132 *self
133 }
134}
135
136struct SqlInfoUnionBuilder {
153 string_values: StringBuilder,
155 bool_values: BooleanBuilder,
156 bigint_values: Int64Builder,
157 int32_bitmask_values: Int32Builder,
158 string_list_values: ListBuilder<StringBuilder>,
159 int32_to_int32_list_map_values: MapBuilder<Int32Builder, ListBuilder<Int32Builder>>,
160 type_ids: Int8Builder,
161 offsets: Int32Builder,
162}
163
164static UNION_TYPE: LazyLock<DataType> = LazyLock::new(|| {
166 let fields = vec![
167 Field::new("string_value", DataType::Utf8, false),
168 Field::new("bool_value", DataType::Boolean, false),
169 Field::new("bigint_value", DataType::Int64, false),
170 Field::new("int32_bitmask", DataType::Int32, false),
171 Field::new(
173 "string_list",
174 DataType::List(Arc::new(Field::new_list_field(DataType::Utf8, true))),
175 true,
176 ),
177 Field::new(
178 "int32_to_int32_list_map",
179 DataType::Map(
180 Arc::new(Field::new(
181 Field::MAP_ENTRIES_FIELD_DEFAULT_NAME,
182 DataType::Struct(Fields::from(vec![
183 Field::new(Field::MAP_KEY_FIELD_DEFAULT_NAME, DataType::Int32, false),
184 Field::new(
185 Field::MAP_VALUE_FIELD_DEFAULT_NAME,
186 DataType::List(Arc::new(Field::new_list_field(DataType::Int32, true))),
187 true,
188 ),
189 ])),
190 false,
191 )),
192 false,
193 ),
194 true,
195 ),
196 ];
197
198 DataType::Union(UnionFields::from_fields(fields), UnionMode::Dense)
199});
200
201impl SqlInfoUnionBuilder {
202 pub fn new() -> Self {
203 Self {
204 string_values: StringBuilder::new(),
205 bool_values: BooleanBuilder::new(),
206 bigint_values: Int64Builder::new(),
207 int32_bitmask_values: Int32Builder::new(),
208 string_list_values: ListBuilder::new(StringBuilder::new()),
209 int32_to_int32_list_map_values: MapBuilder::new(
210 None,
211 Int32Builder::new(),
212 ListBuilder::new(Int32Builder::new()),
213 ),
214 type_ids: Int8Builder::new(),
215 offsets: Int32Builder::new(),
216 }
217 }
218
219 pub fn schema() -> &'static DataType {
221 &UNION_TYPE
222 }
223
224 pub fn append_value(&mut self, v: &SqlInfoValue) -> Result<()> {
226 let (type_id, len) = match v {
229 SqlInfoValue::String(v) => {
230 self.string_values.append_value(v);
231 (0, self.string_values.len())
232 }
233 SqlInfoValue::Bool(v) => {
234 self.bool_values.append_value(*v);
235 (1, self.bool_values.len())
236 }
237 SqlInfoValue::BigInt(v) => {
238 self.bigint_values.append_value(*v);
239 (2, self.bigint_values.len())
240 }
241 SqlInfoValue::Bitmask(v) => {
242 self.int32_bitmask_values.append_value(*v);
243 (3, self.int32_bitmask_values.len())
244 }
245 SqlInfoValue::StringList(values) => {
246 for v in values {
248 self.string_list_values.values().append_value(v);
249 }
250 self.string_list_values.append(true);
252 (4, self.string_list_values.len())
253 }
254 SqlInfoValue::ListMap(values) => {
255 for (k, v) in values.clone() {
257 self.int32_to_int32_list_map_values.keys().append_value(k);
258 self.int32_to_int32_list_map_values
259 .values()
260 .append_value(v.into_iter().map(Some));
261 }
262 self.int32_to_int32_list_map_values.append(true)?;
264 (5, self.int32_to_int32_list_map_values.len())
265 }
266 };
267
268 self.type_ids.append_value(type_id);
269 let len = i32::try_from(len).expect("offset fit in i32");
270 self.offsets.append_value(len - 1);
271 Ok(())
272 }
273
274 pub fn finish(self) -> UnionArray {
276 let Self {
277 mut string_values,
278 mut bool_values,
279 mut bigint_values,
280 mut int32_bitmask_values,
281 mut string_list_values,
282 mut int32_to_int32_list_map_values,
283 mut type_ids,
284 mut offsets,
285 } = self;
286 let type_ids = type_ids.finish();
287 let offsets = offsets.finish();
288
289 let len = offsets.len();
292 let null_bit_buffer = None;
293 let offset = 0;
294
295 let buffers = vec![
296 type_ids.into_data().buffers()[0].clone(),
297 offsets.into_data().buffers()[0].clone(),
298 ];
299
300 let child_data = vec![
301 string_values.finish().into_data(),
302 bool_values.finish().into_data(),
303 bigint_values.finish().into_data(),
304 int32_bitmask_values.finish().into_data(),
305 string_list_values.finish().into_data(),
306 int32_to_int32_list_map_values.finish().into_data(),
307 ];
308
309 let data = ArrayData::try_new(
310 UNION_TYPE.clone(),
311 len,
312 null_bit_buffer,
313 offset,
314 buffers,
315 child_data,
316 )
317 .expect("Correctly created UnionArray");
318
319 UnionArray::from(data)
320 }
321}
322
323#[derive(Debug, Clone, PartialEq, Default)]
331pub struct SqlInfoDataBuilder {
332 infos: BTreeMap<u32, SqlInfoValue>,
338}
339
340impl SqlInfoDataBuilder {
341 pub fn new() -> Self {
343 Self::default()
344 }
345
346 pub fn append(&mut self, name: impl SqlInfoName, value: impl Into<SqlInfoValue>) {
348 self.infos.insert(name.as_u32(), value.into());
349 }
350
351 pub fn build(self) -> Result<SqlInfoData> {
355 let mut name_builder = UInt32Builder::new();
356 let mut value_builder = SqlInfoUnionBuilder::new();
357
358 for (name, value) in &self.infos {
360 name_builder.append_value(*name);
361 value_builder.append_value(value)?
362 }
363
364 let batch = RecordBatch::try_from_iter(vec![
365 ("info_name", Arc::new(name_builder.finish()) as _),
366 ("value", Arc::new(value_builder.finish()) as _),
367 ])?;
368
369 Ok(SqlInfoData { batch })
370 }
371
372 pub fn schema() -> &'static Schema {
374 &SQL_INFO_SCHEMA
376 }
377}
378
379pub struct SqlInfoData {
399 batch: RecordBatch,
400}
401
402impl SqlInfoData {
403 pub fn record_batch(&self, info: impl IntoIterator<Item = u32>) -> Result<RecordBatch> {
406 let arr = self.batch.column(0);
407 let type_filter = info
408 .into_iter()
409 .map(|tt| {
410 let s = UInt32Array::from(vec![tt]);
411 eq(arr, &Scalar::new(&s))
412 })
413 .collect::<std::result::Result<Vec<_>, _>>()?
414 .into_iter()
415 .reduce(|filter, arr| or(&filter, &arr).unwrap());
417 if let Some(filter) = type_filter {
418 Ok(filter_record_batch(&self.batch, &filter)?)
419 } else {
420 Ok(self.batch.clone())
421 }
422 }
423
424 pub fn schema(&self) -> SchemaRef {
427 self.batch.schema()
428 }
429}
430
431pub struct GetSqlInfoBuilder<'a> {
433 info: Vec<u32>,
435 infos: &'a SqlInfoData,
436}
437
438impl CommandGetSqlInfo {
439 pub fn into_builder(self, infos: &SqlInfoData) -> GetSqlInfoBuilder<'_> {
441 GetSqlInfoBuilder {
442 info: self.info,
443 infos,
444 }
445 }
446}
447
448impl GetSqlInfoBuilder<'_> {
449 pub fn build(self) -> Result<RecordBatch> {
451 self.infos.record_batch(self.info)
452 }
453
454 pub fn schema(&self) -> SchemaRef {
457 self.infos.schema()
458 }
459}
460
461static SQL_INFO_SCHEMA: LazyLock<Schema> = LazyLock::new(|| {
463 Schema::new(vec![
464 Field::new("info_name", DataType::UInt32, false),
465 Field::new("value", SqlInfoUnionBuilder::schema().clone(), false),
466 ])
467});
468
469#[cfg(test)]
470mod tests {
471 use std::collections::HashMap;
472
473 use super::SqlInfoDataBuilder;
474 use crate::sql::metadata::tests::assert_batches_eq;
475 use crate::sql::{SqlInfo, SqlNullOrdering, SqlSupportedTransaction, SqlSupportsConvert};
476
477 #[test]
478 fn test_sql_infos() {
479 let mut convert: HashMap<i32, Vec<i32>> = HashMap::new();
480 convert.insert(
481 SqlSupportsConvert::SqlConvertInteger as i32,
482 vec![
483 SqlSupportsConvert::SqlConvertFloat as i32,
484 SqlSupportsConvert::SqlConvertReal as i32,
485 ],
486 );
487
488 let mut builder = SqlInfoDataBuilder::new();
489 builder.append(SqlInfo::SqlIdentifierQuoteChar, r#"""#);
491 builder.append(SqlInfo::SqlDdlCatalog, false);
493 builder.append(
495 SqlInfo::SqlNullOrdering,
496 SqlNullOrdering::SqlNullsSortedHigh as i32,
497 );
498 builder.append(SqlInfo::SqlMaxBinaryLiteralLength, i32::MAX as i64);
500 builder.append(SqlInfo::SqlKeywords, &["SELECT", "DELETE"] as &[&str]);
502 builder.append(SqlInfo::SqlSupportsConvert, &convert);
503
504 let batch = builder.build().unwrap().record_batch(None).unwrap();
505
506 let expected = vec![
507 "+-----------+----------------------------------------+",
508 "| info_name | value |",
509 "+-----------+----------------------------------------+",
510 "| 500 | {bool_value=false} |",
511 "| 504 | {string_value=\"} |",
512 "| 507 | {int32_bitmask=0} |",
513 "| 508 | {string_list=[SELECT, DELETE]} |",
514 "| 517 | {int32_to_int32_list_map={7: [6, 13]}} |",
515 "| 541 | {bigint_value=2147483647} |",
516 "+-----------+----------------------------------------+",
517 ];
518
519 assert_batches_eq(&[batch], &expected);
520 }
521
522 #[test]
523 fn test_filter_sql_infos() {
524 let mut builder = SqlInfoDataBuilder::new();
525 builder.append(SqlInfo::FlightSqlServerName, "server name");
526 builder.append(
527 SqlInfo::FlightSqlServerTransaction,
528 SqlSupportedTransaction::Transaction as i32,
529 );
530 let data = builder.build().unwrap();
531
532 let batch = data.record_batch(None).unwrap();
533 assert_eq!(batch.num_rows(), 2);
534
535 let batch = data
536 .record_batch([SqlInfo::FlightSqlServerTransaction as u32])
537 .unwrap();
538 let mut ref_builder = SqlInfoDataBuilder::new();
539 ref_builder.append(
540 SqlInfo::FlightSqlServerTransaction,
541 SqlSupportedTransaction::Transaction as i32,
542 );
543 let ref_batch = ref_builder.build().unwrap().record_batch(None).unwrap();
544
545 assert_eq!(batch, ref_batch);
546 }
547}