1use std::collections::{BTreeMap, HashMap};
15use std::sync::{Arc, LazyLock};
16
17use arrow_arith::boolean::or;
18use arrow_array::array::{Array, UInt32Array, UnionArray};
19use arrow_array::builder::{
20 ArrayBuilder, BooleanBuilder, Int8Builder, Int32Builder, Int64Builder, ListBuilder, MapBuilder,
21 StringBuilder, UInt32Builder,
22};
23use arrow_array::{RecordBatch, Scalar};
24use arrow_data::ArrayData;
25use arrow_ord::cmp::eq;
26use arrow_schema::{DataType, Field, Fields, Schema, SchemaRef, UnionFields, UnionMode};
27use arrow_select::filter::filter_record_batch;
28
29use crate::error::Result;
30use crate::sql::{CommandGetSqlInfo, SqlInfo};
31
32#[derive(Debug, Clone, PartialEq)]
34pub enum SqlInfoValue {
35 String(String),
36 Bool(bool),
37 BigInt(i64),
38 Bitmask(i32),
39 StringList(Vec<String>),
40 ListMap(BTreeMap<i32, Vec<i32>>),
41}
42
43impl From<&str> for SqlInfoValue {
44 fn from(value: &str) -> Self {
45 Self::String(value.to_string())
46 }
47}
48
49impl From<bool> for SqlInfoValue {
50 fn from(value: bool) -> Self {
51 Self::Bool(value)
52 }
53}
54
55impl From<i32> for SqlInfoValue {
56 fn from(value: i32) -> Self {
57 Self::Bitmask(value)
58 }
59}
60
61impl From<i64> for SqlInfoValue {
62 fn from(value: i64) -> Self {
63 Self::BigInt(value)
64 }
65}
66
67impl From<&[&str]> for SqlInfoValue {
68 fn from(values: &[&str]) -> Self {
69 let values = values.iter().map(|s| s.to_string()).collect();
70 Self::StringList(values)
71 }
72}
73
74impl From<Vec<String>> for SqlInfoValue {
75 fn from(values: Vec<String>) -> Self {
76 Self::StringList(values)
77 }
78}
79
80impl From<BTreeMap<i32, Vec<i32>>> for SqlInfoValue {
81 fn from(value: BTreeMap<i32, Vec<i32>>) -> Self {
82 Self::ListMap(value)
83 }
84}
85
86impl From<HashMap<i32, Vec<i32>>> for SqlInfoValue {
87 fn from(value: HashMap<i32, Vec<i32>>) -> Self {
88 Self::ListMap(value.into_iter().collect())
89 }
90}
91
92impl From<&HashMap<i32, Vec<i32>>> for SqlInfoValue {
93 fn from(value: &HashMap<i32, Vec<i32>>) -> Self {
94 Self::ListMap(
95 value
96 .iter()
97 .map(|(k, v)| (k.to_owned(), v.to_owned()))
98 .collect(),
99 )
100 }
101}
102
103pub trait SqlInfoName {
105 fn as_u32(&self) -> u32;
106}
107
108impl SqlInfoName for SqlInfo {
109 fn as_u32(&self) -> u32 {
110 u32::try_from(i32::from(*self)).expect("SqlInfo fit into u32")
113 }
114}
115
116impl SqlInfoName for u32 {
118 fn as_u32(&self) -> u32 {
119 *self
120 }
121}
122
123struct SqlInfoUnionBuilder {
140 string_values: StringBuilder,
142 bool_values: BooleanBuilder,
143 bigint_values: Int64Builder,
144 int32_bitmask_values: Int32Builder,
145 string_list_values: ListBuilder<StringBuilder>,
146 int32_to_int32_list_map_values: MapBuilder<Int32Builder, ListBuilder<Int32Builder>>,
147 type_ids: Int8Builder,
148 offsets: Int32Builder,
149}
150
151static UNION_TYPE: LazyLock<DataType> = LazyLock::new(|| {
153 let fields = vec![
154 Field::new("string_value", DataType::Utf8, false),
155 Field::new("bool_value", DataType::Boolean, false),
156 Field::new("bigint_value", DataType::Int64, false),
157 Field::new("int32_bitmask", DataType::Int32, false),
158 Field::new(
160 "string_list",
161 DataType::List(Arc::new(Field::new_list_field(DataType::Utf8, true))),
162 true,
163 ),
164 Field::new(
165 "int32_to_int32_list_map",
166 DataType::Map(
167 Arc::new(Field::new(
168 Field::MAP_ENTRIES_FIELD_DEFAULT_NAME,
169 DataType::Struct(Fields::from(vec![
170 Field::new(Field::MAP_KEY_FIELD_DEFAULT_NAME, DataType::Int32, false),
171 Field::new(
172 Field::MAP_VALUE_FIELD_DEFAULT_NAME,
173 DataType::List(Arc::new(Field::new_list_field(DataType::Int32, true))),
174 true,
175 ),
176 ])),
177 false,
178 )),
179 false,
180 ),
181 true,
182 ),
183 ];
184
185 DataType::Union(UnionFields::from_fields(fields), UnionMode::Dense)
186});
187
188impl SqlInfoUnionBuilder {
189 pub fn new() -> Self {
190 Self {
191 string_values: StringBuilder::new(),
192 bool_values: BooleanBuilder::new(),
193 bigint_values: Int64Builder::new(),
194 int32_bitmask_values: Int32Builder::new(),
195 string_list_values: ListBuilder::new(StringBuilder::new()),
196 int32_to_int32_list_map_values: MapBuilder::new(
197 None,
198 Int32Builder::new(),
199 ListBuilder::new(Int32Builder::new()),
200 ),
201 type_ids: Int8Builder::new(),
202 offsets: Int32Builder::new(),
203 }
204 }
205
206 pub fn schema() -> &'static DataType {
208 &UNION_TYPE
209 }
210
211 pub fn append_value(&mut self, v: &SqlInfoValue) -> Result<()> {
213 let (type_id, len) = match v {
216 SqlInfoValue::String(v) => {
217 self.string_values.append_value(v);
218 (0, self.string_values.len())
219 }
220 SqlInfoValue::Bool(v) => {
221 self.bool_values.append_value(*v);
222 (1, self.bool_values.len())
223 }
224 SqlInfoValue::BigInt(v) => {
225 self.bigint_values.append_value(*v);
226 (2, self.bigint_values.len())
227 }
228 SqlInfoValue::Bitmask(v) => {
229 self.int32_bitmask_values.append_value(*v);
230 (3, self.int32_bitmask_values.len())
231 }
232 SqlInfoValue::StringList(values) => {
233 for v in values {
235 self.string_list_values.values().append_value(v);
236 }
237 self.string_list_values.append(true);
239 (4, self.string_list_values.len())
240 }
241 SqlInfoValue::ListMap(values) => {
242 for (k, v) in values.clone() {
244 self.int32_to_int32_list_map_values.keys().append_value(k);
245 self.int32_to_int32_list_map_values
246 .values()
247 .append_value(v.into_iter().map(Some));
248 }
249 self.int32_to_int32_list_map_values.append(true)?;
251 (5, self.int32_to_int32_list_map_values.len())
252 }
253 };
254
255 self.type_ids.append_value(type_id);
256 let len = i32::try_from(len).expect("offset fit in i32");
257 self.offsets.append_value(len - 1);
258 Ok(())
259 }
260
261 pub fn finish(self) -> UnionArray {
263 let Self {
264 mut string_values,
265 mut bool_values,
266 mut bigint_values,
267 mut int32_bitmask_values,
268 mut string_list_values,
269 mut int32_to_int32_list_map_values,
270 mut type_ids,
271 mut offsets,
272 } = self;
273 let type_ids = type_ids.finish();
274 let offsets = offsets.finish();
275
276 let len = offsets.len();
279 let null_bit_buffer = None;
280 let offset = 0;
281
282 let buffers = vec![
283 type_ids.into_data().buffers()[0].clone(),
284 offsets.into_data().buffers()[0].clone(),
285 ];
286
287 let child_data = vec![
288 string_values.finish().into_data(),
289 bool_values.finish().into_data(),
290 bigint_values.finish().into_data(),
291 int32_bitmask_values.finish().into_data(),
292 string_list_values.finish().into_data(),
293 int32_to_int32_list_map_values.finish().into_data(),
294 ];
295
296 let data = ArrayData::try_new(
297 UNION_TYPE.clone(),
298 len,
299 null_bit_buffer,
300 offset,
301 buffers,
302 child_data,
303 )
304 .expect("Correctly created UnionArray");
305
306 UnionArray::from(data)
307 }
308}
309
310#[derive(Debug, Clone, PartialEq, Default)]
318pub struct SqlInfoDataBuilder {
319 infos: BTreeMap<u32, SqlInfoValue>,
325}
326
327impl SqlInfoDataBuilder {
328 pub fn new() -> Self {
330 Self::default()
331 }
332
333 pub fn append(&mut self, name: impl SqlInfoName, value: impl Into<SqlInfoValue>) {
335 self.infos.insert(name.as_u32(), value.into());
336 }
337
338 pub fn build(self) -> Result<SqlInfoData> {
342 let mut name_builder = UInt32Builder::new();
343 let mut value_builder = SqlInfoUnionBuilder::new();
344
345 for (name, value) in &self.infos {
347 name_builder.append_value(*name);
348 value_builder.append_value(value)?
349 }
350
351 let batch = RecordBatch::try_from_iter(vec![
352 ("info_name", Arc::new(name_builder.finish()) as _),
353 ("value", Arc::new(value_builder.finish()) as _),
354 ])?;
355
356 Ok(SqlInfoData { batch })
357 }
358
359 pub fn schema() -> &'static Schema {
361 &SQL_INFO_SCHEMA
363 }
364}
365
366pub struct SqlInfoData {
386 batch: RecordBatch,
387}
388
389impl SqlInfoData {
390 pub fn record_batch(&self, info: impl IntoIterator<Item = u32>) -> Result<RecordBatch> {
393 let arr = self.batch.column(0);
394 let type_filter = info
395 .into_iter()
396 .map(|tt| {
397 let s = UInt32Array::from(vec![tt]);
398 eq(arr, &Scalar::new(&s))
399 })
400 .collect::<std::result::Result<Vec<_>, _>>()?
401 .into_iter()
402 .reduce(|filter, arr| or(&filter, &arr).unwrap());
404 if let Some(filter) = type_filter {
405 Ok(filter_record_batch(&self.batch, &filter)?)
406 } else {
407 Ok(self.batch.clone())
408 }
409 }
410
411 pub fn schema(&self) -> SchemaRef {
414 self.batch.schema()
415 }
416}
417
418pub struct GetSqlInfoBuilder<'a> {
420 info: Vec<u32>,
422 infos: &'a SqlInfoData,
423}
424
425impl CommandGetSqlInfo {
426 pub fn into_builder(self, infos: &SqlInfoData) -> GetSqlInfoBuilder<'_> {
428 GetSqlInfoBuilder {
429 info: self.info,
430 infos,
431 }
432 }
433}
434
435impl GetSqlInfoBuilder<'_> {
436 pub fn build(self) -> Result<RecordBatch> {
438 self.infos.record_batch(self.info)
439 }
440
441 pub fn schema(&self) -> SchemaRef {
444 self.infos.schema()
445 }
446}
447
448static SQL_INFO_SCHEMA: LazyLock<Schema> = LazyLock::new(|| {
450 Schema::new(vec![
451 Field::new("info_name", DataType::UInt32, false),
452 Field::new("value", SqlInfoUnionBuilder::schema().clone(), false),
453 ])
454});
455
456#[cfg(test)]
457mod tests {
458 use std::collections::HashMap;
459
460 use super::SqlInfoDataBuilder;
461 use crate::sql::metadata::tests::assert_batches_eq;
462 use crate::sql::{SqlInfo, SqlNullOrdering, SqlSupportedTransaction, SqlSupportsConvert};
463
464 #[test]
465 fn test_sql_infos() {
466 let mut convert: HashMap<i32, Vec<i32>> = HashMap::new();
467 convert.insert(
468 SqlSupportsConvert::SqlConvertInteger as i32,
469 vec![
470 SqlSupportsConvert::SqlConvertFloat as i32,
471 SqlSupportsConvert::SqlConvertReal as i32,
472 ],
473 );
474
475 let mut builder = SqlInfoDataBuilder::new();
476 builder.append(SqlInfo::SqlIdentifierQuoteChar, r#"""#);
478 builder.append(SqlInfo::SqlDdlCatalog, false);
480 builder.append(
482 SqlInfo::SqlNullOrdering,
483 SqlNullOrdering::SqlNullsSortedHigh as i32,
484 );
485 builder.append(SqlInfo::SqlMaxBinaryLiteralLength, i32::MAX as i64);
487 builder.append(SqlInfo::SqlKeywords, &["SELECT", "DELETE"] as &[&str]);
489 builder.append(SqlInfo::SqlSupportsConvert, &convert);
490
491 let batch = builder.build().unwrap().record_batch(None).unwrap();
492
493 let expected = vec![
494 "+-----------+----------------------------------------+",
495 "| info_name | value |",
496 "+-----------+----------------------------------------+",
497 "| 500 | {bool_value=false} |",
498 "| 504 | {string_value=\"} |",
499 "| 507 | {int32_bitmask=0} |",
500 "| 508 | {string_list=[SELECT, DELETE]} |",
501 "| 517 | {int32_to_int32_list_map={7: [6, 13]}} |",
502 "| 541 | {bigint_value=2147483647} |",
503 "+-----------+----------------------------------------+",
504 ];
505
506 assert_batches_eq(&[batch], &expected);
507 }
508
509 #[test]
510 fn test_filter_sql_infos() {
511 let mut builder = SqlInfoDataBuilder::new();
512 builder.append(SqlInfo::FlightSqlServerName, "server name");
513 builder.append(
514 SqlInfo::FlightSqlServerTransaction,
515 SqlSupportedTransaction::Transaction as i32,
516 );
517 let data = builder.build().unwrap();
518
519 let batch = data.record_batch(None).unwrap();
520 assert_eq!(batch.num_rows(), 2);
521
522 let batch = data
523 .record_batch([SqlInfo::FlightSqlServerTransaction as u32])
524 .unwrap();
525 let mut ref_builder = SqlInfoDataBuilder::new();
526 ref_builder.append(
527 SqlInfo::FlightSqlServerTransaction,
528 SqlSupportedTransaction::Transaction as i32,
529 );
530 let ref_batch = ref_builder.build().unwrap().record_batch(None).unwrap();
531
532 assert_eq!(batch, ref_batch);
533 }
534}