datasources-postgres 0.1.23

Postgres datasource provider
Documentation
mod alter_table_statement_renderer;
mod bind;
mod command;
mod connection;
mod converters;
mod create_table_statement_renderer;
mod create_trigger_statement_renderer;
mod drop_routine_statement_renderer;
mod driver;
mod identity_insert_statement_renderer;
mod default_value_converter;
mod default_value_options;
mod sql_generate;
mod type_map;
mod pg_value;
mod reader;
mod select_inserted_id_statement_text_renderer;
mod sql_seed_statement_renderer;
mod statement_renderer;
mod stored_procedure_statement_renderer;
mod transaction;

pub use driver::PostgresDriver;

#[cfg(test)]
#[path = "../tests/isolate.rs"]
mod isolate;
#[cfg(test)]
#[path = "../tests/contacts.rs"]
mod contacts;
#[cfg(test)]
#[path = "../tests/integration.rs"]
mod integration;
#[cfg(test)]
#[path = "../tests/repository_spec.rs"]
mod repository_spec;
#[cfg(test)]
#[path = "../tests/generate_sql.rs"]
mod generate_sql;
#[cfg(test)]
#[path = "../tests/generate_sql_casing.rs"]
mod generate_sql_casing;
#[cfg(test)]
#[path = "../tests/generate_sql_mapping.rs"]
mod generate_sql_mapping;

#[cfg(test)]
mod tests {
    use crate::bind::{param_type, to_postgres};
    use crate::connection::isolation_sql;
    use crate::converters::BinaryConverter;
    use tokio_postgres::types::Type;
    use crate::command::PostgresCommand;
    use crate::connection::PostgresConnection;
    use crate::transaction::PostgresTransaction;
    use datasources::{
        Row, create, downcast_connection, register, reset, Command, Connection,
        Driver, IsolationLevel, Transaction, TypeConverter, Value,
    };
    use std::collections::HashMap;
    use std::sync::Arc;

    struct With(Option<String>);

    impl Driver for With {
        fn connection(&self, _: Option<&HashMap<String, String>>) -> Arc<dyn Connection> {
            Arc::new(PostgresConnection::new(self.0.clone(), None))
        }

        fn command(&self, connection: Arc<dyn Connection>) -> Arc<dyn Command> {
            connection.create_command()
        }

        fn transaction(
            &self,
            connection: Arc<dyn Connection>,
            isolation: IsolationLevel,
        ) -> Arc<dyn Transaction> {
            let conn = downcast_connection::<PostgresConnection>(connection).expect("postgres");
            Arc::new(PostgresTransaction::new(conn.share(), isolation))
        }

        fn row_type_converters(
            &self,
        ) -> HashMap<
            String,
            Box<
                dyn datasources::TypeConverter<datasources::Value, datasources::Value>
                    + Send
                    + Sync,
            >,
        > {
            crate::converters::row_type_converters()
        }
    }

    #[tokio::test]
    async fn postgres_command_and_repository() {
        let (sql, values) = to_postgres(
            "SELECT * FROM t WHERE id = $id AND n = $id",
            &HashMap::from([("id".into(), Value::Int(1))]),
        );
        assert_eq!(sql, "SELECT * FROM t WHERE id = $1 AND n = $1");
        assert_eq!(values, vec![Value::Int(1)]);
        let (seed, _) = to_postgres(
            "INSERT INTO \"contact_sources\" (\"name\") VALUES ('Manual')",
            &HashMap::new(),
        );
        assert_eq!(seed, "INSERT INTO \"contact_sources\" (\"name\") VALUES ('Manual')");
        let (legacy, _) = to_postgres("SELECT * FROM OldContactsTbl", &HashMap::new());
        assert_eq!(legacy, "SELECT * FROM \"OldContactsTbl\"");
        let (_, missing) = to_postgres("SELECT $missing", &HashMap::new());
        assert_eq!(missing, vec![Value::Null]);
        let stamp = Value::from("2020-01-02T03:04:05.000Z");
        assert_eq!(param_type(&stamp), Type::UNKNOWN);
        assert_eq!(
            param_type(&Value::DateTime(
                chrono::DateTime::parse_from_rfc3339("2020-01-02T03:04:05.000Z")
                    .unwrap()
                    .with_timezone(&chrono::Utc)
            )),
            Type::TIMESTAMPTZ
        );
        assert_eq!(param_type(&Value::from("ada")), Type::UNKNOWN);
        assert!(matches!(
            BinaryConverter.convert(&Value::from("AQ==")).unwrap(),
            Value::Binary(_)
        ));
        assert_eq!(param_type(&Value::from("AA==")), Type::UNKNOWN);
        assert_eq!(param_type(&Value::Binary(vec![0])), Type::BYTEA);
        assert_eq!(param_type(&Value::Int(1)), Type::INT4);
        assert_eq!(param_type(&Value::Int(i64::MAX)), Type::INT8);
        assert_eq!(
            isolation_sql(IsolationLevel::ReadUncommitted),
            "READ UNCOMMITTED"
        );
        assert_eq!(
            isolation_sql(IsolationLevel::ReadCommitted),
            "READ COMMITTED"
        );
        assert_eq!(
            isolation_sql(IsolationLevel::RepeatableRead),
            "REPEATABLE READ"
        );
        assert_eq!(isolation_sql(IsolationLevel::Serializable), "SERIALIZABLE");
        let conn = Arc::new(PostgresConnection::new(
            Some("postgres://db/app".into()),
            Some("s".into()),
        ));
        assert_eq!(conn.schema.as_deref(), Some("s"));
        let command = PostgresCommand::new(conn.clone());
        command
            .execute_native(&datasources::Native::new(""), None)
            .await
            .unwrap();
        command
            .execute_reader_native(&datasources::Native::new(""), None)
            .await
            .unwrap();
        assert!(conn.start_transaction(None).await.is_err());
        conn.close().await.unwrap();
        assert!(conn.ensure_open().is_err());
        let defaulted = PostgresConnection::new(None, None);
        assert_eq!(defaulted.url, "");
        let options = HashMap::from([(
            "DATASOURCE_URL".into(),
            "postgres://db/app?schema=s".into(),
        )]);
        let from = downcast_connection::<PostgresConnection>(PostgresConnection::from_options(
            Some(&options),
        ))
        .expect("postgres");
        assert_eq!(from.url, "postgres://db/app?schema=s");
        assert_eq!(from.schema.as_deref(), Some("s"));
        assert_eq!(
            crate::connection::connect_url("postgres://db/app?schema=s"),
            "postgres://db/app"
        );
        assert_eq!(
            crate::connection::connect_url(
                "postgres://ds:ds@127.0.0.1:15432/datasources?schema=ds_x"
            ),
            "postgres://ds:ds@127.0.0.1:15432/datasources"
        );
        reset();
        register("postgres", Arc::new(With(Some("postgres://db/app".into()))));
        let ds = create(Some(&HashMap::from([(
            "PROVIDER".into(),
            "postgres".into(),
        )])))
        .unwrap();
        assert_eq!(ds.provider(), "postgres");
        assert!(ds.create_command(None).await.is_err());
        ds.create_repository::<Row>(None, None);
        let closed = Arc::new(PostgresConnection::new(None, None));
        assert!(PostgresCommand::new(closed)
            .execute_native(&datasources::Native::new("x"), None)
            .await
            .is_err());
    }
}