Skip to main content

datasources_postgres/
lib.rs

1mod alter_table_statement_renderer;
2mod bind;
3mod command;
4mod connection;
5mod converters;
6mod create_table_statement_renderer;
7mod create_trigger_statement_renderer;
8mod drop_routine_statement_renderer;
9mod driver;
10mod identity_insert_statement_renderer;
11mod sql_generate;
12mod pg_value;
13mod reader;
14mod select_inserted_id_statement_text_renderer;
15mod statement_renderer;
16mod stored_procedure_statement_renderer;
17mod transaction;
18
19pub use driver::PostgresDriver;
20
21#[cfg(test)]
22#[path = "../tests/isolate.rs"]
23mod isolate;
24#[cfg(test)]
25#[path = "../tests/contacts.rs"]
26mod contacts;
27#[cfg(test)]
28#[path = "../tests/integration.rs"]
29mod integration;
30#[cfg(test)]
31#[path = "../tests/repository_spec.rs"]
32mod repository_spec;
33#[cfg(test)]
34#[path = "../tests/generate_sql.rs"]
35mod generate_sql;
36#[cfg(test)]
37#[path = "../tests/generate_sql_casing.rs"]
38mod generate_sql_casing;
39#[cfg(test)]
40#[path = "../tests/generate_sql_mapping.rs"]
41mod generate_sql_mapping;
42
43#[cfg(test)]
44mod tests {
45    use crate::bind::{param_type, to_postgres};
46    use crate::connection::isolation_sql;
47    use crate::converters::BinaryConverter;
48    use tokio_postgres::types::Type;
49    use crate::command::PostgresCommand;
50    use crate::connection::PostgresConnection;
51    use crate::transaction::PostgresTransaction;
52    use datasources::{
53        converters::Cells, create, downcast_connection, register, reset, Command, Connection,
54        Driver, IsolationLevel, Transaction, TypeConverter, Value,
55    };
56    use std::collections::HashMap;
57    use std::sync::Arc;
58
59    struct With(Option<String>);
60
61    impl Driver for With {
62        fn connection(&self, _: Option<&HashMap<String, String>>) -> Arc<dyn Connection> {
63            Arc::new(PostgresConnection::new(self.0.clone(), None))
64        }
65
66        fn command(&self, connection: Arc<dyn Connection>) -> Arc<dyn Command> {
67            connection.create_command()
68        }
69
70        fn transaction(
71            &self,
72            connection: Arc<dyn Connection>,
73            isolation: IsolationLevel,
74        ) -> Arc<dyn Transaction> {
75            let conn = downcast_connection::<PostgresConnection>(connection).expect("postgres");
76            Arc::new(PostgresTransaction::new(conn.share(), isolation))
77        }
78
79        fn row_type_converters(
80            &self,
81        ) -> HashMap<
82            String,
83            Box<
84                dyn datasources::TypeConverter<datasources::Value, datasources::Value>
85                    + Send
86                    + Sync,
87            >,
88        > {
89            crate::converters::row_type_converters()
90        }
91    }
92
93    #[tokio::test]
94    async fn postgres_command_and_repository() {
95        let (sql, values) = to_postgres(
96            "SELECT * FROM t WHERE id = $id AND n = $id",
97            &HashMap::from([("id".into(), Value::Int(1))]),
98        );
99        assert_eq!(sql, "SELECT * FROM t WHERE id = $1 AND n = $1");
100        assert_eq!(values, vec![Value::Int(1)]);
101        let (seed, _) = to_postgres(
102            "INSERT INTO \"contact_sources\" (\"name\") VALUES ('Manual')",
103            &HashMap::new(),
104        );
105        assert_eq!(seed, "INSERT INTO \"contact_sources\" (\"name\") VALUES ('Manual')");
106        let (legacy, _) = to_postgres("SELECT * FROM OldContactsTbl", &HashMap::new());
107        assert_eq!(legacy, "SELECT * FROM \"OldContactsTbl\"");
108        let (_, missing) = to_postgres("SELECT $missing", &HashMap::new());
109        assert_eq!(missing, vec![Value::Null]);
110        let stamp = Value::from("2020-01-02T03:04:05.000Z");
111        assert_eq!(param_type(&stamp), Type::UNKNOWN);
112        assert_eq!(
113            param_type(&Value::DateTime(
114                chrono::DateTime::parse_from_rfc3339("2020-01-02T03:04:05.000Z")
115                    .unwrap()
116                    .with_timezone(&chrono::Utc)
117            )),
118            Type::TIMESTAMPTZ
119        );
120        assert_eq!(param_type(&Value::from("ada")), Type::UNKNOWN);
121        assert!(matches!(
122            BinaryConverter.convert(&Value::from("AQ==")).unwrap(),
123            Value::Binary(_)
124        ));
125        assert_eq!(param_type(&Value::from("AA==")), Type::UNKNOWN);
126        assert_eq!(param_type(&Value::Binary(vec![0])), Type::BYTEA);
127        assert_eq!(param_type(&Value::Int(1)), Type::INT4);
128        assert_eq!(param_type(&Value::Int(i64::MAX)), Type::INT8);
129        assert_eq!(
130            isolation_sql(IsolationLevel::ReadUncommitted),
131            "READ UNCOMMITTED"
132        );
133        assert_eq!(
134            isolation_sql(IsolationLevel::ReadCommitted),
135            "READ COMMITTED"
136        );
137        assert_eq!(
138            isolation_sql(IsolationLevel::RepeatableRead),
139            "REPEATABLE READ"
140        );
141        assert_eq!(isolation_sql(IsolationLevel::Serializable), "SERIALIZABLE");
142        let conn = Arc::new(PostgresConnection::new(
143            Some("postgres://db/app".into()),
144            Some("s".into()),
145        ));
146        assert_eq!(conn.schema.as_deref(), Some("s"));
147        let command = PostgresCommand::new(conn.clone());
148        command
149            .execute_native(&datasources::Native::new(""), None)
150            .await
151            .unwrap();
152        command
153            .execute_reader_native(&datasources::Native::new(""), None)
154            .await
155            .unwrap();
156        assert!(conn.start_transaction(None).await.is_err());
157        conn.close().await.unwrap();
158        assert!(conn.ensure_open().is_err());
159        let defaulted = PostgresConnection::new(None, None);
160        assert_eq!(defaulted.url, "");
161        let options = HashMap::from([(
162            "DATASOURCE_URL".into(),
163            "postgres://db/app?schema=s".into(),
164        )]);
165        let from = downcast_connection::<PostgresConnection>(PostgresConnection::from_options(
166            Some(&options),
167        ))
168        .expect("postgres");
169        assert_eq!(from.url, "postgres://db/app?schema=s");
170        assert_eq!(from.schema.as_deref(), Some("s"));
171        assert_eq!(
172            crate::connection::connect_url("postgres://db/app?schema=s"),
173            "postgres://db/app"
174        );
175        assert_eq!(
176            crate::connection::connect_url(
177                "postgres://ds:ds@127.0.0.1:15432/datasources?schema=ds_x"
178            ),
179            "postgres://ds:ds@127.0.0.1:15432/datasources"
180        );
181        reset();
182        register("postgres", Arc::new(With(Some("postgres://db/app".into()))));
183        let ds = create(Some(&HashMap::from([(
184            "PROVIDER".into(),
185            "postgres".into(),
186        )])))
187        .unwrap();
188        assert_eq!(ds.provider(), "postgres");
189        assert!(ds.create_command(None).await.is_err());
190        ds.create_repository::<Cells>(None, None);
191        let closed = Arc::new(PostgresConnection::new(None, None));
192        assert!(PostgresCommand::new(closed)
193            .execute_native(&datasources::Native::new("x"), None)
194            .await
195            .is_err());
196    }
197}