1use core::ffi::c_char;
4use core::ptr::NonNull;
5use core::slice;
6
7use crate::alloc::Allocator;
8use crate::error::{Result, check};
9use crate::sys;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13#[repr(i32)]
14pub enum Kind {
15 Void = sys::CHC_VOID,
16 Int8 = sys::CHC_INT8,
17 Int16 = sys::CHC_INT16,
18 Int32 = sys::CHC_INT32,
19 Int64 = sys::CHC_INT64,
20 Int128 = sys::CHC_INT128,
21 Int256 = sys::CHC_INT256,
22 UInt8 = sys::CHC_UINT8,
23 UInt16 = sys::CHC_UINT16,
24 UInt32 = sys::CHC_UINT32,
25 UInt64 = sys::CHC_UINT64,
26 UInt128 = sys::CHC_UINT128,
27 UInt256 = sys::CHC_UINT256,
28 Float32 = sys::CHC_FLOAT32,
29 Float64 = sys::CHC_FLOAT64,
30 BFloat16 = sys::CHC_BFLOAT16,
31 Bool = sys::CHC_BOOL,
32 Date = sys::CHC_DATE,
33 Date32 = sys::CHC_DATE32,
34 DateTime = sys::CHC_DATETIME,
35 DateTime64 = sys::CHC_DATETIME64,
36 Time = sys::CHC_TIME,
37 Time64 = sys::CHC_TIME64,
38 String = sys::CHC_STRING,
39 FixedString = sys::CHC_FIXED_STRING,
40 Decimal32 = sys::CHC_DECIMAL32,
41 Decimal64 = sys::CHC_DECIMAL64,
42 Decimal128 = sys::CHC_DECIMAL128,
43 Decimal256 = sys::CHC_DECIMAL256,
44 Uuid = sys::CHC_UUID,
45 Ipv4 = sys::CHC_IPV4,
46 Ipv6 = sys::CHC_IPV6,
47 Enum8 = sys::CHC_ENUM8,
48 Enum16 = sys::CHC_ENUM16,
49 Nullable = sys::CHC_NULLABLE,
50 Array = sys::CHC_ARRAY,
51 Tuple = sys::CHC_TUPLE,
52 Map = sys::CHC_MAP,
53 Nested = sys::CHC_NESTED,
54 LowCardinality = sys::CHC_LOW_CARDINALITY,
55 Interval = sys::CHC_INTERVAL,
56 Point = sys::CHC_POINT,
57 Ring = sys::CHC_RING,
58 LineString = sys::CHC_LINE_STRING,
59 Polygon = sys::CHC_POLYGON,
60 MultiPolygon = sys::CHC_MULTI_POLYGON,
61 MultiLineString = sys::CHC_MULTI_LINE_STRING,
62 Variant = sys::CHC_VARIANT,
63 Dynamic = sys::CHC_DYNAMIC,
64 Json = sys::CHC_JSON,
65 Object = sys::CHC_OBJECT,
66 AggregateFunction = sys::CHC_AGGREGATE_FUNCTION,
67 SimpleAggregateFunction = sys::CHC_SIMPLE_AGGREGATE_FUNCTION,
68 Nothing = sys::CHC_NOTHING,
69 QBit = sys::CHC_QBIT,
70}
71
72impl Kind {
73 pub(crate) fn from_raw(k: sys::chc_kind) -> Option<Self> {
74 Some(match k {
76 sys::CHC_VOID => Self::Void,
77 sys::CHC_INT8 => Self::Int8,
78 sys::CHC_INT16 => Self::Int16,
79 sys::CHC_INT32 => Self::Int32,
80 sys::CHC_INT64 => Self::Int64,
81 sys::CHC_INT128 => Self::Int128,
82 sys::CHC_INT256 => Self::Int256,
83 sys::CHC_UINT8 => Self::UInt8,
84 sys::CHC_UINT16 => Self::UInt16,
85 sys::CHC_UINT32 => Self::UInt32,
86 sys::CHC_UINT64 => Self::UInt64,
87 sys::CHC_UINT128 => Self::UInt128,
88 sys::CHC_UINT256 => Self::UInt256,
89 sys::CHC_FLOAT32 => Self::Float32,
90 sys::CHC_FLOAT64 => Self::Float64,
91 sys::CHC_BFLOAT16 => Self::BFloat16,
92 sys::CHC_BOOL => Self::Bool,
93 sys::CHC_DATE => Self::Date,
94 sys::CHC_DATE32 => Self::Date32,
95 sys::CHC_DATETIME => Self::DateTime,
96 sys::CHC_DATETIME64 => Self::DateTime64,
97 sys::CHC_TIME => Self::Time,
98 sys::CHC_TIME64 => Self::Time64,
99 sys::CHC_STRING => Self::String,
100 sys::CHC_FIXED_STRING => Self::FixedString,
101 sys::CHC_DECIMAL32 => Self::Decimal32,
102 sys::CHC_DECIMAL64 => Self::Decimal64,
103 sys::CHC_DECIMAL128 => Self::Decimal128,
104 sys::CHC_DECIMAL256 => Self::Decimal256,
105 sys::CHC_UUID => Self::Uuid,
106 sys::CHC_IPV4 => Self::Ipv4,
107 sys::CHC_IPV6 => Self::Ipv6,
108 sys::CHC_ENUM8 => Self::Enum8,
109 sys::CHC_ENUM16 => Self::Enum16,
110 sys::CHC_NULLABLE => Self::Nullable,
111 sys::CHC_ARRAY => Self::Array,
112 sys::CHC_TUPLE => Self::Tuple,
113 sys::CHC_MAP => Self::Map,
114 sys::CHC_NESTED => Self::Nested,
115 sys::CHC_LOW_CARDINALITY => Self::LowCardinality,
116 sys::CHC_INTERVAL => Self::Interval,
117 sys::CHC_POINT => Self::Point,
118 sys::CHC_RING => Self::Ring,
119 sys::CHC_LINE_STRING => Self::LineString,
120 sys::CHC_POLYGON => Self::Polygon,
121 sys::CHC_MULTI_POLYGON => Self::MultiPolygon,
122 sys::CHC_MULTI_LINE_STRING => Self::MultiLineString,
123 sys::CHC_VARIANT => Self::Variant,
124 sys::CHC_DYNAMIC => Self::Dynamic,
125 sys::CHC_JSON => Self::Json,
126 sys::CHC_OBJECT => Self::Object,
127 sys::CHC_AGGREGATE_FUNCTION => Self::AggregateFunction,
128 sys::CHC_SIMPLE_AGGREGATE_FUNCTION => Self::SimpleAggregateFunction,
129 sys::CHC_NOTHING => Self::Nothing,
130 sys::CHC_QBIT => Self::QBit,
131 _ => return None,
132 })
133 }
134}
135
136#[derive(Debug, Clone, Copy, PartialEq, Eq)]
141#[repr(i32)]
142pub enum IntervalUnit {
143 Nanosecond = sys::CHC_INTERVAL_NANOSECOND,
144 Microsecond = sys::CHC_INTERVAL_MICROSECOND,
145 Millisecond = sys::CHC_INTERVAL_MILLISECOND,
146 Second = sys::CHC_INTERVAL_SECOND,
147 Minute = sys::CHC_INTERVAL_MINUTE,
148 Hour = sys::CHC_INTERVAL_HOUR,
149 Day = sys::CHC_INTERVAL_DAY,
150 Week = sys::CHC_INTERVAL_WEEK,
151 Month = sys::CHC_INTERVAL_MONTH,
152 Quarter = sys::CHC_INTERVAL_QUARTER,
153 Year = sys::CHC_INTERVAL_YEAR,
154}
155
156impl IntervalUnit {
157 pub(crate) fn from_raw(u: sys::chc_interval_unit) -> Option<Self> {
160 Some(match u {
161 sys::CHC_INTERVAL_NANOSECOND => Self::Nanosecond,
162 sys::CHC_INTERVAL_MICROSECOND => Self::Microsecond,
163 sys::CHC_INTERVAL_MILLISECOND => Self::Millisecond,
164 sys::CHC_INTERVAL_SECOND => Self::Second,
165 sys::CHC_INTERVAL_MINUTE => Self::Minute,
166 sys::CHC_INTERVAL_HOUR => Self::Hour,
167 sys::CHC_INTERVAL_DAY => Self::Day,
168 sys::CHC_INTERVAL_WEEK => Self::Week,
169 sys::CHC_INTERVAL_MONTH => Self::Month,
170 sys::CHC_INTERVAL_QUARTER => Self::Quarter,
171 sys::CHC_INTERVAL_YEAR => Self::Year,
172 _ => return None,
173 })
174 }
175}
176
177pub struct TypeAst {
181 raw: NonNull<sys::chc_type>,
182 alloc: Allocator,
183}
184
185impl TypeAst {
186 pub fn parse(name: &str, alloc: Allocator) -> Result<Self> {
188 let mut out: *mut sys::chc_type = core::ptr::null_mut();
189 let mut err = sys::chc_err::zeroed();
190 let rc = unsafe {
191 sys::chc_type_parse(
192 name.as_ptr().cast::<c_char>(),
193 name.len(),
194 alloc.as_ptr(),
195 &mut out,
196 &mut err,
197 )
198 };
199 check(rc, &err)?;
200 Ok(Self {
201 raw: NonNull::new(out).expect("chc_type_parse returned OK with NULL"),
202 alloc,
203 })
204 }
205
206 pub fn view(&self) -> TypeRef<'_> {
208 TypeRef {
209 raw: self.raw.as_ptr().cast_const(),
210 _marker: core::marker::PhantomData,
211 }
212 }
213}
214
215impl Drop for TypeAst {
216 fn drop(&mut self) {
217 unsafe { sys::chc_type_destroy(self.raw.as_ptr(), self.alloc.as_ptr()) };
218 }
219}
220
221unsafe impl Send for TypeAst {}
222
223#[derive(Clone, Copy)]
225pub struct TypeRef<'a> {
226 pub(crate) raw: *const sys::chc_type,
227 pub(crate) _marker: core::marker::PhantomData<&'a sys::chc_type>,
228}
229
230impl<'a> TypeRef<'a> {
231 pub fn kind(&self) -> Option<Kind> {
232 Kind::from_raw(unsafe { sys::chc_type_kind(self.raw) })
233 }
234
235 pub fn n_children(&self) -> usize {
241 unsafe { sys::chc_type_n_children(self.raw) }
242 }
243
244 pub fn child(&self, i: usize) -> Option<TypeRef<'a>> {
245 let c = unsafe { sys::chc_type_child(self.raw, i) };
246 if c.is_null() {
247 None
248 } else {
249 Some(TypeRef {
250 raw: c,
251 _marker: core::marker::PhantomData,
252 })
253 }
254 }
255
256 pub fn fixed_size(&self) -> i32 {
258 unsafe { sys::chc_type_fixed_size(self.raw) }
259 }
260
261 pub fn elem_size(&self) -> usize {
264 unsafe { sys::chc_type_elem_size(self.raw) }
265 }
266
267 pub fn decimal_precision(&self) -> i32 {
269 unsafe { sys::chc_type_decimal_precision(self.raw) }
270 }
271
272 pub fn decimal_scale(&self) -> i32 {
274 unsafe { sys::chc_type_decimal_scale(self.raw) }
275 }
276
277 pub fn datetime64_scale(&self) -> i32 {
280 unsafe { sys::chc_type_datetime64_scale(self.raw) }
281 }
282
283 pub fn interval_unit(&self) -> Option<IntervalUnit> {
285 IntervalUnit::from_raw(unsafe { sys::chc_type_interval_unit(self.raw) })
286 }
287
288 pub fn qbit_dimension(&self) -> usize {
297 unsafe { sys::chc_type_qbit_dimension(self.raw) }
298 }
299
300 pub fn qbit_element_size(&self) -> usize {
303 unsafe { sys::chc_type_qbit_element_size(self.raw) }
304 }
305
306 pub fn timezone(&self) -> Option<&'a [u8]> {
308 let mut len = 0;
309 let p = unsafe { sys::chc_type_timezone(self.raw, &mut len) };
310 if p.is_null() {
311 None
312 } else {
313 Some(unsafe { slice::from_raw_parts(p.cast::<u8>(), len) })
315 }
316 }
317
318 pub fn name(&self) -> Option<&'a [u8]> {
320 let mut len = 0;
321 let p = unsafe { sys::chc_type_name(self.raw, &mut len) };
322 (!p.is_null()).then(|| unsafe { slice::from_raw_parts(p.cast::<u8>(), len) })
323 }
324
325 pub fn enum_count(&self) -> usize {
328 unsafe { sys::chc_type_enum_count(self.raw) }
329 }
330
331 pub fn enum_at(&self, i: usize) -> Option<(&'a [u8], i64)> {
334 if i >= self.enum_count() {
335 return None;
336 }
337 let mut name_ptr: *const c_char = core::ptr::null();
338 let mut name_len: usize = 0;
339 let mut value: i64 = 0;
340 unsafe {
341 sys::chc_type_enum_at(self.raw, i, &mut name_ptr, &mut name_len, &mut value);
342 }
343 (!name_ptr.is_null()).then(|| {
344 (
345 unsafe { slice::from_raw_parts(name_ptr.cast::<u8>(), name_len) },
346 value,
347 )
348 })
349 }
350
351 pub fn tuple_field_name(&self, i: usize) -> Option<&'a [u8]> {
353 let mut len = 0;
354 let p = unsafe { sys::chc_type_tuple_field_name(self.raw, i, &mut len) };
355 if p.is_null() {
356 None
357 } else {
358 Some(unsafe { slice::from_raw_parts(p.cast::<u8>(), len) })
359 }
360 }
361
362 pub fn agg_function(&self) -> Option<&'a [u8]> {
368 let mut len = 0;
369 let p = unsafe { sys::chc_type_agg_function(self.raw, &mut len) };
370 if p.is_null() {
371 None
372 } else {
373 Some(unsafe { slice::from_raw_parts(p.cast::<u8>(), len) })
374 }
375 }
376
377 pub fn format(&self) -> String {
379 let needed = unsafe { sys::chc_type_format(self.raw, core::ptr::null_mut(), 0) };
380 let mut buf = vec![0u8; needed.saturating_add(1)];
382 let _ =
383 unsafe { sys::chc_type_format(self.raw, buf.as_mut_ptr().cast::<c_char>(), buf.len()) };
384 buf.truncate(needed);
385 String::from_utf8_lossy(&buf).into_owned()
386 }
387}
388
389#[cfg(test)]
390mod tests {
391 use super::{IntervalUnit, Kind, TypeAst};
392 use crate::Allocator;
393 use crate::sys;
394
395 fn parse(name: &str) -> TypeAst {
396 TypeAst::parse(name, Allocator::stdlib()).expect(name)
397 }
398
399 #[test]
400 fn qbit_metadata() {
401 let ty = parse("QBit(Float32, 16)");
402 let view = ty.view();
403 assert_eq!(view.kind(), Some(Kind::QBit));
404 assert_eq!(view.qbit_dimension(), 16);
405 assert_eq!(view.qbit_element_size(), 32);
406 assert_eq!(view.format(), "QBit(Float32, 16)");
407 assert_eq!(view.child(0).and_then(|c| c.kind()), Some(Kind::Float32));
408 }
409
410 #[test]
411 fn qbit_accessors_are_zero_off_qbit() {
412 let ty = parse("Array(UInt32)");
413 assert_eq!(ty.view().qbit_dimension(), 0);
414 assert_eq!(ty.view().qbit_element_size(), 0);
415 }
416
417 #[test]
418 fn interval_names_carry_their_unit() {
419 let ty = parse("IntervalQuarter");
420 assert_eq!(ty.view().kind(), Some(Kind::Interval));
421 assert_eq!(ty.view().interval_unit(), Some(IntervalUnit::Quarter));
422
423 let nested = parse("Nullable(IntervalDay)");
424 let inner = nested.view().child(0).expect("Nullable child");
425 assert_eq!(inner.interval_unit(), Some(IntervalUnit::Day));
426 assert_eq!(nested.view().interval_unit(), None);
427 }
428
429 #[test]
430 fn line_string_kinds_parse() {
431 assert_eq!(parse("LineString").view().kind(), Some(Kind::LineString));
432 assert_eq!(
433 parse("MultiLineString").view().kind(),
434 Some(Kind::MultiLineString)
435 );
436 }
437
438 #[test]
439 fn nested_fields_carry_names() {
440 let ty = parse("Nested(a UInt32, b String)");
441 let view = ty.view();
442 assert_eq!(view.kind(), Some(Kind::Nested));
443 assert_eq!(view.n_children(), 2);
444 assert_eq!(view.tuple_field_name(0), Some(&b"a"[..]));
445 assert_eq!(view.tuple_field_name(1), Some(&b"b"[..]));
446 }
447
448 #[test]
449 fn aggregate_children_drop_function_parameters() {
450 let ty = parse("AggregateFunction(quantiles(0.5, 0.9), UInt64)");
451 let view = ty.view();
452 assert_eq!(view.kind(), Some(Kind::AggregateFunction));
453 assert_eq!(view.agg_function(), Some(&b"quantiles"[..]));
454 assert_eq!(view.n_children(), 1);
455 assert_eq!(view.child(0).and_then(|c| c.kind()), Some(Kind::UInt64));
456 }
457
458 #[test]
460 fn aggregate_version_prefix_parses() {
461 let ty = parse("AggregateFunction(1, sumMap, Array(UInt32), Array(UInt64))");
462 let view = ty.view();
463 assert_eq!(view.agg_function(), Some(&b"sumMap"[..]));
464 assert_eq!(view.n_children(), 2);
465 }
466
467 #[test]
468 fn simple_aggregate_stores_its_first_argument() {
469 let ty = parse("SimpleAggregateFunction(anyLast, Nullable(String))");
470 let view = ty.view();
471 assert_eq!(view.kind(), Some(Kind::SimpleAggregateFunction));
472 assert_eq!(view.agg_function(), Some(&b"anyLast"[..]));
473 assert_eq!(view.child(0).and_then(|c| c.kind()), Some(Kind::Nullable));
474 }
475
476 #[test]
477 fn agg_function_is_none_off_aggregates() {
478 assert_eq!(parse("Tuple(UInt32)").view().agg_function(), None);
479 }
480
481 #[test]
483 fn json_parameters_leave_no_children() {
484 let ty = parse("JSON(max_dynamic_paths=16, `a.b` UInt32, SKIP `a.e`)");
485 assert_eq!(ty.view().kind(), Some(Kind::Json));
486 assert_eq!(ty.view().n_children(), 0);
487 }
488
489 #[test]
491 fn unknown_discriminant_is_none() {
492 assert_eq!(Kind::from_raw(i32::MAX), None);
493 assert_eq!(Kind::from_raw(-1), None);
494 }
495
496 #[test]
497 fn unknown_interval_unit_is_none() {
498 assert_eq!(IntervalUnit::from_raw(sys::CHC_INTERVAL_NONE), None);
499 assert_eq!(IntervalUnit::from_raw(i32::MAX), None);
500 }
501
502 #[test]
503 fn every_c_interval_unit_maps_to_its_variant() {
504 for (raw, unit) in [
505 (sys::CHC_INTERVAL_NANOSECOND, IntervalUnit::Nanosecond),
506 (sys::CHC_INTERVAL_MICROSECOND, IntervalUnit::Microsecond),
507 (sys::CHC_INTERVAL_MILLISECOND, IntervalUnit::Millisecond),
508 (sys::CHC_INTERVAL_SECOND, IntervalUnit::Second),
509 (sys::CHC_INTERVAL_MINUTE, IntervalUnit::Minute),
510 (sys::CHC_INTERVAL_HOUR, IntervalUnit::Hour),
511 (sys::CHC_INTERVAL_DAY, IntervalUnit::Day),
512 (sys::CHC_INTERVAL_WEEK, IntervalUnit::Week),
513 (sys::CHC_INTERVAL_MONTH, IntervalUnit::Month),
514 (sys::CHC_INTERVAL_QUARTER, IntervalUnit::Quarter),
515 (sys::CHC_INTERVAL_YEAR, IntervalUnit::Year),
516 ] {
517 assert_eq!(IntervalUnit::from_raw(raw), Some(unit));
518 }
519 }
520
521 #[test]
522 fn fixed_string_reports_its_width() {
523 let ty = parse("FixedString(12)");
524 assert_eq!(ty.view().kind(), Some(Kind::FixedString));
525 assert_eq!(ty.view().fixed_size(), 12);
526 assert_eq!(ty.view().elem_size(), 12);
527 assert_eq!(parse("UInt32").view().fixed_size(), 0);
528 }
529
530 #[test]
531 fn decimal_carries_precision_and_scale() {
532 let ty = parse("Decimal(18, 4)");
533 assert_eq!(ty.view().kind(), Some(Kind::Decimal64));
534 assert_eq!(ty.view().decimal_precision(), 18);
535 assert_eq!(ty.view().decimal_scale(), 4);
536
537 let plain = parse("String");
538 assert_eq!(plain.view().decimal_precision(), 0);
539 assert_eq!(plain.view().decimal_scale(), 0);
540 }
541
542 #[test]
543 fn subsecond_scale_comes_from_the_type() {
544 assert_eq!(parse("DateTime64(9)").view().datetime64_scale(), 9);
545 assert_eq!(parse("Time64(3)").view().datetime64_scale(), 3);
546 assert_eq!(parse("DateTime").view().datetime64_scale(), 0);
547 }
548
549 #[test]
550 fn timezone_is_present_only_on_temporal_types() {
551 let ty = parse("DateTime64(3, 'Europe/Paris')");
552 assert_eq!(ty.view().timezone(), Some(&b"Europe/Paris"[..]));
553 assert_eq!(parse("UInt32").view().timezone(), None);
554 }
555
556 #[test]
557 fn every_parsed_type_reports_its_source_name() {
558 assert_eq!(
559 parse("Array(UInt32)").view().name(),
560 Some(&b"Array(UInt32)"[..])
561 );
562 let child = parse("Array(UInt32)");
563 let child = child.view().child(0).expect("element type");
564 assert_eq!(child.name(), Some(&b"UInt32"[..]));
565 assert_eq!(child.format(), "UInt32");
566 }
567
568 #[test]
569 fn enum_entries_are_addressable_by_index() {
570 let ty = parse("Enum8('red' = -1, 'green' = 2)");
571 let view = ty.view();
572 assert_eq!(view.kind(), Some(Kind::Enum8));
573 assert_eq!(view.enum_count(), 2);
574 assert_eq!(view.enum_at(0), Some((&b"red"[..], -1)));
575 assert_eq!(view.enum_at(1), Some((&b"green"[..], 2)));
576 assert_eq!(view.enum_at(2), None);
577 }
578
579 #[test]
580 fn enum_accessors_are_empty_off_enums() {
581 let ty = parse("UInt32");
582 assert_eq!(ty.view().enum_count(), 0);
583 assert_eq!(ty.view().enum_at(0), None);
584 }
585
586 #[test]
587 fn unnamed_and_out_of_range_fields_have_no_name() {
588 let ty = parse("Tuple(UInt8, String)");
589 assert_eq!(ty.view().tuple_field_name(0), None);
590 assert_eq!(ty.view().tuple_field_name(9), None);
591 assert_eq!(parse("UInt32").view().tuple_field_name(0), None);
592 }
593
594 #[test]
595 fn child_index_past_the_end_is_none() {
596 let ty = parse("Map(String, UInt64)");
597 assert_eq!(ty.view().n_children(), 2);
598 assert!(ty.view().child(2).is_none());
599 assert!(parse("UInt32").view().child(0).is_none());
600 }
601
602 #[test]
603 fn a_bad_type_name_is_an_error() {
604 let err = TypeAst::parse("NotAType(", Allocator::stdlib())
605 .err()
606 .expect("unterminated type name accepted");
607 assert_eq!(err.kind, crate::ErrorKind::Type);
608 }
609}