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