//! Types related to database connections

#[cfg(all(
    not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"),
    any(feature = "sqlite", feature = "postgres", feature = "mysql")
))]
pub(crate) mod statement_cache;
#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
pub mod statement_cache;
mod transaction_manager;

use crate::backend::Backend;
use crate::expression::QueryMetadata;
use crate::query_builder::{Query, QueryFragment, QueryId};
use crate::result::*;
use std::fmt::Debug;

pub use self::transaction_manager::{
    AnsiTransactionManager, InTransactionStatus, TransactionDepthChange, TransactionManager,
    TransactionManagerStatus, ValidTransactionManagerStatus,
};

#[diesel_derives::__diesel_public_if(
    feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
)]
pub(crate) use self::private::ConnectionSealed;

#[cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes")]
pub use self::private::MultiConnectionHelper;

#[cfg(all(
    not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"),
    any(feature = "sqlite", feature = "postgres", feature = "mysql")
))]
pub(crate) use self::private::MultiConnectionHelper;

/// Perform simple operations on a backend.
///
/// You should likely use [`Connection`] instead.
pub trait SimpleConnection {
    /// Execute multiple SQL statements within the same string.
    ///
    /// This function is used to execute migrations,
    /// which may contain more than one SQL statement.
    fn batch_execute(&mut self, query: &str) -> QueryResult<()>;
}

#[doc(hidden)]
#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))]
#[deprecated(note = "Directly use `LoadConnection::Cursor` instead")]
pub type LoadRowIter<'conn, 'query, C, DB, B = DefaultLoadingMode> =
    <C as self::private::ConnectionHelperType<DB, B>>::Cursor<'conn, 'query>;

/// A connection to a database
///
/// This trait represents a database connection. It can be used to query the database through
/// the query dsl provided by diesel, custom extensions or raw sql queries.
///
/// # Implementing a custom connection
///
/// There are several reasons why you would want to implement a custom connection implementation:
///
/// * To wrap an existing connection for instrumentation purposes
/// * To use a different underlying library to provide a connection implementation
/// for already existing backends.
/// * To add support for an unsupported database system
///
/// Implementing a `Connection` in a third party crate requires
/// enabling the
/// `i-implement-a-third-party-backend-and-opt-into-breaking-changes`
/// crate feature which grants access to some of diesel's implementation details.
///
///
/// ## Wrapping an existing connection impl
///
/// Wrapping an existing connection allows you to customize the implementation to
/// add additional functionality, like for example instrumentation. For this use case
/// you only need to implement `Connection`, [`LoadConnection`] and all super traits.
/// You should forward any method call to the wrapped connection type.
/// It is **important** to also forward any method where diesel provides a
/// default implementation, as the wrapped connection implementation may
/// contain a customized implementation.
///
/// To allow the integration of your new connection type with other diesel features
#[cfg_attr(
    feature = "r2d2",
    doc = "it may be useful to also implement [`R2D2Connection`](crate::r2d2::R2D2Connection)"
)]
#[cfg_attr(
    not(feature = "r2d2"),
    doc = "it may be useful to also implement `R2D2Connection`"
)]
/// and [`MigrationConnection`](crate::migration::MigrationConnection).
///
/// ## Provide a new connection implementation for an existing backend
///
/// Implementing a new connection based on an existing backend can enable the usage of
/// other methods to connect to the database. One example here would be to replace
/// the official diesel provided connection implementations with an implementation
/// based on a pure rust connection crate.
///
/// **It's important to use prepared statements to implement the following methods:**
/// * [`LoadConnection::load`]
/// * [`Connection::execute_returning_count`]
///
/// For performance reasons it may also be meaningful to cache already prepared statements.
#[cfg_attr(
    feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes",
    doc = "See [`StatementCache`](self::statement_cache::StatementCache)"
)]
#[cfg_attr(
    not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"),
    doc = "See `StatementCache`"
)]
/// for a helper type to implement prepared statement caching.
#[cfg_attr(
    feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes",
    doc = "The [statement_cache](self::statement_cache)"
)]
#[cfg_attr(
    not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"),
    doc = "The statement_cache"
)]
/// module documentation contains details about efficient prepared statement caching
/// based on diesels query builder.
///
/// It is required to implement at least the following parts:
///
/// * A row type that describes how to receive values form a database row.
///   This type needs to implement [`Row`](crate::row::Row)
/// * A field type that describes a database field value.
///   This type needs to implement [`Field`](crate::row::Field)
/// * A connection type that wraps the connection +
///   the necessary state management.
/// * Maybe a [`TransactionManager`] implementation matching
///  the interface provided by the database connection crate.
///  Otherwise the implementation used by the corresponding
///  `Connection` in diesel can be reused.
///
/// To allow the integration of your new connection type with other diesel features
#[cfg_attr(
    feature = "r2d2",
    doc = "it may be useful to also implement [`R2D2Connection`](crate::r2d2::R2D2Connection)"
)]
#[cfg_attr(
    not(feature = "r2d2"),
    doc = "it may be useful to also implement `R2D2Connection`"
)]
/// and [`MigrationConnection`](crate::migration::MigrationConnection).
///
/// The exact implementation of the `Connection` trait depends on the interface provided
/// by the connection crate/library. A struct implementing `Connection` should
#[cfg_attr(
    feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes",
    doc = "likely contain a [`StatementCache`](self::statement_cache::StatementCache)"
)]
#[cfg_attr(
    not(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"),
    doc = "likely contain a `StatementCache`"
)]
/// to cache prepared statements efficiently.
///
/// As implementations differ significantly between the supported backends
/// we cannot give a one for all description here. Generally it's likely a
/// good idea to follow the implementation of the corresponding connection
/// in diesel at a high level to gain some idea how to implement your
/// custom implementation.
///
/// ## Implement support for an unsupported database system
///
/// Additionally to anything mentioned in the previous section the following steps are required:
///
/// * Implement a custom backend type. See the documentation of [`Backend`] for details
/// * Implement appropriate [`FromSql`](crate::deserialize::FromSql)/
/// [`ToSql`](crate::serialize::ToSql) conversions.
/// At least the following impls should be considered:
///     * `i16`: `FromSql<SmallInt, YourBackend>`
///     * `i32`: `FromSql<Integer, YourBackend>`
///     * `i64`: `FromSql<BigInt, YourBackend>`
///     * `f32`: `FromSql<Float, YourBackend>`
///     * `f64`: `FromSql<Double, YourBackend>`
///     * `bool`: `FromSql<Bool, YourBackend>`
///     * `String`: `FromSql<Text, YourBackend>`
///     * `Vec<u8>`: `FromSql<Binary, YourBackend>`
///     * `i16`: `ToSql<SmallInt, YourBackend>`
///     * `i32`: `ToSql<Integer, YourBackend>`
///     * `i64`: `ToSql<BigInt, YourBackend>`
///     * `f32`: `ToSql<Float, YourBackend>`
///     * `f64`: `ToSql<Double, YourBackend>`
///     * `bool`: `ToSql<Bool, YourBackend>`
///     * `String`: `ToSql<Text, YourBackend>`
///     * `Vec<u8>`: `ToSql<Binary, YourBackend>`
/// * Maybe a [`TransactionManager`] implementation matching
///  the interface provided by the database connection crate.
///  Otherwise the implementation used by the corresponding
///  `Connection` in diesel can be reused.
///
/// As these implementations will vary depending on the backend being used,
/// we cannot give concrete examples here. We recommend looking at our existing
/// implementations to see how you can implement your own connection.
pub trait Connection: SimpleConnection + Sized + Send
where
    // This trait bound is there so that implementing a new connection is
    // gated behind the `i-implement-a-third-party-backend-and-opt-into-breaking-changes`
    // feature flag
    Self: ConnectionSealed,
{
    /// The backend this type connects to
    type Backend: Backend;

    /// The transaction manager implementation used by this connection
    #[diesel_derives::__diesel_public_if(
        feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
    )]
    type TransactionManager: TransactionManager<Self>;

    /// Establishes a new connection to the database
    ///
    /// The argument to this method and the method's behavior varies by backend.
    /// See the documentation for that backend's connection class
    /// for details about what it accepts and how it behaves.
    fn establish(database_url: &str) -> ConnectionResult<Self>;

    /// Executes the given function inside of a database transaction
    ///
    /// This function executes the provided closure `f` inside a database
    /// transaction. If there is already an open transaction for the current
    /// connection savepoints will be used instead. The connection is committed if
    /// the closure returns `Ok(_)`, it will be rolled back if it returns `Err(_)`.
    /// For both cases the original result value will be returned from this function.
    ///
    /// If the transaction fails to commit due to a `SerializationFailure` or a
    /// `ReadOnlyTransaction` a rollback will be attempted.
    /// If the rollback fails, the error will be returned in a
    /// [`Error::RollbackErrorOnCommit`](crate::result::Error::RollbackErrorOnCommit),
    /// from which you will be able to extract both the original commit error and
    /// the rollback error.
    /// In addition, the connection will be considered broken
    /// as it contains a uncommitted unabortable open transaction. Any further
    /// interaction with the transaction system will result in an returned error
    /// in this case.
    ///
    /// If the closure returns an `Err(_)` and the rollback fails the function
    /// will return that rollback error directly, and the transaction manager will
    /// be marked as broken as it contains a uncommitted unabortable open transaction.
    ///
    /// If a nested transaction fails to release the corresponding savepoint
    /// the error will be returned directly.
    ///
    /// # Example
    ///
    /// ```rust
    /// # include!("../doctest_setup.rs");
    /// use diesel::result::Error;
    ///
    /// # fn main() {
    /// #     run_test().unwrap();
    /// # }
    /// #
    /// # fn run_test() -> QueryResult<()> {
    /// #     use schema::users::dsl::*;
    /// #     let conn = &mut establish_connection();
    /// conn.transaction::<_, Error, _>(|conn| {
    ///     diesel::insert_into(users)
    ///         .values(name.eq("Ruby"))
    ///         .execute(conn)?;
    ///
    ///     let all_names = users.select(name).load::<String>(conn)?;
    ///     assert_eq!(vec!["Sean", "Tess", "Ruby"], all_names);
    ///
    ///     Ok(())
    /// })?;
    ///
    /// conn.transaction::<(), _, _>(|conn| {
    ///     diesel::insert_into(users)
    ///         .values(name.eq("Pascal"))
    ///         .execute(conn)?;
    ///
    ///     let all_names = users.select(name).load::<String>(conn)?;
    ///     assert_eq!(vec!["Sean", "Tess", "Ruby", "Pascal"], all_names);
    ///
    ///     // If we want to roll back the transaction, but don't have an
    ///     // actual error to return, we can return `RollbackTransaction`.
    ///     Err(Error::RollbackTransaction)
    /// });
    ///
    /// let all_names = users.select(name).load::<String>(conn)?;
    /// assert_eq!(vec!["Sean", "Tess", "Ruby"], all_names);
    /// #     Ok(())
    /// # }
    /// ```
    fn transaction<T, E, F>(&mut self, f: F) -> Result<T, E>
    where
        F: FnOnce(&mut Self) -> Result<T, E>,
        E: From<Error>,
    {
        Self::TransactionManager::transaction(self, f)
    }

    /// Creates a transaction that will never be committed. This is useful for
    /// tests. Panics if called while inside of a transaction or
    /// if called with a connection containing a broken transaction
    fn begin_test_transaction(&mut self) -> QueryResult<()> {
        match Self::TransactionManager::transaction_manager_status_mut(self) {
            TransactionManagerStatus::Valid(valid_status) => {
                assert_eq!(None, valid_status.transaction_depth())
            }
            TransactionManagerStatus::InError => panic!("Transaction manager in error"),
        };
        Self::TransactionManager::begin_transaction(self)?;
        // set the test transaction flag
        // to prevent that this connection gets dropped in connection pools
        // Tests commonly set the poolsize to 1 and use `begin_test_transaction`
        // to prevent modifications to the schema
        Self::TransactionManager::transaction_manager_status_mut(self).set_test_transaction_flag();
        Ok(())
    }

    /// Executes the given function inside a transaction, but does not commit
    /// it. Panics if the given function returns an error.
    ///
    /// # Example
    ///
    /// ```rust
    /// # include!("../doctest_setup.rs");
    /// use diesel::result::Error;
    ///
    /// # fn main() {
    /// #     run_test().unwrap();
    /// # }
    /// #
    /// # fn run_test() -> QueryResult<()> {
    /// #     use schema::users::dsl::*;
    /// #     let conn = &mut establish_connection();
    /// conn.test_transaction::<_, Error, _>(|conn| {
    ///     diesel::insert_into(users)
    ///         .values(name.eq("Ruby"))
    ///         .execute(conn)?;
    ///
    ///     let all_names = users.select(name).load::<String>(conn)?;
    ///     assert_eq!(vec!["Sean", "Tess", "Ruby"], all_names);
    ///
    ///     Ok(())
    /// });
    ///
    /// // Even though we returned `Ok`, the transaction wasn't committed.
    /// let all_names = users.select(name).load::<String>(conn)?;
    /// assert_eq!(vec!["Sean", "Tess"], all_names);
    /// #     Ok(())
    /// # }
    /// ```
    fn test_transaction<T, E, F>(&mut self, f: F) -> T
    where
        F: FnOnce(&mut Self) -> Result<T, E>,
        E: Debug,
    {
        let mut user_result = None;
        let _ = self.transaction::<(), _, _>(|conn| {
            user_result = f(conn).ok();
            Err(Error::RollbackTransaction)
        });
        user_result.expect("Transaction did not succeed")
    }

    /// Execute a single SQL statements given by a query and return
    /// number of affected rows
    #[diesel_derives::__diesel_public_if(
        feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
    )]
    fn execute_returning_count<T>(&mut self, source: &T) -> QueryResult<usize>
    where
        T: QueryFragment<Self::Backend> + QueryId;

    /// Get access to the current transaction state of this connection
    ///
    /// This function should be used from [`TransactionManager`] to access
    /// internally required state.
    #[diesel_derives::__diesel_public_if(
        feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
    )]
    fn transaction_state(
        &mut self,
    ) -> &mut <Self::TransactionManager as TransactionManager<Self>>::TransactionStateData;
}

/// The specific part of a [`Connection`] which actually loads data from the database
///
/// This is a separate trait to allow connection implementations to specify
/// different loading modes via the generic parameter.
pub trait LoadConnection<B = DefaultLoadingMode>: Connection {
    /// The cursor type returned by [`LoadConnection::load`]
    ///
    /// Users should handle this as opaque type that implements [`Iterator`]
    type Cursor<'conn, 'query>: Iterator<
        Item = QueryResult<<Self as LoadConnection<B>>::Row<'conn, 'query>>,
    >
    where
        Self: 'conn;

    /// The row type used as [`Iterator::Item`] for the iterator implementation
    /// of [`LoadConnection::Cursor`]
    type Row<'conn, 'query>: crate::row::Row<'conn, Self::Backend>
    where
        Self: 'conn;

    /// Executes a given query and returns any requested values
    ///
    /// This function executes a given query and returns the
    /// query result as given by the database. **Normal users
    /// should not use this function**. Use
    /// [`QueryDsl::load`](crate::QueryDsl) instead.
    ///
    /// This function is useful for people trying to build an alternative
    /// dsl on top of diesel. It returns an [`impl Iterator<Item = QueryResult<&impl Row<Self::Backend>>`](Iterator).
    /// This type can be used to iterate over all rows returned by the database.
    #[diesel_derives::__diesel_public_if(
        feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
    )]
    fn load<'conn, 'query, T>(
        &'conn mut self,
        source: T,
    ) -> QueryResult<Self::Cursor<'conn, 'query>>
    where
        T: Query + QueryFragment<Self::Backend> + QueryId + 'query,
        Self::Backend: QueryMetadata<T::SqlType>;
}

/// A variant of the [`Connection`](trait.Connection.html) trait that is
/// usable with dynamic dispatch
///
/// If you are looking for a way to use pass database connections
/// for different database backends around in your application
/// this trait won't help you much. Normally you should only
/// need to use this trait if you are interacting with a connection
/// passed to a [`Migration`](../migration/trait.Migration.html)
pub trait BoxableConnection<DB: Backend>: SimpleConnection + std::any::Any {
    /// Maps the current connection to `std::any::Any`
    #[diesel_derives::__diesel_public_if(
        feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"
    )]
    fn as_any(&self) -> &dyn std::any::Any;

    #[doc(hidden)]
    fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
}

impl<C> BoxableConnection<C::Backend> for C
where
    C: Connection + std::any::Any,
{
    fn as_any(&self) -> &dyn std::any::Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
        self
    }
}

/// The default loading mode provided by a [`Connection`].
///
/// Checkout the documentation of concrete connection types for details about
/// supported loading modes.
///
/// All types implementing [`Connection`] should provide at least
/// a single [`LoadConnection<DefaultLoadingMode>`](self::LoadConnection)
/// implementation.
#[derive(Debug, Copy, Clone)]
pub struct DefaultLoadingMode;

impl<DB: Backend + 'static> dyn BoxableConnection<DB> {
    /// Downcast the current connection to a specific connection
    /// type.
    ///
    /// This will return `None` if the underlying
    /// connection does not match the corresponding
    /// type, otherwise a reference to the underlying connection is returned
    pub fn downcast_ref<T>(&self) -> Option<&T>
    where
        T: Connection<Backend = DB> + 'static,
    {
        self.as_any().downcast_ref::<T>()
    }

    /// Downcast the current connection to a specific mutable connection
    /// type.
    ///
    /// This will return `None` if the underlying
    /// connection does not match the corresponding
    /// type, otherwise a mutable reference to the underlying connection is returned
    pub fn downcast_mut<T>(&mut self) -> Option<&mut T>
    where
        T: Connection<Backend = DB> + 'static,
    {
        self.as_any_mut().downcast_mut::<T>()
    }

    /// Check if the current connection is
    /// a specific connection type
    pub fn is<T>(&self) -> bool
    where
        T: Connection<Backend = DB> + 'static,
    {
        self.as_any().is::<T>()
    }
}

// These traits are considered private for different reasons:
//
// `ConnectionSealed` to control who can implement `Connection`,
// so that we can later change the `Connection` trait
//
// `MultiConnectionHelper` is a workaround needed for the
// `MultiConnection` derive. We might stabilize this trait with
// the corresponding derive
//
// `ConnectionHelperType` as a workaround for the `LoadRowIter`
// type def. That trait should not be used by any user outside of diesel,
// it purely exists for backward compatibility reasons.
pub(crate) mod private {

    /// This trait restricts who can implement `Connection`
    #[cfg_attr(
        doc_cfg,
        doc(cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"))
    )]
    pub trait ConnectionSealed {}

    /// This trait provides helper methods to convert a database lookup type
    /// to/from an `std::any::Any` reference. This is used internally by the `#[derive(MultiConnection)]`
    /// implementation
    #[cfg_attr(
        doc_cfg,
        doc(cfg(feature = "i-implement-a-third-party-backend-and-opt-into-breaking-changes"))
    )]
    pub trait MultiConnectionHelper: super::Connection {
        /// Convert the lookup type to any
        fn to_any<'a>(
            lookup: &mut <Self::Backend as crate::sql_types::TypeMetadata>::MetadataLookup,
        ) -> &mut (dyn std::any::Any + 'a);

        /// Get the lookup type from any
        fn from_any(
            lookup: &mut dyn std::any::Any,
        ) -> Option<&mut <Self::Backend as crate::sql_types::TypeMetadata>::MetadataLookup>;
    }

    // These impls are only there for backward compatibility reasons
    // Remove them on the next breaking release
    #[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))]
    pub trait ConnectionHelperType<DB, B>: super::LoadConnection<B, Backend = DB> {
        type Cursor<'conn, 'query>
        where
            Self: 'conn;
    }
    #[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))]
    impl<T, B> ConnectionHelperType<T::Backend, B> for T
    where
        T: super::LoadConnection<B>,
    {
        type Cursor<'conn, 'query> = <T as super::LoadConnection<B>>::Cursor<'conn, 'query> where T: 'conn;
    }
}
