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