1use std::sync::Arc;
28
29use arrow_array::builder::{BooleanBuilder, Int32Builder, ListBuilder, StringBuilder};
30use arrow_array::{ArrayRef, Int32Array, ListArray, RecordBatch, Scalar};
31use arrow_ord::cmp::eq;
32use arrow_schema::{DataType, Field, Schema, SchemaRef};
33use arrow_select::filter::filter_record_batch;
34use arrow_select::take::take;
35use once_cell::sync::Lazy;
36
37use super::lexsort_to_indices;
38use crate::error::*;
39use crate::sql::{CommandGetXdbcTypeInfo, Nullable, Searchable, XdbcDataType, XdbcDatetimeSubcode};
40
41#[derive(Debug, Clone, Default)]
43pub struct XdbcTypeInfo {
44 pub type_name: String,
46 pub data_type: XdbcDataType,
48 pub column_size: Option<i32>,
50 pub literal_prefix: Option<String>,
52 pub literal_suffix: Option<String>,
54 pub create_params: Option<Vec<String>>,
56 pub nullable: Nullable,
58 pub case_sensitive: bool,
60 pub searchable: Searchable,
62 pub unsigned_attribute: Option<bool>,
64 pub fixed_prec_scale: bool,
66 pub auto_increment: Option<bool>,
68 pub local_type_name: Option<String>,
70 pub minimum_scale: Option<i32>,
72 pub maximum_scale: Option<i32>,
74 pub sql_data_type: XdbcDataType,
76 pub datetime_subcode: Option<XdbcDatetimeSubcode>,
78 pub num_prec_radix: Option<i32>,
80 pub interval_precision: Option<i32>,
82}
83
84pub struct XdbcTypeInfoData {
92 batch: RecordBatch,
93}
94
95impl XdbcTypeInfoData {
96 pub fn record_batch(&self, data_type: impl Into<Option<i32>>) -> Result<RecordBatch> {
99 if let Some(dt) = data_type.into() {
100 let scalar = Int32Array::from(vec![dt]);
101 let filter = eq(self.batch.column(1), &Scalar::new(&scalar))?;
102 Ok(filter_record_batch(&self.batch, &filter)?)
103 } else {
104 Ok(self.batch.clone())
105 }
106 }
107
108 pub fn schema(&self) -> SchemaRef {
111 self.batch.schema()
112 }
113}
114
115pub struct XdbcTypeInfoDataBuilder {
151 infos: Vec<XdbcTypeInfo>,
152}
153
154impl Default for XdbcTypeInfoDataBuilder {
155 fn default() -> Self {
156 Self::new()
157 }
158}
159
160impl XdbcTypeInfoDataBuilder {
161 pub fn new() -> Self {
163 Self { infos: Vec::new() }
164 }
165
166 pub fn append(&mut self, info: XdbcTypeInfo) {
168 self.infos.push(info);
169 }
170
171 pub fn build(self) -> Result<XdbcTypeInfoData> {
173 let mut type_name_builder = StringBuilder::new();
174 let mut data_type_builder = Int32Builder::new();
175 let mut column_size_builder = Int32Builder::new();
176 let mut literal_prefix_builder = StringBuilder::new();
177 let mut literal_suffix_builder = StringBuilder::new();
178 let mut create_params_builder = ListBuilder::new(StringBuilder::new());
179 let mut nullable_builder = Int32Builder::new();
180 let mut case_sensitive_builder = BooleanBuilder::new();
181 let mut searchable_builder = Int32Builder::new();
182 let mut unsigned_attribute_builder = BooleanBuilder::new();
183 let mut fixed_prec_scale_builder = BooleanBuilder::new();
184 let mut auto_increment_builder = BooleanBuilder::new();
185 let mut local_type_name_builder = StringBuilder::new();
186 let mut minimum_scale_builder = Int32Builder::new();
187 let mut maximum_scale_builder = Int32Builder::new();
188 let mut sql_data_type_builder = Int32Builder::new();
189 let mut datetime_subcode_builder = Int32Builder::new();
190 let mut num_prec_radix_builder = Int32Builder::new();
191 let mut interval_precision_builder = Int32Builder::new();
192
193 self.infos.into_iter().for_each(|info| {
194 type_name_builder.append_value(info.type_name);
195 data_type_builder.append_value(info.data_type as i32);
196 column_size_builder.append_option(info.column_size);
197 literal_prefix_builder.append_option(info.literal_prefix);
198 literal_suffix_builder.append_option(info.literal_suffix);
199 if let Some(params) = info.create_params {
200 if !params.is_empty() {
201 for param in params {
202 create_params_builder.values().append_value(param);
203 }
204 create_params_builder.append(true);
205 } else {
206 create_params_builder.append_null();
207 }
208 } else {
209 create_params_builder.append_null();
210 }
211 nullable_builder.append_value(info.nullable as i32);
212 case_sensitive_builder.append_value(info.case_sensitive);
213 searchable_builder.append_value(info.searchable as i32);
214 unsigned_attribute_builder.append_option(info.unsigned_attribute);
215 fixed_prec_scale_builder.append_value(info.fixed_prec_scale);
216 auto_increment_builder.append_option(info.auto_increment);
217 local_type_name_builder.append_option(info.local_type_name);
218 minimum_scale_builder.append_option(info.minimum_scale);
219 maximum_scale_builder.append_option(info.maximum_scale);
220 sql_data_type_builder.append_value(info.sql_data_type as i32);
221 datetime_subcode_builder.append_option(info.datetime_subcode.map(|code| code as i32));
222 num_prec_radix_builder.append_option(info.num_prec_radix);
223 interval_precision_builder.append_option(info.interval_precision);
224 });
225
226 let type_name = Arc::new(type_name_builder.finish());
227 let data_type = Arc::new(data_type_builder.finish());
228 let column_size = Arc::new(column_size_builder.finish());
229 let literal_prefix = Arc::new(literal_prefix_builder.finish());
230 let literal_suffix = Arc::new(literal_suffix_builder.finish());
231 let (field, offsets, values, nulls) = create_params_builder.finish().into_parts();
232 let new_field = Arc::new(field.as_ref().clone().with_nullable(false));
234 let create_params = Arc::new(ListArray::new(new_field, offsets, values, nulls)) as ArrayRef;
235 let nullable = Arc::new(nullable_builder.finish());
236 let case_sensitive = Arc::new(case_sensitive_builder.finish());
237 let searchable = Arc::new(searchable_builder.finish());
238 let unsigned_attribute = Arc::new(unsigned_attribute_builder.finish());
239 let fixed_prec_scale = Arc::new(fixed_prec_scale_builder.finish());
240 let auto_increment = Arc::new(auto_increment_builder.finish());
241 let local_type_name = Arc::new(local_type_name_builder.finish());
242 let minimum_scale = Arc::new(minimum_scale_builder.finish());
243 let maximum_scale = Arc::new(maximum_scale_builder.finish());
244 let sql_data_type = Arc::new(sql_data_type_builder.finish());
245 let datetime_subcode = Arc::new(datetime_subcode_builder.finish());
246 let num_prec_radix = Arc::new(num_prec_radix_builder.finish());
247 let interval_precision = Arc::new(interval_precision_builder.finish());
248
249 let batch = RecordBatch::try_new(
250 Arc::clone(&GET_XDBC_INFO_SCHEMA),
251 vec![
252 type_name,
253 data_type,
254 column_size,
255 literal_prefix,
256 literal_suffix,
257 create_params,
258 nullable,
259 case_sensitive,
260 searchable,
261 unsigned_attribute,
262 fixed_prec_scale,
263 auto_increment,
264 local_type_name,
265 minimum_scale,
266 maximum_scale,
267 sql_data_type,
268 datetime_subcode,
269 num_prec_radix,
270 interval_precision,
271 ],
272 )?;
273
274 let sort_cols = batch.project(&[1, 0])?;
276 let indices = lexsort_to_indices(sort_cols.columns());
277 let columns = batch
278 .columns()
279 .iter()
280 .map(|c| take(c, &indices, None))
281 .collect::<std::result::Result<Vec<_>, _>>()?;
282
283 Ok(XdbcTypeInfoData {
284 batch: RecordBatch::try_new(batch.schema(), columns)?,
285 })
286 }
287
288 pub fn schema(&self) -> SchemaRef {
290 Arc::clone(&GET_XDBC_INFO_SCHEMA)
291 }
292}
293
294pub struct GetXdbcTypeInfoBuilder<'a> {
296 data_type: Option<i32>,
297 infos: &'a XdbcTypeInfoData,
298}
299
300impl CommandGetXdbcTypeInfo {
301 pub fn into_builder(self, infos: &XdbcTypeInfoData) -> GetXdbcTypeInfoBuilder {
303 GetXdbcTypeInfoBuilder {
304 data_type: self.data_type,
305 infos,
306 }
307 }
308}
309
310impl GetXdbcTypeInfoBuilder<'_> {
311 pub fn build(self) -> Result<RecordBatch> {
313 self.infos.record_batch(self.data_type)
314 }
315
316 pub fn schema(&self) -> SchemaRef {
319 self.infos.schema()
320 }
321}
322
323static GET_XDBC_INFO_SCHEMA: Lazy<SchemaRef> = Lazy::new(|| {
325 Arc::new(Schema::new(vec![
326 Field::new("type_name", DataType::Utf8, false),
327 Field::new("data_type", DataType::Int32, false),
328 Field::new("column_size", DataType::Int32, true),
329 Field::new("literal_prefix", DataType::Utf8, true),
330 Field::new("literal_suffix", DataType::Utf8, true),
331 Field::new(
332 "create_params",
333 DataType::List(Arc::new(Field::new_list_field(DataType::Utf8, false))),
334 true,
335 ),
336 Field::new("nullable", DataType::Int32, false),
337 Field::new("case_sensitive", DataType::Boolean, false),
338 Field::new("searchable", DataType::Int32, false),
339 Field::new("unsigned_attribute", DataType::Boolean, true),
340 Field::new("fixed_prec_scale", DataType::Boolean, false),
341 Field::new("auto_increment", DataType::Boolean, true),
342 Field::new("local_type_name", DataType::Utf8, true),
343 Field::new("minimum_scale", DataType::Int32, true),
344 Field::new("maximum_scale", DataType::Int32, true),
345 Field::new("sql_data_type", DataType::Int32, false),
346 Field::new("datetime_subcode", DataType::Int32, true),
347 Field::new("num_prec_radix", DataType::Int32, true),
348 Field::new("interval_precision", DataType::Int32, true),
349 ]))
350});
351
352#[cfg(test)]
353mod tests {
354 use super::*;
355 use crate::sql::metadata::tests::assert_batches_eq;
356
357 #[test]
358 fn test_create_batch() {
359 let mut builder = XdbcTypeInfoDataBuilder::new();
360 builder.append(XdbcTypeInfo {
361 type_name: "VARCHAR".into(),
362 data_type: XdbcDataType::XdbcVarchar,
363 column_size: Some(i32::MAX),
364 literal_prefix: Some("'".into()),
365 literal_suffix: Some("'".into()),
366 create_params: Some(vec!["length".into()]),
367 nullable: Nullable::NullabilityNullable,
368 case_sensitive: true,
369 searchable: Searchable::Full,
370 unsigned_attribute: None,
371 fixed_prec_scale: false,
372 auto_increment: None,
373 local_type_name: Some("VARCHAR".into()),
374 minimum_scale: None,
375 maximum_scale: None,
376 sql_data_type: XdbcDataType::XdbcVarchar,
377 datetime_subcode: None,
378 num_prec_radix: None,
379 interval_precision: None,
380 });
381 builder.append(XdbcTypeInfo {
382 type_name: "INTEGER".into(),
383 data_type: XdbcDataType::XdbcInteger,
384 column_size: Some(32),
385 literal_prefix: None,
386 literal_suffix: None,
387 create_params: None,
388 nullable: Nullable::NullabilityNullable,
389 case_sensitive: false,
390 searchable: Searchable::Full,
391 unsigned_attribute: Some(false),
392 fixed_prec_scale: false,
393 auto_increment: Some(false),
394 local_type_name: Some("INTEGER".into()),
395 minimum_scale: None,
396 maximum_scale: None,
397 sql_data_type: XdbcDataType::XdbcInteger,
398 datetime_subcode: None,
399 num_prec_radix: Some(2),
400 interval_precision: None,
401 });
402 builder.append(XdbcTypeInfo {
403 type_name: "INTERVAL".into(),
404 data_type: XdbcDataType::XdbcInterval,
405 column_size: Some(i32::MAX),
406 literal_prefix: Some("'".into()),
407 literal_suffix: Some("'".into()),
408 create_params: None,
409 nullable: Nullable::NullabilityNullable,
410 case_sensitive: false,
411 searchable: Searchable::Full,
412 unsigned_attribute: None,
413 fixed_prec_scale: false,
414 auto_increment: None,
415 local_type_name: Some("INTERVAL".into()),
416 minimum_scale: None,
417 maximum_scale: None,
418 sql_data_type: XdbcDataType::XdbcInterval,
419 datetime_subcode: Some(XdbcDatetimeSubcode::XdbcSubcodeUnknown),
420 num_prec_radix: None,
421 interval_precision: None,
422 });
423 let infos = builder.build().unwrap();
424
425 let batch = infos.record_batch(None).unwrap();
426 let expected = vec![
427 "+-----------+-----------+-------------+----------------+----------------+---------------+----------+----------------+------------+--------------------+------------------+----------------+-----------------+---------------+---------------+---------------+------------------+----------------+--------------------+",
428 "| type_name | data_type | column_size | literal_prefix | literal_suffix | create_params | nullable | case_sensitive | searchable | unsigned_attribute | fixed_prec_scale | auto_increment | local_type_name | minimum_scale | maximum_scale | sql_data_type | datetime_subcode | num_prec_radix | interval_precision |",
429 "+-----------+-----------+-------------+----------------+----------------+---------------+----------+----------------+------------+--------------------+------------------+----------------+-----------------+---------------+---------------+---------------+------------------+----------------+--------------------+",
430 "| INTEGER | 4 | 32 | | | | 1 | false | 3 | false | false | false | INTEGER | | | 4 | | 2 | |",
431 "| INTERVAL | 10 | 2147483647 | ' | ' | | 1 | false | 3 | | false | | INTERVAL | | | 10 | 0 | | |",
432 "| VARCHAR | 12 | 2147483647 | ' | ' | [length] | 1 | true | 3 | | false | | VARCHAR | | | 12 | | | |",
433 "+-----------+-----------+-------------+----------------+----------------+---------------+----------+----------------+------------+--------------------+------------------+----------------+-----------------+---------------+---------------+---------------+------------------+----------------+--------------------+",
434 ];
435 assert_batches_eq(&[batch], &expected);
436
437 let batch = infos.record_batch(Some(10)).unwrap();
438 let expected = vec![
439 "+-----------+-----------+-------------+----------------+----------------+---------------+----------+----------------+------------+--------------------+------------------+----------------+-----------------+---------------+---------------+---------------+------------------+----------------+--------------------+",
440 "| type_name | data_type | column_size | literal_prefix | literal_suffix | create_params | nullable | case_sensitive | searchable | unsigned_attribute | fixed_prec_scale | auto_increment | local_type_name | minimum_scale | maximum_scale | sql_data_type | datetime_subcode | num_prec_radix | interval_precision |",
441 "+-----------+-----------+-------------+----------------+----------------+---------------+----------+----------------+------------+--------------------+------------------+----------------+-----------------+---------------+---------------+---------------+------------------+----------------+--------------------+",
442 "| INTERVAL | 10 | 2147483647 | ' | ' | | 1 | false | 3 | | false | | INTERVAL | | | 10 | 0 | | |",
443 "+-----------+-----------+-------------+----------------+----------------+---------------+----------+----------------+------------+--------------------+------------------+----------------+-----------------+---------------+---------------+---------------+------------------+----------------+--------------------+",
444 ];
445 assert_batches_eq(&[batch], &expected);
446 }
447}