adbc_snowflake 0.23.0

Snowflake Arrow Database Connectivity (ADBC) driver
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

//! Snowflake ADBC Database
//!
//!

use std::{collections::HashSet, ffi::c_int, sync::Arc};

use adbc_core::{
    error::{Error, Result, Status},
    options::{AdbcVersion, InfoCode, OptionConnection, OptionDatabase, OptionValue},
    Connection as _, Database as _, Optionable,
};
use adbc_driver_manager::ManagedDatabase;
use arrow_array::{
    cast::AsArray,
    types::{Int64Type, UInt32Type},
    Array,
};

use crate::Connection;

mod builder;
pub use builder::*;

/// Snowflake ADBC Database.
#[derive(Clone)]
pub struct Database(pub(crate) ManagedDatabase);

impl Database {
    fn get_info(&self, info_code: InfoCode) -> Result<Arc<dyn Array>> {
        self.new_connection()?
            .get_info(Some(HashSet::from_iter([info_code])))?
            .next()
            .ok_or(Error::with_message_and_status(
                "failed to get info",
                Status::Internal,
            ))?
            .map_err(Into::into)
            .and_then(|record_batch| {
                if InfoCode::try_from(record_batch.column(0).as_primitive::<UInt32Type>().value(0))?
                    == info_code
                {
                    Ok(record_batch.column(1).as_union().value(0))
                } else {
                    Err(Error::with_message_and_status(
                        "invalid get info reply",
                        Status::Internal,
                    ))
                }
            })
    }

    /// Returns the name of the vendor.
    pub fn vendor_name(&mut self) -> Result<String> {
        self.get_info(InfoCode::VendorName)
            .map(|array| array.as_string::<i32>().value(0).to_owned())
    }

    /// Returns the version of the vendor.
    pub fn vendor_version(&mut self) -> Result<String> {
        self.get_info(InfoCode::VendorVersion)
            .map(|array| array.as_string::<i32>().value(0).to_owned())
    }

    /// Returns the Arrow version of the vendor.
    pub fn vendor_arrow_version(&mut self) -> Result<String> {
        self.get_info(InfoCode::VendorArrowVersion)
            .map(|array| array.as_string::<i32>().value(0).to_owned())
    }

    /// Returns true if SQL queries are supported.
    pub fn vendor_sql(&mut self) -> Result<bool> {
        self.get_info(InfoCode::VendorSql)
            .map(|array| array.as_boolean().value(0))
    }

    /// Returns true if Substrait queries are supported.
    pub fn vendor_substrait(&mut self) -> Result<bool> {
        self.get_info(InfoCode::VendorSubstrait)
            .map(|array| array.as_boolean().value(0))
    }

    /// Returns the name of the wrapped Go driver.
    pub fn driver_name(&mut self) -> Result<String> {
        self.get_info(InfoCode::DriverName)
            .map(|array| array.as_string::<i32>().value(0).to_owned())
    }

    /// Returns the version of the wrapped Go driver.
    pub fn driver_version(&mut self) -> Result<String> {
        self.get_info(InfoCode::DriverVersion)
            .map(|array| array.as_string::<i32>().value(0).to_owned())
    }

    /// Returns the Arrow version of the wrapped Go driver.
    pub fn driver_arrow_version(&mut self) -> Result<String> {
        self.get_info(InfoCode::DriverArrowVersion)
            .map(|array| array.as_string::<i32>().value(0).to_owned())
    }

    /// Returns the [`AdbcVersion`] reported by the driver.
    pub fn adbc_version(&self) -> Result<AdbcVersion> {
        self.new_connection()?
            .get_info(Some(HashSet::from_iter([InfoCode::DriverAdbcVersion])))?
            .next()
            .ok_or(Error::with_message_and_status(
                "failed to get info",
                Status::Internal,
            ))?
            .map_err(Into::into)
            .and_then(|record_batch| {
                assert_eq!(
                    record_batch.column(0).as_primitive::<UInt32Type>().value(0),
                    u32::from(&InfoCode::DriverAdbcVersion)
                );
                AdbcVersion::try_from(
                    record_batch
                        .column(1)
                        .as_union()
                        .value(0)
                        .as_primitive::<Int64Type>()
                        .value(0) as c_int,
                )
            })
    }
}

impl Optionable for Database {
    type Option = OptionDatabase;

    fn set_option(&mut self, key: Self::Option, value: OptionValue) -> Result<()> {
        self.0.set_option(key, value)
    }

    fn get_option_string(&self, key: Self::Option) -> Result<String> {
        self.0.get_option_string(key)
    }

    fn get_option_bytes(&self, key: Self::Option) -> Result<Vec<u8>> {
        self.0.get_option_bytes(key)
    }

    fn get_option_int(&self, key: Self::Option) -> Result<i64> {
        self.0.get_option_int(key)
    }

    fn get_option_double(&self, key: Self::Option) -> Result<f64> {
        self.0.get_option_double(key)
    }
}

impl adbc_core::Database for Database {
    type ConnectionType = Connection;

    fn new_connection(&self) -> Result<Self::ConnectionType> {
        self.0.new_connection().map(Connection)
    }

    fn new_connection_with_opts(
        &self,
        opts: impl IntoIterator<Item = (OptionConnection, OptionValue)>,
    ) -> Result<Self::ConnectionType> {
        self.0.new_connection_with_opts(opts).map(Connection)
    }
}