Skip to main content

adbc_core/
options.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! Various option and configuration types.
19use std::{os::raw::c_int, str::FromStr};
20
21use crate::constants::{ADBC_OPTION_VALUE_DISABLED, ADBC_OPTION_VALUE_ENABLED};
22use crate::{
23    constants,
24    error::{Error, Status},
25};
26
27/// Option value.
28///
29/// Can be created with various implementations of [From].
30///
31/// # Note: Booleans
32/// ADBC passes booleans as the strings `"true"` ([`ADBC_OPTION_VALUE_ENABLED`])
33/// or `"false"` ([`ADBC_OPTION_VALUE_DISABLED`]).
34///
35/// To reflect this, instead of a special boolean variant, this type implements `From<bool>`
36/// and provides `TryFrom<OptionValue> for bool` which expects [`Self::String`].
37#[derive(Debug, Clone)]
38#[non_exhaustive]
39pub enum OptionValue {
40    String(String),
41    Bytes(Vec<u8>),
42    Int(i64),
43    Double(f64),
44}
45
46impl From<String> for OptionValue {
47    fn from(value: String) -> Self {
48        Self::String(value)
49    }
50}
51
52impl From<&str> for OptionValue {
53    fn from(value: &str) -> Self {
54        Self::String(value.into())
55    }
56}
57
58impl From<i64> for OptionValue {
59    fn from(value: i64) -> Self {
60        Self::Int(value)
61    }
62}
63
64impl From<f64> for OptionValue {
65    fn from(value: f64) -> Self {
66        Self::Double(value)
67    }
68}
69
70impl From<Vec<u8>> for OptionValue {
71    fn from(value: Vec<u8>) -> Self {
72        Self::Bytes(value)
73    }
74}
75
76impl From<&[u8]> for OptionValue {
77    fn from(value: &[u8]) -> Self {
78        Self::Bytes(value.into())
79    }
80}
81
82impl<const N: usize> From<[u8; N]> for OptionValue {
83    fn from(value: [u8; N]) -> Self {
84        Self::Bytes(value.into())
85    }
86}
87
88impl<const N: usize> From<&[u8; N]> for OptionValue {
89    fn from(value: &[u8; N]) -> Self {
90        Self::Bytes(value.into())
91    }
92}
93
94impl From<bool> for OptionValue {
95    /// Convert a boolean to the ADBC string equivalent.
96    ///
97    /// Returns `"true"` ([`ADBC_OPTION_VALUE_ENABLED`]) for `true`
98    /// or `"false"` ([`ADBC_OPTION_VALUE_DISABLED`]) for `false`.
99    fn from(value: bool) -> Self {
100        if value {
101            ADBC_OPTION_VALUE_ENABLED.into()
102        } else {
103            ADBC_OPTION_VALUE_DISABLED.into()
104        }
105    }
106}
107
108impl TryFrom<OptionValue> for bool {
109    type Error = Error;
110
111    /// Expects either `"true"` ([`ADBC_OPTION_VALUE_ENABLED`]) for `true`
112    /// or `"false"` ([`ADBC_OPTION_VALUE_DISABLED`]) for `false`.
113    ///
114    /// Returns an error with [`Status::InvalidArguments`] if any other string or value type.
115    fn try_from(value: OptionValue) -> Result<Self, Self::Error> {
116        // Forward to the borrowed implementation
117        <bool as TryFrom<&OptionValue>>::try_from(&value)
118    }
119}
120
121impl TryFrom<&OptionValue> for bool {
122    type Error = Error;
123
124    /// Expects either `"true"` ([`ADBC_OPTION_VALUE_ENABLED`]) for `true`
125    /// or `"false"` ([`ADBC_OPTION_VALUE_DISABLED`]) for `false`.
126    ///
127    /// Returns an error with [`Status::InvalidArguments`] if any other string or value type.
128    fn try_from(value: &OptionValue) -> Result<Self, Self::Error> {
129        match value {
130            OptionValue::String(value) if value == ADBC_OPTION_VALUE_ENABLED => Ok(true),
131            OptionValue::String(value) if value == ADBC_OPTION_VALUE_DISABLED => Ok(false),
132            _ => Err(Error::with_message_and_status(
133                format!(
134                    "expected {ADBC_OPTION_VALUE_ENABLED:?} or {ADBC_OPTION_VALUE_DISABLED:?}, \
135                     got {value:?}"
136                ),
137                Status::InvalidArguments,
138            )),
139        }
140    }
141}
142
143/// ADBC revision versions.
144///
145/// The [`Default`] implementation returns the latest version.
146#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
147#[non_exhaustive]
148pub enum AdbcVersion {
149    /// Version 1.0.0.
150    V100,
151    /// Version 1.1.0.
152    ///
153    /// <https://arrow.apache.org/adbc/current/format/specification.html#version-1-1-0>
154    #[default]
155    V110,
156}
157
158impl From<AdbcVersion> for c_int {
159    fn from(value: AdbcVersion) -> Self {
160        match value {
161            AdbcVersion::V100 => constants::ADBC_VERSION_1_0_0,
162            AdbcVersion::V110 => constants::ADBC_VERSION_1_1_0,
163        }
164    }
165}
166
167impl TryFrom<c_int> for AdbcVersion {
168    type Error = Error;
169    fn try_from(value: c_int) -> Result<Self, Self::Error> {
170        match value {
171            constants::ADBC_VERSION_1_0_0 => Ok(AdbcVersion::V100),
172            constants::ADBC_VERSION_1_1_0 => Ok(AdbcVersion::V110),
173            value => Err(Error::with_message_and_status(
174                format!("Unknown ADBC version: {value}"),
175                Status::InvalidArguments,
176            )),
177        }
178    }
179}
180
181impl FromStr for AdbcVersion {
182    type Err = Error;
183
184    fn from_str(s: &str) -> Result<Self, Self::Err> {
185        match s {
186            "1.0.0" | "1_0_0" | "100" => Ok(AdbcVersion::V100),
187            "1.1.0" | "1_1_0" | "110" => Ok(AdbcVersion::V110),
188            value => Err(Error::with_message_and_status(
189                format!("Unknown ADBC version: {value}"),
190                Status::InvalidArguments,
191            )),
192        }
193    }
194}
195
196/// Info codes for database/driver metadata.
197#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
198#[non_exhaustive]
199pub enum InfoCode {
200    /// The database vendor/product name (type: utf8).
201    VendorName,
202    /// The database vendor/product version (type: utf8).
203    VendorVersion,
204    /// The database vendor/product Arrow library version (type: utf8).
205    VendorArrowVersion,
206    /// Indicates whether SQL queries are supported (type: bool).
207    VendorSql,
208    /// Indicates whether Substrait queries are supported (type: bool).
209    VendorSubstrait,
210    /// The minimum supported Substrait version, or null if Substrait is not supported (type: utf8).
211    VendorSubstraitMinVersion,
212    /// The maximum supported Substrait version, or null if Substrait is not supported (type: utf8).
213    VendorSubstraitMaxVersion,
214    /// The driver name (type: utf8).
215    DriverName,
216    /// The driver version (type: utf8).
217    DriverVersion,
218    /// The driver Arrow library version (type: utf8).
219    DriverArrowVersion,
220    /// The driver ADBC API version (type: int64).
221    ///
222    /// # Since
223    ///
224    /// ADBC API revision 1.1.0
225    DriverAdbcVersion,
226    /// Any other info code than the ones listed above. The value is the raw code.
227    ///
228    /// Codes `[0, 10_000)` are reserved for ADBC usage (of which `[500, 1_000)`
229    /// is reserved for "XDBC" information since ADBC API revision 1.1.0), while
230    /// codes `10_000` and higher are driver/vendor-specific. Drivers ignore
231    /// requests for codes they don't recognize (the row is omitted from the
232    /// result).
233    ///
234    /// Note: to keep equality and hashing consistent, this variant should never
235    /// be constructed with the value of a code that has a dedicated variant;
236    /// use [InfoCode::from] to convert from a raw `u32`, which only produces
237    /// `Other` for values without one.
238    Other(u32),
239}
240
241impl From<&InfoCode> for u32 {
242    fn from(value: &InfoCode) -> Self {
243        match value {
244            InfoCode::VendorName => constants::ADBC_INFO_VENDOR_NAME,
245            InfoCode::VendorVersion => constants::ADBC_INFO_VENDOR_VERSION,
246            InfoCode::VendorArrowVersion => constants::ADBC_INFO_VENDOR_ARROW_VERSION,
247            InfoCode::VendorSql => constants::ADBC_INFO_VENDOR_SQL,
248            InfoCode::VendorSubstrait => constants::ADBC_INFO_VENDOR_SUBSTRAIT,
249            InfoCode::VendorSubstraitMinVersion => {
250                constants::ADBC_INFO_VENDOR_SUBSTRAIT_MIN_VERSION
251            }
252            InfoCode::VendorSubstraitMaxVersion => {
253                constants::ADBC_INFO_VENDOR_SUBSTRAIT_MAX_VERSION
254            }
255            InfoCode::DriverName => constants::ADBC_INFO_DRIVER_NAME,
256            InfoCode::DriverVersion => constants::ADBC_INFO_DRIVER_VERSION,
257            InfoCode::DriverArrowVersion => constants::ADBC_INFO_DRIVER_ARROW_VERSION,
258            InfoCode::DriverAdbcVersion => constants::ADBC_INFO_DRIVER_ADBC_VERSION,
259            InfoCode::Other(v) => *v,
260        }
261    }
262}
263
264impl From<u32> for InfoCode {
265    fn from(value: u32) -> Self {
266        match value {
267            constants::ADBC_INFO_VENDOR_NAME => InfoCode::VendorName,
268            constants::ADBC_INFO_VENDOR_VERSION => InfoCode::VendorVersion,
269            constants::ADBC_INFO_VENDOR_ARROW_VERSION => InfoCode::VendorArrowVersion,
270            constants::ADBC_INFO_VENDOR_SQL => InfoCode::VendorSql,
271            constants::ADBC_INFO_VENDOR_SUBSTRAIT => InfoCode::VendorSubstrait,
272            constants::ADBC_INFO_VENDOR_SUBSTRAIT_MIN_VERSION => {
273                InfoCode::VendorSubstraitMinVersion
274            }
275            constants::ADBC_INFO_VENDOR_SUBSTRAIT_MAX_VERSION => {
276                InfoCode::VendorSubstraitMaxVersion
277            }
278            constants::ADBC_INFO_DRIVER_NAME => InfoCode::DriverName,
279            constants::ADBC_INFO_DRIVER_VERSION => InfoCode::DriverVersion,
280            constants::ADBC_INFO_DRIVER_ARROW_VERSION => InfoCode::DriverArrowVersion,
281            constants::ADBC_INFO_DRIVER_ADBC_VERSION => InfoCode::DriverAdbcVersion,
282            v => InfoCode::Other(v),
283        }
284    }
285}
286
287/// Depth parameter for [get_objects][crate::Connection::get_objects] method.
288#[derive(Debug, Clone, Copy, Eq, PartialEq)]
289pub enum ObjectDepth {
290    /// Catalogs, schemas, tables, and columns.
291    All,
292    /// Catalogs only.
293    Catalogs,
294    /// Catalogs and schemas.
295    Schemas,
296    /// Catalogs, schemas, and tables.
297    Tables,
298    /// Catalogs, schemas, tables, and columns. Identical to [ObjectDepth::All].
299    Columns,
300}
301
302impl From<ObjectDepth> for c_int {
303    fn from(value: ObjectDepth) -> Self {
304        match value {
305            ObjectDepth::All => constants::ADBC_OBJECT_DEPTH_ALL,
306            ObjectDepth::Catalogs => constants::ADBC_OBJECT_DEPTH_CATALOGS,
307            ObjectDepth::Schemas => constants::ADBC_OBJECT_DEPTH_DB_SCHEMAS,
308            ObjectDepth::Tables => constants::ADBC_OBJECT_DEPTH_TABLES,
309            ObjectDepth::Columns => constants::ADBC_OBJECT_DEPTH_COLUMNS,
310        }
311    }
312}
313
314impl TryFrom<c_int> for ObjectDepth {
315    type Error = Error;
316
317    fn try_from(value: c_int) -> Result<Self, Error> {
318        match value {
319            constants::ADBC_OBJECT_DEPTH_ALL => Ok(ObjectDepth::All),
320            constants::ADBC_OBJECT_DEPTH_CATALOGS => Ok(ObjectDepth::Catalogs),
321            constants::ADBC_OBJECT_DEPTH_DB_SCHEMAS => Ok(ObjectDepth::Schemas),
322            constants::ADBC_OBJECT_DEPTH_TABLES => Ok(ObjectDepth::Tables),
323            v => Err(Error::with_message_and_status(
324                format!("Unknown object depth: {v}"),
325                Status::InvalidData,
326            )),
327        }
328    }
329}
330
331/// Database option key.
332#[derive(PartialEq, Eq, Hash, Debug, Clone)]
333#[non_exhaustive]
334pub enum OptionDatabase {
335    /// Canonical option key for URIs.
336    ///
337    /// # Since
338    ///
339    /// ADBC API revision 1.1.0
340    Uri,
341    /// Canonical option key for usernames.
342    ///
343    /// # Since
344    ///
345    /// ADBC API revision 1.1.0
346    Username,
347    /// Canonical option key for passwords.
348    ///
349    /// # Since
350    ///
351    /// ADBC API revision 1.1.0
352    Password,
353    /// Driver-specific key.
354    Other(String),
355}
356
357impl AsRef<str> for OptionDatabase {
358    fn as_ref(&self) -> &str {
359        match self {
360            Self::Uri => constants::ADBC_OPTION_URI,
361            Self::Username => constants::ADBC_OPTION_USERNAME,
362            Self::Password => constants::ADBC_OPTION_PASSWORD,
363            Self::Other(key) => key,
364        }
365    }
366}
367
368impl From<&str> for OptionDatabase {
369    fn from(value: &str) -> Self {
370        match value {
371            constants::ADBC_OPTION_URI => Self::Uri,
372            constants::ADBC_OPTION_USERNAME => Self::Username,
373            constants::ADBC_OPTION_PASSWORD => Self::Password,
374            key => Self::Other(key.into()),
375        }
376    }
377}
378
379/// Connection option key.
380#[derive(PartialEq, Eq, Hash, Debug, Clone)]
381#[non_exhaustive]
382pub enum OptionConnection {
383    /// Whether autocommit is enabled.
384    AutoCommit,
385    /// Whether the current connection should be restricted to being read-only.
386    ReadOnly,
387    /// The catalog used by the connection.
388    /// # Since
389    /// ADBC API revision 1.1.0
390    CurrentCatalog,
391    /// The database schema used by the connection.
392    /// # Since
393    /// ADBC API revision 1.1.0
394    CurrentSchema,
395    /// The isolation level of the connection. See [IsolationLevel].
396    IsolationLevel,
397    /// Driver-specific key.
398    Other(String),
399}
400
401impl AsRef<str> for OptionConnection {
402    fn as_ref(&self) -> &str {
403        match self {
404            Self::AutoCommit => constants::ADBC_CONNECTION_OPTION_AUTOCOMMIT,
405            Self::ReadOnly => constants::ADBC_CONNECTION_OPTION_READ_ONLY,
406            Self::CurrentCatalog => constants::ADBC_CONNECTION_OPTION_CURRENT_CATALOG,
407            Self::CurrentSchema => constants::ADBC_CONNECTION_OPTION_CURRENT_DB_SCHEMA,
408            Self::IsolationLevel => constants::ADBC_CONNECTION_OPTION_ISOLATION_LEVEL,
409            Self::Other(key) => key,
410        }
411    }
412}
413
414impl From<&str> for OptionConnection {
415    fn from(value: &str) -> Self {
416        match value {
417            constants::ADBC_CONNECTION_OPTION_AUTOCOMMIT => Self::AutoCommit,
418            constants::ADBC_CONNECTION_OPTION_READ_ONLY => Self::ReadOnly,
419            constants::ADBC_CONNECTION_OPTION_CURRENT_CATALOG => Self::CurrentCatalog,
420            constants::ADBC_CONNECTION_OPTION_CURRENT_DB_SCHEMA => Self::CurrentSchema,
421            constants::ADBC_CONNECTION_OPTION_ISOLATION_LEVEL => Self::IsolationLevel,
422            key => Self::Other(key.into()),
423        }
424    }
425}
426
427/// Statement option key.
428#[derive(PartialEq, Eq, Hash, Debug, Clone)]
429#[non_exhaustive]
430pub enum OptionStatement {
431    /// The ingest mode for a bulk insert. See [IngestMode].
432    IngestMode,
433    /// The name of the target table for a bulk insert.
434    TargetTable,
435    /// The catalog of the table for bulk insert.
436    TargetCatalog,
437    /// The schema of the table for bulk insert.
438    TargetDbSchema,
439    /// Use a temporary table for ingestion.
440    Temporary,
441    /// Whether query execution is nonblocking. By default, execution is blocking.
442    ///
443    /// When enabled, [execute_partitions][crate::Statement::execute_partitions]
444    /// will return partitions as soon as they are available, instead of returning
445    /// them all at the end. When there are no more to return, it will return an
446    /// empty set of partitions. The methods [execute][crate::Statement::execute],
447    /// [execute_schema][crate::Statement::execute_schema] and
448    /// [execute_update][crate::Statement::execute_update] are not affected.
449    ///
450    /// # Since
451    ///
452    /// ADBC API revision 1.1.0
453    Incremental,
454    /// Get the progress of a query. It's a read-only option that should be
455    /// read with [get_option_double][crate::Optionable::get_option_double].
456    ///
457    /// The value is not necessarily in any particular range or have any
458    /// particular units. For example, it might be a percentage, bytes of data,
459    /// rows of data, number of workers, etc. The max value can be retrieved
460    /// via [OptionStatement::MaxProgress]. This represents the progress of
461    /// execution, not of consumption (i.e., it is independent of how much of the
462    /// result set has been read by the client).
463    ///
464    /// # Since
465    ///
466    /// ADBC API revision 1.1.0
467    Progress,
468    /// Get the maximum progress of a query. It's a read-only option that should be
469    /// read with [get_option_double][crate::Optionable::get_option_double].
470    ///
471    /// This is the value of [OptionStatement::Progress] for a completed query.
472    /// If not supported, or if the value is nonpositive, then the maximum is not
473    /// known. For instance, the query may be fully streaming and the driver
474    /// does not know when the result set will end.
475    ///
476    /// # Since
477    ///
478    /// ADBC API revision 1.1.0
479    MaxProgress,
480    /// Driver-specific key.
481    Other(String),
482}
483
484impl AsRef<str> for OptionStatement {
485    fn as_ref(&self) -> &str {
486        match self {
487            Self::IngestMode => constants::ADBC_INGEST_OPTION_MODE,
488            Self::TargetTable => constants::ADBC_INGEST_OPTION_TARGET_TABLE,
489            Self::TargetCatalog => constants::ADBC_INGEST_OPTION_TARGET_CATALOG,
490            Self::TargetDbSchema => constants::ADBC_INGEST_OPTION_TARGET_DB_SCHEMA,
491            Self::Temporary => constants::ADBC_INGEST_OPTION_TEMPORARY,
492            Self::Incremental => constants::ADBC_STATEMENT_OPTION_INCREMENTAL,
493            Self::Progress => constants::ADBC_STATEMENT_OPTION_PROGRESS,
494            Self::MaxProgress => constants::ADBC_STATEMENT_OPTION_MAX_PROGRESS,
495            Self::Other(key) => key,
496        }
497    }
498}
499
500impl From<&str> for OptionStatement {
501    fn from(value: &str) -> Self {
502        match value {
503            constants::ADBC_INGEST_OPTION_MODE => Self::IngestMode,
504            constants::ADBC_INGEST_OPTION_TARGET_TABLE => Self::TargetTable,
505            constants::ADBC_INGEST_OPTION_TARGET_CATALOG => Self::TargetCatalog,
506            constants::ADBC_INGEST_OPTION_TARGET_DB_SCHEMA => Self::TargetDbSchema,
507            constants::ADBC_INGEST_OPTION_TEMPORARY => Self::Temporary,
508            constants::ADBC_STATEMENT_OPTION_INCREMENTAL => Self::Incremental,
509            constants::ADBC_STATEMENT_OPTION_PROGRESS => Self::Progress,
510            constants::ADBC_STATEMENT_OPTION_MAX_PROGRESS => Self::MaxProgress,
511            key => Self::Other(key.into()),
512        }
513    }
514}
515
516/// Isolation level value for key [OptionConnection::IsolationLevel].
517#[derive(Debug)]
518pub enum IsolationLevel {
519    /// Use database or driver default isolation level.
520    Default,
521    /// The lowest isolation level. Dirty reads are allowed, so one transaction
522    /// may see not-yet-committed changes made by others.
523    ReadUncommitted,
524    /// Lock-based concurrency control keeps write locks until the end of the
525    /// transaction, but read locks are released as soon as a SELECT is
526    /// performed. Non-repeatable reads can occur in this isolation level.
527    ///
528    /// More simply put, `ReadCommitted` is an isolation level that guarantees
529    /// that any data read is committed at the moment it is read. It simply
530    /// restricts the reader from seeing any intermediate, uncommitted,
531    /// 'dirty' reads. It makes no promise whatsoever that if the transaction
532    /// re-issues the read, it will find the same data; data is free to change
533    /// after it is read.
534    ReadCommitted,
535    /// Lock-based concurrency control keeps read AND write locks (acquired on
536    /// selection data) until the end of the transaction.
537    ///
538    /// However, range-locks are not managed, so phantom reads can occur.
539    /// Write skew is possible at this isolation level in some systems.
540    RepeatableRead,
541    /// This isolation guarantees that all reads in the transaction will see a
542    /// consistent snapshot of the database and the transaction should only
543    /// successfully commit if no updates conflict with any concurrent updates
544    /// made since that snapshot.
545    Snapshot,
546    /// Serializability requires read and write locks to be released only at the
547    /// end of the transaction. This includes acquiring range-locks when a
548    /// select query uses a ranged WHERE clause to avoid phantom reads.
549    Serializable,
550    /// The central distinction between serializability and linearizability is
551    /// that serializability is a global property; a property of an entire
552    /// history of operations and transactions. Linearizability is a local
553    /// property; a property of a single operation/transaction.
554    ///
555    /// Linearizability can be viewed as a special case of strict serializability
556    /// where transactions are restricted to consist of a single operation applied
557    /// to a single object.
558    Linearizable,
559}
560
561impl From<IsolationLevel> for String {
562    fn from(value: IsolationLevel) -> Self {
563        match value {
564            IsolationLevel::Default => constants::ADBC_OPTION_ISOLATION_LEVEL_DEFAULT.into(),
565            IsolationLevel::ReadUncommitted => {
566                constants::ADBC_OPTION_ISOLATION_LEVEL_READ_UNCOMMITTED.into()
567            }
568            IsolationLevel::ReadCommitted => {
569                constants::ADBC_OPTION_ISOLATION_LEVEL_READ_COMMITTED.into()
570            }
571            IsolationLevel::RepeatableRead => {
572                constants::ADBC_OPTION_ISOLATION_LEVEL_REPEATABLE_READ.into()
573            }
574            IsolationLevel::Snapshot => constants::ADBC_OPTION_ISOLATION_LEVEL_SNAPSHOT.into(),
575            IsolationLevel::Serializable => {
576                constants::ADBC_OPTION_ISOLATION_LEVEL_SERIALIZABLE.into()
577            }
578            IsolationLevel::Linearizable => {
579                constants::ADBC_OPTION_ISOLATION_LEVEL_LINEARIZABLE.into()
580            }
581        }
582    }
583}
584
585impl From<IsolationLevel> for OptionValue {
586    fn from(value: IsolationLevel) -> Self {
587        Self::String(value.into())
588    }
589}
590
591/// Ingestion mode value for key [OptionStatement::IngestMode].
592#[derive(Debug, Clone, Copy, Eq, PartialEq)]
593pub enum IngestMode {
594    /// Create the table and insert data; error if the table exists.
595    Create,
596    /// Do not create the table, and insert data; error if the table does not
597    /// exist ([Status::NotFound]) or does not match the schema of the data to
598    /// append ([Status::AlreadyExists]).
599    Append,
600    /// Create the table and insert data; drop the original table if it already
601    /// exists.
602    ///
603    /// # Since
604    ///
605    /// ADBC API revision 1.1.0
606    Replace,
607    /// Insert data; create the table if it does not exist, or error if the
608    /// table exists, but the schema does not match the schema of the data to
609    /// append ([Status::AlreadyExists]).
610    ///
611    /// # Since
612    ///
613    /// ADBC API revision 1.1.0
614    CreateAppend,
615}
616
617impl From<IngestMode> for String {
618    fn from(value: IngestMode) -> Self {
619        match value {
620            IngestMode::Create => constants::ADBC_INGEST_OPTION_MODE_CREATE.into(),
621            IngestMode::Append => constants::ADBC_INGEST_OPTION_MODE_APPEND.into(),
622            IngestMode::Replace => constants::ADBC_INGEST_OPTION_MODE_REPLACE.into(),
623            IngestMode::CreateAppend => constants::ADBC_INGEST_OPTION_MODE_CREATE_APPEND.into(),
624        }
625    }
626}
627impl From<IngestMode> for OptionValue {
628    fn from(value: IngestMode) -> Self {
629        Self::String(value.into())
630    }
631}
632
633impl FromStr for IngestMode {
634    type Err = Error;
635
636    fn from_str(s: &str) -> Result<Self, Self::Err> {
637        match s {
638            constants::ADBC_INGEST_OPTION_MODE_CREATE => Ok(Self::Create),
639            constants::ADBC_INGEST_OPTION_MODE_APPEND => Ok(Self::Append),
640            constants::ADBC_INGEST_OPTION_MODE_REPLACE => Ok(Self::Replace),
641            constants::ADBC_INGEST_OPTION_MODE_CREATE_APPEND => Ok(Self::CreateAppend),
642            other => Err(Error::with_message_and_status(
643                format!(
644                    "invalid value for option {:?}: {other:?}",
645                    constants::ADBC_INGEST_OPTION_MODE
646                ),
647                Status::InvalidArguments,
648            )),
649        }
650    }
651}
652
653impl TryFrom<&OptionValue> for IngestMode {
654    type Error = Error;
655
656    fn try_from(value: &OptionValue) -> Result<Self, Self::Error> {
657        match value {
658            OptionValue::String(s) => s.parse(),
659            other => Err(Error::with_message_and_status(
660                format!(
661                    "invalid value type for option {:?}: {other:?}",
662                    constants::ADBC_INGEST_OPTION_MODE
663                ),
664                Status::InvalidArguments,
665            )),
666        }
667    }
668}
669
670/// Statistics about the data distribution.
671#[derive(Debug, Clone)]
672pub enum Statistics {
673    /// The average byte width statistic. The average size in bytes of a row in
674    /// the column. Value type is `float64`. For example, this is roughly the
675    /// average length of a string for a string column.
676    AverageByteWidth,
677    /// The distinct value count (NDV) statistic. The number of distinct values
678    /// in the column. Value type is `int64` (when not approximate) or `float64`
679    /// (when approximate).
680    DistinctCount,
681    /// The max byte width statistic. The maximum size in bytes of a row in the
682    /// column. Value type is `int64` (when not approximate) or `float64` (when approximate).
683    /// For example, this is the maximum length of a string for a string column.
684    MaxByteWidth,
685    /// The max value statistic. Value type is column-dependent.
686    MaxValue,
687    /// The min value statistic. Value type is column-dependent.
688    MinValue,
689    /// The null count statistic. The number of values that are null in the
690    /// column. Value type is `int64` (when not approximate) or `float64` (when approximate).
691    NullCount,
692    /// The row count statistic. The number of rows in the column or table.
693    /// Value type is `int64` (when not approximate) or `float64` (when approximate).
694    RowCount,
695    /// Driver-specific statistics.
696    Other { key: i16, name: String },
697}
698
699impl TryFrom<i16> for Statistics {
700    type Error = Error;
701    fn try_from(value: i16) -> Result<Self, Self::Error> {
702        match value {
703            constants::ADBC_STATISTIC_AVERAGE_BYTE_WIDTH_KEY => Ok(Self::AverageByteWidth),
704            constants::ADBC_STATISTIC_DISTINCT_COUNT_KEY => Ok(Self::DistinctCount),
705            constants::ADBC_STATISTIC_MAX_BYTE_WIDTH_KEY => Ok(Self::MaxByteWidth),
706            constants::ADBC_STATISTIC_MAX_VALUE_KEY => Ok(Self::MaxValue),
707            constants::ADBC_STATISTIC_MIN_VALUE_KEY => Ok(Self::MinValue),
708            constants::ADBC_STATISTIC_NULL_COUNT_KEY => Ok(Self::NullCount),
709            constants::ADBC_STATISTIC_ROW_COUNT_KEY => Ok(Self::RowCount),
710            _ => Err(Error::with_message_and_status(
711                format!("Unknown standard statistic key: {value}"),
712                Status::InvalidArguments,
713            )),
714        }
715    }
716}
717
718impl From<Statistics> for i16 {
719    fn from(value: Statistics) -> Self {
720        match value {
721            Statistics::AverageByteWidth => constants::ADBC_STATISTIC_AVERAGE_BYTE_WIDTH_KEY,
722            Statistics::DistinctCount => constants::ADBC_STATISTIC_DISTINCT_COUNT_KEY,
723            Statistics::MaxByteWidth => constants::ADBC_STATISTIC_MAX_BYTE_WIDTH_KEY,
724            Statistics::MaxValue => constants::ADBC_STATISTIC_MAX_VALUE_KEY,
725            Statistics::MinValue => constants::ADBC_STATISTIC_MIN_VALUE_KEY,
726            Statistics::NullCount => constants::ADBC_STATISTIC_NULL_COUNT_KEY,
727            Statistics::RowCount => constants::ADBC_STATISTIC_ROW_COUNT_KEY,
728            Statistics::Other { key, name: _ } => key,
729        }
730    }
731}
732
733impl AsRef<str> for Statistics {
734    fn as_ref(&self) -> &str {
735        match self {
736            Statistics::AverageByteWidth => constants::ADBC_STATISTIC_AVERAGE_BYTE_WIDTH_NAME,
737            Statistics::DistinctCount => constants::ADBC_STATISTIC_DISTINCT_COUNT_NAME,
738            Statistics::MaxByteWidth => constants::ADBC_STATISTIC_MAX_BYTE_WIDTH_NAME,
739            Statistics::MaxValue => constants::ADBC_STATISTIC_MAX_VALUE_NAME,
740            Statistics::MinValue => constants::ADBC_STATISTIC_MIN_VALUE_NAME,
741            Statistics::NullCount => constants::ADBC_STATISTIC_NULL_COUNT_NAME,
742            Statistics::RowCount => constants::ADBC_STATISTIC_ROW_COUNT_NAME,
743            Statistics::Other { key: _, name } => name,
744        }
745    }
746}
747
748impl std::fmt::Display for Statistics {
749    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
750        write!(f, "{}", self.as_ref())
751    }
752}
753
754#[cfg(test)]
755mod tests {
756    use super::*;
757
758    #[test]
759    fn info_code_u32_roundtrip() {
760        for code in (0..20_000_u32).chain([u32::MAX]) {
761            assert_eq!(u32::from(&InfoCode::from(code)), code);
762        }
763        assert_eq!(
764            InfoCode::from(constants::ADBC_INFO_VENDOR_NAME),
765            InfoCode::VendorName
766        );
767        assert_eq!(InfoCode::from(10_042), InfoCode::Other(10_042));
768    }
769}