Repository: shuttle-hq/synth (language: rust)

Issue:
Use parameterized Statements for Postgres export
**Required Functionality**
Currently, the postgres export statements are build as strings. See [postgres.sql:91](https://github.com/getsynth/synth/blob/master/synth/src/cli/postgres.rs#L91) and following.

**Proposed Solution**
Make it use a prepared statement instead. This requires implementing `ToSql` on our [`Value`]() implementation and creating a function that builds the SQL statement from a given list of columns for the namespace.

**Use case**
Not only will this make the export faster by removing the Value → String → Value roundtrip, it will also reduce the avenues for errors because of failing to escape data (if someone manages to generate [bobby tables](https://xkcd.com/327)).


Discussion / hints:
Hey! I believe I can work on this. I'm just not quite sure I understand about _ToSql_. Is it a trait from another library? One to be implemented in Synth itself?
@llogiq, `postgres` has an implementation of `ToSql`  that may be enabled by including it in the features list. [See.](https://docs.rs/postgres/0.15.2/postgres/types/trait.ToSql.html) With that, do we still need to re-implement `ToSql`?
In the lossless sampling PR, we switch to sqlx, along with implementing it's `Type` and `Encode` traits. Yesterday I split out the export half of the PR so it's easier to review. Expect it to be merged next week.

Fix patch:
diff --git a/synth/src/datasource/postgres_datasource.rs b/synth/src/datasource/postgres_datasource.rs
index 16d849e9..4d9122e4 100644
--- a/synth/src/datasource/postgres_datasource.rs
+++ b/synth/src/datasource/postgres_datasource.rs
@@ -100,16 +100,13 @@ impl RelationalDataSource for PostgresDataSource {
     }
 
     async fn get_primary_keys(&self, table_name: &str) -> Result<Vec<PrimaryKey>> {
-        // Unfortunately cannot use parameterised queries here
-        let query: &str = &format!(
-            r"SELECT a.attname, format_type(a.atttypid, a.atttypmod) AS data_type
-            FROM pg_index i
-            JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey)
-            WHERE  i.indrelid = '{}'::regclass AND i.indisprimary",
-            &table_name
-        );
+        let query = r"SELECT a.attname, format_type(a.atttypid, a.atttypmod) AS data_type
+        FROM pg_index i
+        JOIN pg_attribute a ON a.attrelid = i.indrelid AND a.attnum = ANY(i.indkey)
+        WHERE  i.indrelid = cast($1 as regclass) AND i.indisprimary";
 
         sqlx::query(query)
+            .bind(table_name)
             .fetch_all(&self.pool)
             .await?
             .into_iter()


Test patch: