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