quack-ruby 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/LICENSE-DuckDB +7 -0
  4. data/LICENSE-QuackProtocol +21 -0
  5. data/LICENSE-Rust-dependencies.json +2057 -0
  6. data/README.md +229 -0
  7. data/ext/quack/Cargo.lock +1803 -0
  8. data/ext/quack/Cargo.toml +17 -0
  9. data/ext/quack/extconf.rb +4 -0
  10. data/ext/quack/src/lib.rs +647 -0
  11. data/ext/quack/vendor/quack_protocol/Cargo.lock +1907 -0
  12. data/ext/quack/vendor/quack_protocol/Cargo.toml +41 -0
  13. data/ext/quack/vendor/quack_protocol/LICENSE +21 -0
  14. data/ext/quack/vendor/quack_protocol/PATCHES.md +16 -0
  15. data/ext/quack/vendor/quack_protocol/README.md +272 -0
  16. data/ext/quack/vendor/quack_protocol/src/arrow/columns.rs +399 -0
  17. data/ext/quack/vendor/quack_protocol/src/arrow/mod.rs +134 -0
  18. data/ext/quack/vendor/quack_protocol/src/arrow/schema.rs +103 -0
  19. data/ext/quack/vendor/quack_protocol/src/arrow/tests.rs +851 -0
  20. data/ext/quack/vendor/quack_protocol/src/arrow/values.rs +150 -0
  21. data/ext/quack/vendor/quack_protocol/src/binary.rs +504 -0
  22. data/ext/quack/vendor/quack_protocol/src/builders.rs +217 -0
  23. data/ext/quack/vendor/quack_protocol/src/client.rs +1486 -0
  24. data/ext/quack/vendor/quack_protocol/src/constants.rs +11 -0
  25. data/ext/quack/vendor/quack_protocol/src/errors.rs +103 -0
  26. data/ext/quack/vendor/quack_protocol/src/inspection.rs +250 -0
  27. data/ext/quack/vendor/quack_protocol/src/json.rs +225 -0
  28. data/ext/quack/vendor/quack_protocol/src/lib.rs +54 -0
  29. data/ext/quack/vendor/quack_protocol/src/logical_types.rs +977 -0
  30. data/ext/quack/vendor/quack_protocol/src/messages.rs +843 -0
  31. data/ext/quack/vendor/quack_protocol/src/pool.rs +741 -0
  32. data/ext/quack/vendor/quack_protocol/src/protocol_tests.rs +759 -0
  33. data/ext/quack/vendor/quack_protocol/src/proxy_error.rs +77 -0
  34. data/ext/quack/vendor/quack_protocol/src/sql.rs +359 -0
  35. data/ext/quack/vendor/quack_protocol/src/tls.rs +354 -0
  36. data/ext/quack/vendor/quack_protocol/src/values.rs +77 -0
  37. data/ext/quack/vendor/quack_protocol/src/vector.rs +1364 -0
  38. data/ext/quack/vendor/quack_protocol/testdata/tls/key.der +0 -0
  39. data/ext/quack/vendor/quack_protocol/testdata/tls/spki.der +0 -0
  40. data/ext/quack/vendor/quack_protocol/testdata/tls/v1-cert.der +0 -0
  41. data/ext/quack/vendor/quack_protocol/testdata/tls/v3-cert.der +0 -0
  42. data/lib/quack/codec.rb +263 -0
  43. data/lib/quack/connection.rb +131 -0
  44. data/lib/quack/logical_types.rb +72 -0
  45. data/lib/quack/result.rb +64 -0
  46. data/lib/quack/sql.rb +183 -0
  47. data/lib/quack/types.rb +40 -0
  48. data/lib/quack/variant.rb +298 -0
  49. data/lib/quack.rb +34 -0
  50. metadata +136 -0
@@ -0,0 +1,977 @@
1
+ use crate::binary::{BinaryReader, BinaryWriter};
2
+ use crate::errors::{QuackError, Result};
3
+
4
+ #[derive(Clone, Copy, Debug, PartialEq, Eq)]
5
+ #[repr(u64)]
6
+ pub(crate) enum PhysicalType {
7
+ Bool = 1,
8
+ UInt8 = 2,
9
+ Int8 = 3,
10
+ UInt16 = 4,
11
+ Int16 = 5,
12
+ UInt32 = 6,
13
+ Int32 = 7,
14
+ UInt64 = 8,
15
+ Int64 = 9,
16
+ Float = 11,
17
+ Double = 12,
18
+ Interval = 21,
19
+ List = 23,
20
+ Struct = 24,
21
+ Array = 29,
22
+ Varchar = 200,
23
+ UInt128 = 203,
24
+ Int128 = 204,
25
+ Bit = 206,
26
+ Invalid = 255,
27
+ }
28
+
29
+ #[derive(Clone, Copy, Debug, PartialEq, Eq)]
30
+ #[repr(u64)]
31
+ pub enum LogicalTypeId {
32
+ Invalid = 0,
33
+ SqlNull = 1,
34
+ Unknown = 2,
35
+ Any = 3,
36
+ Unbound = 4,
37
+ Template = 5,
38
+ Type = 6,
39
+ Boolean = 10,
40
+ TinyInt = 11,
41
+ SmallInt = 12,
42
+ Integer = 13,
43
+ BigInt = 14,
44
+ Date = 15,
45
+ Time = 16,
46
+ TimestampSec = 17,
47
+ TimestampMs = 18,
48
+ Timestamp = 19,
49
+ TimestampNs = 20,
50
+ Decimal = 21,
51
+ Float = 22,
52
+ Double = 23,
53
+ Char = 24,
54
+ Varchar = 25,
55
+ Blob = 26,
56
+ Interval = 27,
57
+ UTinyInt = 28,
58
+ USmallInt = 29,
59
+ UInteger = 30,
60
+ UBigInt = 31,
61
+ TimestampTz = 32,
62
+ TimestampTzNs = 33,
63
+ TimeTz = 34,
64
+ TimeNs = 35,
65
+ Bit = 36,
66
+ StringLiteral = 37,
67
+ IntegerLiteral = 38,
68
+ BigNum = 39,
69
+ UHugeInt = 49,
70
+ HugeInt = 50,
71
+ Pointer = 51,
72
+ Validity = 53,
73
+ Uuid = 54,
74
+ Geometry = 60,
75
+ Struct = 100,
76
+ List = 101,
77
+ Map = 102,
78
+ Table = 103,
79
+ Enum = 104,
80
+ AggregateState = 105,
81
+ Lambda = 106,
82
+ Union = 107,
83
+ Array = 108,
84
+ Variant = 109,
85
+ Tuple = 110,
86
+ }
87
+
88
+ impl TryFrom<u64> for LogicalTypeId {
89
+ type Error = QuackError;
90
+
91
+ fn try_from(value: u64) -> Result<Self> {
92
+ Ok(match value {
93
+ 0 => Self::Invalid,
94
+ 1 => Self::SqlNull,
95
+ 2 => Self::Unknown,
96
+ 3 => Self::Any,
97
+ 4 => Self::Unbound,
98
+ 5 => Self::Template,
99
+ 6 => Self::Type,
100
+ 10 => Self::Boolean,
101
+ 11 => Self::TinyInt,
102
+ 12 => Self::SmallInt,
103
+ 13 => Self::Integer,
104
+ 14 => Self::BigInt,
105
+ 15 => Self::Date,
106
+ 16 => Self::Time,
107
+ 17 => Self::TimestampSec,
108
+ 18 => Self::TimestampMs,
109
+ 19 => Self::Timestamp,
110
+ 20 => Self::TimestampNs,
111
+ 21 => Self::Decimal,
112
+ 22 => Self::Float,
113
+ 23 => Self::Double,
114
+ 24 => Self::Char,
115
+ 25 => Self::Varchar,
116
+ 26 => Self::Blob,
117
+ 27 => Self::Interval,
118
+ 28 => Self::UTinyInt,
119
+ 29 => Self::USmallInt,
120
+ 30 => Self::UInteger,
121
+ 31 => Self::UBigInt,
122
+ 32 => Self::TimestampTz,
123
+ 33 => Self::TimestampTzNs,
124
+ 34 => Self::TimeTz,
125
+ 35 => Self::TimeNs,
126
+ 36 => Self::Bit,
127
+ 37 => Self::StringLiteral,
128
+ 38 => Self::IntegerLiteral,
129
+ 39 => Self::BigNum,
130
+ 49 => Self::UHugeInt,
131
+ 50 => Self::HugeInt,
132
+ 51 => Self::Pointer,
133
+ 53 => Self::Validity,
134
+ 54 => Self::Uuid,
135
+ 60 => Self::Geometry,
136
+ 100 => Self::Struct,
137
+ 101 => Self::List,
138
+ 102 => Self::Map,
139
+ 103 => Self::Table,
140
+ 104 => Self::Enum,
141
+ 105 => Self::AggregateState,
142
+ 106 => Self::Lambda,
143
+ 107 => Self::Union,
144
+ 108 => Self::Array,
145
+ 109 => Self::Variant,
146
+ 110 => Self::Tuple,
147
+ _ => {
148
+ return Err(QuackError::protocol(format!(
149
+ "unknown LogicalTypeId {value}"
150
+ )));
151
+ }
152
+ })
153
+ }
154
+ }
155
+
156
+ #[derive(Clone, Copy, Debug, PartialEq, Eq)]
157
+ #[repr(u64)]
158
+ pub(crate) enum ExtraTypeInfoType {
159
+ Invalid = 0,
160
+ Generic = 1,
161
+ Decimal = 2,
162
+ String = 3,
163
+ List = 4,
164
+ Struct = 5,
165
+ Enum = 6,
166
+ Unbound = 7,
167
+ AggregateState = 8,
168
+ Array = 9,
169
+ Any = 10,
170
+ IntegerLiteral = 11,
171
+ Template = 12,
172
+ Geo = 13,
173
+ }
174
+
175
+ impl TryFrom<u64> for ExtraTypeInfoType {
176
+ type Error = QuackError;
177
+
178
+ fn try_from(value: u64) -> Result<Self> {
179
+ Ok(match value {
180
+ 0 => Self::Invalid,
181
+ 1 => Self::Generic,
182
+ 2 => Self::Decimal,
183
+ 3 => Self::String,
184
+ 4 => Self::List,
185
+ 5 => Self::Struct,
186
+ 6 => Self::Enum,
187
+ 7 => Self::Unbound,
188
+ 8 => Self::AggregateState,
189
+ 9 => Self::Array,
190
+ 10 => Self::Any,
191
+ 11 => Self::IntegerLiteral,
192
+ 12 => Self::Template,
193
+ 13 => Self::Geo,
194
+ _ => {
195
+ return Err(QuackError::protocol(format!(
196
+ "unknown ExtraTypeInfoType {value}"
197
+ )));
198
+ }
199
+ })
200
+ }
201
+ }
202
+
203
+ #[derive(Clone, Debug, PartialEq, Eq)]
204
+ pub struct LogicalType {
205
+ pub id: LogicalTypeId,
206
+ pub type_info: Option<ExtraTypeInfo>,
207
+ }
208
+
209
+ #[derive(Clone, Debug, PartialEq, Eq)]
210
+ pub struct ChildType {
211
+ pub name: String,
212
+ pub logical_type: LogicalType,
213
+ }
214
+
215
+ #[derive(Clone, Debug, PartialEq, Eq, Default)]
216
+ pub struct CoordinateReferenceSystem {
217
+ pub definition: Option<String>,
218
+ }
219
+
220
+ #[derive(Clone, Debug, PartialEq, Eq)]
221
+ pub enum ExtraTypeInfo {
222
+ Invalid {
223
+ alias: Option<String>,
224
+ },
225
+ Generic {
226
+ alias: Option<String>,
227
+ },
228
+ Decimal {
229
+ alias: Option<String>,
230
+ width: u64,
231
+ scale: u64,
232
+ },
233
+ String {
234
+ alias: Option<String>,
235
+ collation: String,
236
+ },
237
+ List {
238
+ alias: Option<String>,
239
+ child_type: Box<LogicalType>,
240
+ },
241
+ Struct {
242
+ alias: Option<String>,
243
+ child_types: Vec<ChildType>,
244
+ },
245
+ Enum {
246
+ alias: Option<String>,
247
+ values: Vec<String>,
248
+ },
249
+ AggregateState {
250
+ alias: Option<String>,
251
+ function_name: String,
252
+ return_type: Box<LogicalType>,
253
+ bound_argument_types: Vec<LogicalType>,
254
+ },
255
+ Array {
256
+ alias: Option<String>,
257
+ child_type: Box<LogicalType>,
258
+ size: u64,
259
+ },
260
+ Any {
261
+ alias: Option<String>,
262
+ target_type: Box<LogicalType>,
263
+ cast_score: u64,
264
+ },
265
+ Template {
266
+ alias: Option<String>,
267
+ name: String,
268
+ },
269
+ IntegerLiteral {
270
+ alias: Option<String>,
271
+ },
272
+ Geo {
273
+ alias: Option<String>,
274
+ crs: CoordinateReferenceSystem,
275
+ },
276
+ Unbound {
277
+ alias: Option<String>,
278
+ name: Option<String>,
279
+ catalog: Option<String>,
280
+ schema: Option<String>,
281
+ },
282
+ }
283
+
284
+ impl ExtraTypeInfo {
285
+ pub(crate) fn type_id(&self) -> ExtraTypeInfoType {
286
+ match self {
287
+ Self::Invalid { .. } => ExtraTypeInfoType::Invalid,
288
+ Self::Generic { .. } => ExtraTypeInfoType::Generic,
289
+ Self::Decimal { .. } => ExtraTypeInfoType::Decimal,
290
+ Self::String { .. } => ExtraTypeInfoType::String,
291
+ Self::List { .. } => ExtraTypeInfoType::List,
292
+ Self::Struct { .. } => ExtraTypeInfoType::Struct,
293
+ Self::Enum { .. } => ExtraTypeInfoType::Enum,
294
+ Self::AggregateState { .. } => ExtraTypeInfoType::AggregateState,
295
+ Self::Array { .. } => ExtraTypeInfoType::Array,
296
+ Self::Any { .. } => ExtraTypeInfoType::Any,
297
+ Self::Template { .. } => ExtraTypeInfoType::Template,
298
+ Self::IntegerLiteral { .. } => ExtraTypeInfoType::IntegerLiteral,
299
+ Self::Geo { .. } => ExtraTypeInfoType::Geo,
300
+ Self::Unbound { .. } => ExtraTypeInfoType::Unbound,
301
+ }
302
+ }
303
+
304
+ pub fn alias(&self) -> Option<&str> {
305
+ match self {
306
+ Self::Invalid { alias }
307
+ | Self::Generic { alias }
308
+ | Self::Decimal { alias, .. }
309
+ | Self::String { alias, .. }
310
+ | Self::List { alias, .. }
311
+ | Self::Struct { alias, .. }
312
+ | Self::Enum { alias, .. }
313
+ | Self::AggregateState { alias, .. }
314
+ | Self::Array { alias, .. }
315
+ | Self::Any { alias, .. }
316
+ | Self::Template { alias, .. }
317
+ | Self::IntegerLiteral { alias }
318
+ | Self::Geo { alias, .. }
319
+ | Self::Unbound { alias, .. } => alias.as_deref(),
320
+ }
321
+ }
322
+ }
323
+
324
+ pub(crate) fn logical_type(id: LogicalTypeId) -> LogicalType {
325
+ LogicalType {
326
+ id,
327
+ type_info: None,
328
+ }
329
+ }
330
+
331
+ pub(crate) fn logical_type_with_info(id: LogicalTypeId, type_info: ExtraTypeInfo) -> LogicalType {
332
+ LogicalType {
333
+ id,
334
+ type_info: Some(type_info),
335
+ }
336
+ }
337
+
338
+ pub struct LogicalTypes;
339
+
340
+ impl LogicalTypes {
341
+ pub fn null() -> LogicalType {
342
+ logical_type(LogicalTypeId::SqlNull)
343
+ }
344
+ pub fn boolean() -> LogicalType {
345
+ logical_type(LogicalTypeId::Boolean)
346
+ }
347
+ pub fn tinyint() -> LogicalType {
348
+ logical_type(LogicalTypeId::TinyInt)
349
+ }
350
+ pub fn smallint() -> LogicalType {
351
+ logical_type(LogicalTypeId::SmallInt)
352
+ }
353
+ pub fn integer() -> LogicalType {
354
+ logical_type(LogicalTypeId::Integer)
355
+ }
356
+ pub fn bigint() -> LogicalType {
357
+ logical_type(LogicalTypeId::BigInt)
358
+ }
359
+ pub fn utinyint() -> LogicalType {
360
+ logical_type(LogicalTypeId::UTinyInt)
361
+ }
362
+ pub fn usmallint() -> LogicalType {
363
+ logical_type(LogicalTypeId::USmallInt)
364
+ }
365
+ pub fn uinteger() -> LogicalType {
366
+ logical_type(LogicalTypeId::UInteger)
367
+ }
368
+ pub fn ubigint() -> LogicalType {
369
+ logical_type(LogicalTypeId::UBigInt)
370
+ }
371
+ pub fn hugeint() -> LogicalType {
372
+ logical_type(LogicalTypeId::HugeInt)
373
+ }
374
+ pub fn uhugeint() -> LogicalType {
375
+ logical_type(LogicalTypeId::UHugeInt)
376
+ }
377
+ pub fn float() -> LogicalType {
378
+ logical_type(LogicalTypeId::Float)
379
+ }
380
+ pub fn double() -> LogicalType {
381
+ logical_type(LogicalTypeId::Double)
382
+ }
383
+ pub fn varchar() -> LogicalType {
384
+ logical_type(LogicalTypeId::Varchar)
385
+ }
386
+ pub fn blob() -> LogicalType {
387
+ logical_type(LogicalTypeId::Blob)
388
+ }
389
+ pub fn bit() -> LogicalType {
390
+ logical_type(LogicalTypeId::Bit)
391
+ }
392
+ pub fn uuid() -> LogicalType {
393
+ logical_type(LogicalTypeId::Uuid)
394
+ }
395
+ pub fn date() -> LogicalType {
396
+ logical_type(LogicalTypeId::Date)
397
+ }
398
+ pub fn time() -> LogicalType {
399
+ logical_type(LogicalTypeId::Time)
400
+ }
401
+ pub fn time_ns() -> LogicalType {
402
+ logical_type(LogicalTypeId::TimeNs)
403
+ }
404
+ pub fn time_tz() -> LogicalType {
405
+ logical_type(LogicalTypeId::TimeTz)
406
+ }
407
+ pub fn timestamp() -> LogicalType {
408
+ logical_type(LogicalTypeId::Timestamp)
409
+ }
410
+ pub fn timestamp_seconds() -> LogicalType {
411
+ logical_type(LogicalTypeId::TimestampSec)
412
+ }
413
+ pub fn timestamp_millis() -> LogicalType {
414
+ logical_type(LogicalTypeId::TimestampMs)
415
+ }
416
+ pub fn timestamp_nanos() -> LogicalType {
417
+ logical_type(LogicalTypeId::TimestampNs)
418
+ }
419
+ pub fn timestamp_tz() -> LogicalType {
420
+ logical_type(LogicalTypeId::TimestampTz)
421
+ }
422
+ pub fn interval() -> LogicalType {
423
+ logical_type(LogicalTypeId::Interval)
424
+ }
425
+
426
+ pub fn decimal(width: u64, scale: u64) -> LogicalType {
427
+ logical_type_with_info(
428
+ LogicalTypeId::Decimal,
429
+ ExtraTypeInfo::Decimal {
430
+ alias: None,
431
+ width,
432
+ scale,
433
+ },
434
+ )
435
+ }
436
+
437
+ pub fn list(child_type: LogicalType) -> LogicalType {
438
+ logical_type_with_info(
439
+ LogicalTypeId::List,
440
+ ExtraTypeInfo::List {
441
+ alias: None,
442
+ child_type: Box::new(child_type),
443
+ },
444
+ )
445
+ }
446
+
447
+ pub fn map(key_type: LogicalType, value_type: LogicalType) -> LogicalType {
448
+ Self::list(Self::r#struct(vec![
449
+ ChildType {
450
+ name: "key".to_string(),
451
+ logical_type: key_type,
452
+ },
453
+ ChildType {
454
+ name: "value".to_string(),
455
+ logical_type: value_type,
456
+ },
457
+ ]))
458
+ .with_id(LogicalTypeId::Map)
459
+ }
460
+
461
+ pub fn r#struct(child_types: Vec<ChildType>) -> LogicalType {
462
+ logical_type_with_info(
463
+ LogicalTypeId::Struct,
464
+ ExtraTypeInfo::Struct {
465
+ alias: None,
466
+ child_types,
467
+ },
468
+ )
469
+ }
470
+
471
+ pub fn array(child_type: LogicalType, size: u64) -> LogicalType {
472
+ logical_type_with_info(
473
+ LogicalTypeId::Array,
474
+ ExtraTypeInfo::Array {
475
+ alias: None,
476
+ child_type: Box::new(child_type),
477
+ size,
478
+ },
479
+ )
480
+ }
481
+
482
+ pub fn r#enum(values: Vec<String>) -> LogicalType {
483
+ logical_type_with_info(
484
+ LogicalTypeId::Enum,
485
+ ExtraTypeInfo::Enum {
486
+ alias: None,
487
+ values,
488
+ },
489
+ )
490
+ }
491
+
492
+ pub fn geometry(crs: Option<CoordinateReferenceSystem>) -> LogicalType {
493
+ match crs {
494
+ Some(crs) => logical_type_with_info(
495
+ LogicalTypeId::Geometry,
496
+ ExtraTypeInfo::Geo { alias: None, crs },
497
+ ),
498
+ None => logical_type(LogicalTypeId::Geometry),
499
+ }
500
+ }
501
+ }
502
+
503
+ impl LogicalType {
504
+ fn with_id(mut self, id: LogicalTypeId) -> Self {
505
+ self.id = id;
506
+ self
507
+ }
508
+ }
509
+
510
+ pub(crate) fn encode_logical_type(
511
+ writer: &mut BinaryWriter,
512
+ logical_type: &LogicalType,
513
+ ) -> Result<()> {
514
+ writer.write_object(|object| {
515
+ object.write_field(100, |object| object.write_uleb(logical_type.id as u64))?;
516
+ if let Some(info) = &logical_type.type_info {
517
+ object.write_field(101, |object| {
518
+ object.write_nullable(Some(info), encode_extra_type_info)
519
+ })?;
520
+ }
521
+ Ok(())
522
+ })
523
+ }
524
+
525
+ pub(crate) fn decode_logical_type(reader: &mut BinaryReader<'_>) -> Result<LogicalType> {
526
+ reader.read_object(|object| {
527
+ let id = LogicalTypeId::try_from(
528
+ object.read_required_field(100, |object| object.read_uleb_u64())?,
529
+ )?;
530
+ let type_info = object.read_optional_field(
531
+ 101,
532
+ |object| object.read_nullable(decode_extra_type_info),
533
+ None,
534
+ )?;
535
+ Ok(LogicalType { id, type_info })
536
+ })
537
+ }
538
+
539
+ pub(crate) fn encode_extra_type_info(
540
+ writer: &mut BinaryWriter,
541
+ info: &ExtraTypeInfo,
542
+ ) -> Result<()> {
543
+ writer.write_object(|object| {
544
+ object.write_field(100, |object| object.write_uleb(info.type_id() as u64))?;
545
+ if let Some(alias) = info.alias().filter(|alias| !alias.is_empty()) {
546
+ object.write_field(101, |object| object.write_string(alias))?;
547
+ }
548
+ match info {
549
+ ExtraTypeInfo::Invalid { .. } | ExtraTypeInfo::Generic { .. } => {}
550
+ ExtraTypeInfo::Decimal { width, scale, .. } => {
551
+ object.write_field(200, |object| object.write_uleb(*width))?;
552
+ object.write_field(201, |object| object.write_uleb(*scale))?;
553
+ }
554
+ ExtraTypeInfo::String { collation, .. } => {
555
+ object.write_field(200, |object| object.write_string(collation))?;
556
+ }
557
+ ExtraTypeInfo::List { child_type, .. } => {
558
+ object.write_field(200, |object| encode_logical_type(object, child_type))?;
559
+ }
560
+ ExtraTypeInfo::Struct { child_types, .. } => {
561
+ object.write_field(200, |object| encode_child_types(object, child_types))?;
562
+ }
563
+ ExtraTypeInfo::Enum { values, .. } => {
564
+ object.write_field(200, |object| object.write_uleb(values.len() as u64))?;
565
+ object.write_field(201, |object| {
566
+ object.write_list(values, |object, value, _| object.write_string(value))
567
+ })?;
568
+ }
569
+ ExtraTypeInfo::AggregateState {
570
+ function_name,
571
+ return_type,
572
+ bound_argument_types,
573
+ ..
574
+ } => {
575
+ object.write_field(200, |object| object.write_string(function_name))?;
576
+ object.write_field(201, |object| encode_logical_type(object, return_type))?;
577
+ object.write_field(202, |object| {
578
+ object.write_list(bound_argument_types, |object, logical_type, _| {
579
+ encode_logical_type(object, logical_type)
580
+ })
581
+ })?;
582
+ }
583
+ ExtraTypeInfo::Array {
584
+ child_type, size, ..
585
+ } => {
586
+ object.write_field(200, |object| encode_logical_type(object, child_type))?;
587
+ object.write_field(201, |object| object.write_uleb(*size))?;
588
+ }
589
+ ExtraTypeInfo::Any {
590
+ target_type,
591
+ cast_score,
592
+ ..
593
+ } => {
594
+ object.write_field(200, |object| encode_logical_type(object, target_type))?;
595
+ object.write_field(201, |object| object.write_uleb(*cast_score))?;
596
+ }
597
+ ExtraTypeInfo::Template { name, .. } => {
598
+ object.write_field(200, |object| object.write_string(name))?;
599
+ }
600
+ ExtraTypeInfo::IntegerLiteral { .. } => {
601
+ return Err(QuackError::unsupported(
602
+ "encoding INTEGER_LITERAL type metadata is not supported",
603
+ ));
604
+ }
605
+ ExtraTypeInfo::Geo { crs, .. } => {
606
+ object.write_field(200, |object| {
607
+ encode_coordinate_reference_system(object, crs)
608
+ })?;
609
+ }
610
+ ExtraTypeInfo::Unbound {
611
+ name,
612
+ catalog,
613
+ schema,
614
+ ..
615
+ } => {
616
+ if let Some(name) = name {
617
+ object.write_field(200, |object| object.write_string(name))?;
618
+ }
619
+ if let Some(catalog) = catalog {
620
+ object.write_field(201, |object| object.write_string(catalog))?;
621
+ }
622
+ if let Some(schema) = schema {
623
+ object.write_field(202, |object| object.write_string(schema))?;
624
+ }
625
+ }
626
+ }
627
+ Ok(())
628
+ })
629
+ }
630
+
631
+ pub(crate) fn decode_extra_type_info(reader: &mut BinaryReader<'_>) -> Result<ExtraTypeInfo> {
632
+ reader.read_object(|object| {
633
+ let type_id = ExtraTypeInfoType::try_from(object.read_required_field(100, |object| object.read_uleb_u64())?)?;
634
+ let alias = object.read_optional_field(
635
+ 101,
636
+ |object| Ok(Some(object.read_string()?)),
637
+ None::<String>,
638
+ )?;
639
+ if object.peek_field_id()? == 103 {
640
+ return Err(QuackError::unsupported(
641
+ "extension type metadata is not supported",
642
+ ));
643
+ }
644
+ Ok(match type_id {
645
+ ExtraTypeInfoType::Invalid => ExtraTypeInfo::Invalid { alias },
646
+ ExtraTypeInfoType::Generic => ExtraTypeInfo::Generic { alias },
647
+ ExtraTypeInfoType::Decimal => ExtraTypeInfo::Decimal {
648
+ alias,
649
+ width: object.read_optional_field(200, |object| object.read_uleb_u64(), 0)?,
650
+ scale: object.read_optional_field(201, |object| object.read_uleb_u64(), 0)?,
651
+ },
652
+ ExtraTypeInfoType::String => ExtraTypeInfo::String {
653
+ alias,
654
+ collation: object.read_optional_field(200, |object| object.read_string(), String::new())?,
655
+ },
656
+ ExtraTypeInfoType::List => ExtraTypeInfo::List {
657
+ alias,
658
+ child_type: Box::new(object.read_required_field(200, decode_logical_type)?),
659
+ },
660
+ ExtraTypeInfoType::Struct => ExtraTypeInfo::Struct {
661
+ alias,
662
+ child_types: object.read_optional_field(200, decode_child_types, Vec::new())?,
663
+ },
664
+ ExtraTypeInfoType::Enum => {
665
+ let values_count = object.read_required_field(200, |object| object.read_uleb_usize())?;
666
+ let values = object.read_required_field(201, |object| {
667
+ object.read_list(|object, _| object.read_string())
668
+ })?;
669
+ if values.len() != values_count {
670
+ return Err(QuackError::protocol(format!(
671
+ "ENUM metadata declared {values_count} values but serialized {}",
672
+ values.len()
673
+ )));
674
+ }
675
+ ExtraTypeInfo::Enum { alias, values }
676
+ }
677
+ ExtraTypeInfoType::AggregateState => ExtraTypeInfo::AggregateState {
678
+ alias,
679
+ function_name: object.read_required_field(200, |object| object.read_string())?,
680
+ return_type: Box::new(object.read_required_field(201, decode_logical_type)?),
681
+ bound_argument_types: object.read_required_field(202, |object| {
682
+ object.read_list(|object, _| decode_logical_type(object))
683
+ })?,
684
+ },
685
+ ExtraTypeInfoType::Array => ExtraTypeInfo::Array {
686
+ alias,
687
+ child_type: Box::new(object.read_required_field(200, decode_logical_type)?),
688
+ size: object.read_required_field(201, |object| object.read_uleb_u64())?,
689
+ },
690
+ ExtraTypeInfoType::Any => ExtraTypeInfo::Any {
691
+ alias,
692
+ target_type: Box::new(object.read_required_field(200, decode_logical_type)?),
693
+ cast_score: object.read_required_field(201, |object| object.read_uleb_u64())?,
694
+ },
695
+ ExtraTypeInfoType::IntegerLiteral => {
696
+ if object.peek_field_id()? == 200 {
697
+ return Err(QuackError::unsupported(
698
+ "INTEGER_LITERAL type metadata contains a DuckDB Value, which is not supported",
699
+ ));
700
+ }
701
+ ExtraTypeInfo::IntegerLiteral { alias }
702
+ }
703
+ ExtraTypeInfoType::Template => ExtraTypeInfo::Template {
704
+ alias,
705
+ name: object.read_optional_field(200, |object| object.read_string(), String::new())?,
706
+ },
707
+ ExtraTypeInfoType::Geo => ExtraTypeInfo::Geo {
708
+ alias,
709
+ crs: object.read_optional_field(
710
+ 200,
711
+ decode_coordinate_reference_system,
712
+ CoordinateReferenceSystem::default(),
713
+ )?,
714
+ },
715
+ ExtraTypeInfoType::Unbound => {
716
+ if object.peek_field_id()? == 204 {
717
+ return Err(QuackError::unsupported(
718
+ "UNBOUND type metadata contains a ParsedExpression, which is not supported",
719
+ ));
720
+ }
721
+ ExtraTypeInfo::Unbound {
722
+ alias,
723
+ name: object.read_optional_field(
724
+ 200,
725
+ |object| Ok(Some(object.read_string()?)),
726
+ None::<String>,
727
+ )?,
728
+ catalog: object.read_optional_field(
729
+ 201,
730
+ |object| Ok(Some(object.read_string()?)),
731
+ None::<String>,
732
+ )?,
733
+ schema: object.read_optional_field(
734
+ 202,
735
+ |object| Ok(Some(object.read_string()?)),
736
+ None::<String>,
737
+ )?,
738
+ }
739
+ }
740
+ })
741
+ })
742
+ }
743
+
744
+ pub(crate) fn get_physical_type(logical_type: &LogicalType) -> Result<PhysicalType> {
745
+ Ok(match logical_type.id {
746
+ LogicalTypeId::Boolean => PhysicalType::Bool,
747
+ LogicalTypeId::TinyInt => PhysicalType::Int8,
748
+ LogicalTypeId::UTinyInt => PhysicalType::UInt8,
749
+ LogicalTypeId::SmallInt => PhysicalType::Int16,
750
+ LogicalTypeId::USmallInt => PhysicalType::UInt16,
751
+ LogicalTypeId::SqlNull | LogicalTypeId::Date | LogicalTypeId::Integer => {
752
+ PhysicalType::Int32
753
+ }
754
+ LogicalTypeId::UInteger => PhysicalType::UInt32,
755
+ LogicalTypeId::BigInt
756
+ | LogicalTypeId::Time
757
+ | LogicalTypeId::TimeNs
758
+ | LogicalTypeId::Timestamp
759
+ | LogicalTypeId::TimestampSec
760
+ | LogicalTypeId::TimestampNs
761
+ | LogicalTypeId::TimestampMs
762
+ | LogicalTypeId::TimeTz
763
+ | LogicalTypeId::TimestampTz
764
+ | LogicalTypeId::TimestampTzNs => PhysicalType::Int64,
765
+ LogicalTypeId::UBigInt => PhysicalType::UInt64,
766
+ LogicalTypeId::UHugeInt => PhysicalType::UInt128,
767
+ LogicalTypeId::HugeInt | LogicalTypeId::Uuid => PhysicalType::Int128,
768
+ LogicalTypeId::Float => PhysicalType::Float,
769
+ LogicalTypeId::Double => PhysicalType::Double,
770
+ LogicalTypeId::Decimal => decimal_physical_type(logical_type)?,
771
+ LogicalTypeId::BigNum
772
+ | LogicalTypeId::Varchar
773
+ | LogicalTypeId::Char
774
+ | LogicalTypeId::Blob
775
+ | LogicalTypeId::Bit
776
+ | LogicalTypeId::Type
777
+ | LogicalTypeId::AggregateState
778
+ | LogicalTypeId::Geometry => PhysicalType::Varchar,
779
+ LogicalTypeId::Interval => PhysicalType::Interval,
780
+ LogicalTypeId::Union
781
+ | LogicalTypeId::Variant
782
+ | LogicalTypeId::Struct
783
+ | LogicalTypeId::Tuple => PhysicalType::Struct,
784
+ LogicalTypeId::List | LogicalTypeId::Map => PhysicalType::List,
785
+ LogicalTypeId::Array => PhysicalType::Array,
786
+ LogicalTypeId::Pointer => PhysicalType::UInt64,
787
+ LogicalTypeId::Validity => PhysicalType::Bit,
788
+ LogicalTypeId::Enum => enum_physical_type(logical_type)?,
789
+ _ => PhysicalType::Invalid,
790
+ })
791
+ }
792
+
793
+ pub(crate) fn is_constant_size_physical_type(physical_type: PhysicalType) -> bool {
794
+ matches!(
795
+ physical_type,
796
+ PhysicalType::Bool
797
+ | PhysicalType::UInt8
798
+ | PhysicalType::Int8
799
+ | PhysicalType::UInt16
800
+ | PhysicalType::Int16
801
+ | PhysicalType::UInt32
802
+ | PhysicalType::Int32
803
+ | PhysicalType::UInt64
804
+ | PhysicalType::Int64
805
+ | PhysicalType::Float
806
+ | PhysicalType::Double
807
+ | PhysicalType::Interval
808
+ | PhysicalType::UInt128
809
+ | PhysicalType::Int128
810
+ )
811
+ }
812
+
813
+ pub(crate) fn physical_type_size(physical_type: PhysicalType) -> Result<usize> {
814
+ Ok(match physical_type {
815
+ PhysicalType::Bool | PhysicalType::UInt8 | PhysicalType::Int8 => 1,
816
+ PhysicalType::UInt16 | PhysicalType::Int16 => 2,
817
+ PhysicalType::UInt32 | PhysicalType::Int32 | PhysicalType::Float => 4,
818
+ PhysicalType::UInt64 | PhysicalType::Int64 | PhysicalType::Double => 8,
819
+ PhysicalType::Interval | PhysicalType::UInt128 | PhysicalType::Int128 => 16,
820
+ _ => {
821
+ return Err(QuackError::protocol(format!(
822
+ "physical type {physical_type:?} is not fixed size"
823
+ )));
824
+ }
825
+ })
826
+ }
827
+
828
+ pub(crate) fn get_child_type(logical_type: &LogicalType) -> Result<&LogicalType> {
829
+ match logical_type.type_info.as_ref() {
830
+ Some(ExtraTypeInfo::List { child_type, .. })
831
+ | Some(ExtraTypeInfo::Array { child_type, .. }) => Ok(child_type),
832
+ _ => Err(QuackError::protocol(format!(
833
+ "logical type {:?} does not have a child type",
834
+ logical_type.id
835
+ ))),
836
+ }
837
+ }
838
+
839
+ pub(crate) fn get_struct_children(logical_type: &LogicalType) -> Result<&[ChildType]> {
840
+ match logical_type.type_info.as_ref() {
841
+ Some(ExtraTypeInfo::Struct { child_types, .. }) => Ok(child_types),
842
+ _ if matches!(
843
+ logical_type.id,
844
+ LogicalTypeId::Variant | LogicalTypeId::Union
845
+ ) =>
846
+ {
847
+ Ok(&[])
848
+ }
849
+ _ => Err(QuackError::protocol(format!(
850
+ "logical type {:?} does not have struct children",
851
+ logical_type.id
852
+ ))),
853
+ }
854
+ }
855
+
856
+ // Unnamed STRUCT/TUPLE fields share an empty wire name. Keep their ordinal
857
+ // identity in Value::Struct instead of overwriting earlier fields in the map.
858
+ pub(crate) fn struct_child_key(
859
+ logical_type: &LogicalType,
860
+ index: usize,
861
+ child: &ChildType,
862
+ ) -> String {
863
+ if matches!(
864
+ logical_type.id,
865
+ LogicalTypeId::Struct | LogicalTypeId::Tuple
866
+ ) && get_struct_children(logical_type)
867
+ .is_ok_and(|children| children.iter().all(|child| child.name.is_empty()))
868
+ {
869
+ index.to_string()
870
+ } else {
871
+ child.name.clone()
872
+ }
873
+ }
874
+
875
+ pub(crate) fn get_array_size(logical_type: &LogicalType) -> Result<u64> {
876
+ match logical_type.type_info.as_ref() {
877
+ Some(ExtraTypeInfo::Array { size, .. }) => Ok(*size),
878
+ _ => Err(QuackError::protocol(format!(
879
+ "logical type {:?} is not an ARRAY",
880
+ logical_type.id
881
+ ))),
882
+ }
883
+ }
884
+
885
+ pub(crate) fn get_decimal_width_and_scale(logical_type: &LogicalType) -> Result<(u64, u64)> {
886
+ match logical_type.type_info.as_ref() {
887
+ Some(ExtraTypeInfo::Decimal { width, scale, .. }) => Ok((*width, *scale)),
888
+ _ => Err(QuackError::protocol(
889
+ "DECIMAL type is missing DecimalTypeInfo",
890
+ )),
891
+ }
892
+ }
893
+
894
+ pub(crate) fn get_enum_values(logical_type: &LogicalType) -> Result<&[String]> {
895
+ match logical_type.type_info.as_ref() {
896
+ Some(ExtraTypeInfo::Enum { values, .. }) => Ok(values),
897
+ _ => Err(QuackError::protocol(format!(
898
+ "logical type {:?} is not an ENUM",
899
+ logical_type.id
900
+ ))),
901
+ }
902
+ }
903
+
904
+ fn encode_child_types(writer: &mut BinaryWriter, children: &[ChildType]) -> Result<()> {
905
+ writer.write_list(children, |writer, child, _| {
906
+ writer.write_object(|pair| {
907
+ pair.write_field(0, |pair| pair.write_string(&child.name))?;
908
+ pair.write_field(1, |pair| encode_logical_type(pair, &child.logical_type))?;
909
+ Ok(())
910
+ })
911
+ })
912
+ }
913
+
914
+ fn decode_child_types(reader: &mut BinaryReader<'_>) -> Result<Vec<ChildType>> {
915
+ reader.read_list(|reader, _| {
916
+ reader.read_object(|pair| {
917
+ Ok(ChildType {
918
+ name: pair.read_required_field(0, |pair| pair.read_string())?,
919
+ logical_type: pair.read_required_field(1, decode_logical_type)?,
920
+ })
921
+ })
922
+ })
923
+ }
924
+
925
+ fn encode_coordinate_reference_system(
926
+ writer: &mut BinaryWriter,
927
+ crs: &CoordinateReferenceSystem,
928
+ ) -> Result<()> {
929
+ writer.write_object(|object| {
930
+ if let Some(definition) = crs.definition.as_deref() {
931
+ object.write_field(100, |object| object.write_string(definition))?;
932
+ }
933
+ Ok(())
934
+ })
935
+ }
936
+
937
+ fn decode_coordinate_reference_system(
938
+ reader: &mut BinaryReader<'_>,
939
+ ) -> Result<CoordinateReferenceSystem> {
940
+ reader.read_object(|object| {
941
+ Ok(CoordinateReferenceSystem {
942
+ definition: object.read_optional_field(
943
+ 100,
944
+ |object| Ok(Some(object.read_string()?)),
945
+ None::<String>,
946
+ )?,
947
+ })
948
+ })
949
+ }
950
+
951
+ fn decimal_physical_type(logical_type: &LogicalType) -> Result<PhysicalType> {
952
+ let (width, _) = get_decimal_width_and_scale(logical_type)?;
953
+ Ok(if width <= 4 {
954
+ PhysicalType::Int16
955
+ } else if width <= 9 {
956
+ PhysicalType::Int32
957
+ } else if width <= 18 {
958
+ PhysicalType::Int64
959
+ } else if width <= 38 {
960
+ PhysicalType::Int128
961
+ } else {
962
+ return Err(QuackError::protocol(format!(
963
+ "unsupported DECIMAL width {width}"
964
+ )));
965
+ })
966
+ }
967
+
968
+ fn enum_physical_type(logical_type: &LogicalType) -> Result<PhysicalType> {
969
+ let value_count = get_enum_values(logical_type)?.len();
970
+ Ok(if value_count <= 0xff {
971
+ PhysicalType::UInt8
972
+ } else if value_count <= 0xffff {
973
+ PhysicalType::UInt16
974
+ } else {
975
+ PhysicalType::UInt32
976
+ })
977
+ }